├── README.md ├── SwiftUI-Mac-2022.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── sarah.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── sarah.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SwiftUI-Mac-2022 ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── mac1024.png │ │ ├── mac128.png │ │ ├── mac16.png │ │ ├── mac256.png │ │ ├── mac32.png │ │ ├── mac512.png │ │ └── mac64.png │ └── Contents.json ├── Menus.swift ├── Models │ ├── Helper.swift │ ├── HttpSection.swift │ ├── HttpStatus.swift │ └── httpcodes.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── SwiftUI_Mac_2022.entitlements ├── SwiftUI_Mac_2022App.swift └── Views │ ├── CatImageView.swift │ ├── ContentView.swift │ ├── DetailView.swift │ ├── MenuBarView.swift │ ├── OptionalDetailView.swift │ ├── Samples │ ├── ChartSamplesView.swift │ ├── FormSamplesView.swift │ └── SamplesView.swift │ ├── SettingsView.swift │ ├── SidebarRowView.swift │ └── StatusView.swift └── assets └── navigation_2022.jpg /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI for Mac Big Sur 2 | 3 | An Xcode project for use with https://troz.net/post/2022/swiftui-mac-2022 4 | 5 | Demonstrating using SwiftUI to create a Mac app on macOS 13 Ventura with Xcode 14. 6 | 7 | ![App UI](assets/navigation_2022.jpg) 8 | 9 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2617277F2859A59300D73331 /* ChartSamplesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2617277E2859A59300D73331 /* ChartSamplesView.swift */; }; 11 | 26172780285AEE4700D73331 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2CA28595BAC008EEEA6 /* DetailView.swift */; }; 12 | 26172782285AF3C900D73331 /* MenuBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26172781285AF3C900D73331 /* MenuBarView.swift */; }; 13 | 262A9B0D285070AF00D99C71 /* SwiftUI_Mac_2022App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B0C285070AF00D99C71 /* SwiftUI_Mac_2022App.swift */; }; 14 | 262A9B0F285070AF00D99C71 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B0E285070AF00D99C71 /* ContentView.swift */; }; 15 | 262A9B11285070B000D99C71 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 262A9B10285070B000D99C71 /* Assets.xcassets */; }; 16 | 262A9B14285070B000D99C71 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 262A9B13285070B000D99C71 /* Preview Assets.xcassets */; }; 17 | 262A9B1C285076D100D99C71 /* httpcodes.json in Resources */ = {isa = PBXBuildFile; fileRef = 262A9B1B285076D100D99C71 /* httpcodes.json */; }; 18 | 262A9B1E2850770900D99C71 /* HttpStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B1D2850770900D99C71 /* HttpStatus.swift */; }; 19 | 262A9B20285077B800D99C71 /* HttpSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B1F285077B800D99C71 /* HttpSection.swift */; }; 20 | 262A9B232850781800D99C71 /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B222850781800D99C71 /* Helper.swift */; }; 21 | 262A9B252850789100D99C71 /* SidebarRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262A9B242850789100D99C71 /* SidebarRowView.swift */; }; 22 | 266DA2CD28598D50008EEEA6 /* CatImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2CC28598D50008EEEA6 /* CatImageView.swift */; }; 23 | 266DA2D028599362008EEEA6 /* Menus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2CF28599362008EEEA6 /* Menus.swift */; }; 24 | 266DA2D4285997AB008EEEA6 /* SamplesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2D3285997AB008EEEA6 /* SamplesView.swift */; }; 25 | 266DA2DA285997C8008EEEA6 /* FormSamplesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2D7285997C8008EEEA6 /* FormSamplesView.swift */; }; 26 | 266DA2DC28599B24008EEEA6 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266DA2DB28599B24008EEEA6 /* SettingsView.swift */; }; 27 | 26B738ED285861A900F26D5D /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B738EC285861A900F26D5D /* StatusView.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 2617277E2859A59300D73331 /* ChartSamplesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartSamplesView.swift; sourceTree = ""; }; 32 | 26172781285AF3C900D73331 /* MenuBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarView.swift; sourceTree = ""; }; 33 | 262A9B09285070AF00D99C71 /* SwiftUI Mac 2022.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SwiftUI Mac 2022.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 262A9B0C285070AF00D99C71 /* SwiftUI_Mac_2022App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_Mac_2022App.swift; sourceTree = ""; }; 35 | 262A9B0E285070AF00D99C71 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 36 | 262A9B10285070B000D99C71 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 37 | 262A9B13285070B000D99C71 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 38 | 262A9B1B285076D100D99C71 /* httpcodes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = httpcodes.json; sourceTree = ""; }; 39 | 262A9B1D2850770900D99C71 /* HttpStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpStatus.swift; sourceTree = ""; }; 40 | 262A9B1F285077B800D99C71 /* HttpSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpSection.swift; sourceTree = ""; }; 41 | 262A9B222850781800D99C71 /* Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = ""; }; 42 | 262A9B242850789100D99C71 /* SidebarRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarRowView.swift; sourceTree = ""; }; 43 | 266DA2CA28595BAC008EEEA6 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; 44 | 266DA2CC28598D50008EEEA6 /* CatImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatImageView.swift; sourceTree = ""; }; 45 | 266DA2CF28599362008EEEA6 /* Menus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menus.swift; sourceTree = ""; }; 46 | 266DA2D3285997AB008EEEA6 /* SamplesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SamplesView.swift; sourceTree = ""; }; 47 | 266DA2D7285997C8008EEEA6 /* FormSamplesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormSamplesView.swift; sourceTree = ""; }; 48 | 266DA2DB28599B24008EEEA6 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 49 | 26B738EC285861A900F26D5D /* StatusView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 262A9B06285070AF00D99C71 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 262A9B00285070AF00D99C71 = { 64 | isa = PBXGroup; 65 | children = ( 66 | 262A9B0B285070AF00D99C71 /* SwiftUI-Mac-2022 */, 67 | 262A9B0A285070AF00D99C71 /* Products */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 262A9B0A285070AF00D99C71 /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 262A9B09285070AF00D99C71 /* SwiftUI Mac 2022.app */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 262A9B0B285070AF00D99C71 /* SwiftUI-Mac-2022 */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 262A9B0C285070AF00D99C71 /* SwiftUI_Mac_2022App.swift */, 83 | 266DA2CF28599362008EEEA6 /* Menus.swift */, 84 | 266DA2CE2859904C008EEEA6 /* Views */, 85 | 262A9B21285077C000D99C71 /* Models */, 86 | 262A9B10285070B000D99C71 /* Assets.xcassets */, 87 | 262A9B12285070B000D99C71 /* Preview Content */, 88 | ); 89 | path = "SwiftUI-Mac-2022"; 90 | sourceTree = ""; 91 | }; 92 | 262A9B12285070B000D99C71 /* Preview Content */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 262A9B13285070B000D99C71 /* Preview Assets.xcassets */, 96 | ); 97 | path = "Preview Content"; 98 | sourceTree = ""; 99 | }; 100 | 262A9B21285077C000D99C71 /* Models */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 262A9B1B285076D100D99C71 /* httpcodes.json */, 104 | 262A9B1F285077B800D99C71 /* HttpSection.swift */, 105 | 262A9B1D2850770900D99C71 /* HttpStatus.swift */, 106 | 262A9B222850781800D99C71 /* Helper.swift */, 107 | ); 108 | path = Models; 109 | sourceTree = ""; 110 | }; 111 | 266DA2CE2859904C008EEEA6 /* Views */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 262A9B0E285070AF00D99C71 /* ContentView.swift */, 115 | 262A9B242850789100D99C71 /* SidebarRowView.swift */, 116 | 266DA2CA28595BAC008EEEA6 /* DetailView.swift */, 117 | 26B738EC285861A900F26D5D /* StatusView.swift */, 118 | 266DA2CC28598D50008EEEA6 /* CatImageView.swift */, 119 | 266DA2DB28599B24008EEEA6 /* SettingsView.swift */, 120 | 26172781285AF3C900D73331 /* MenuBarView.swift */, 121 | 266DA2DD28599D5B008EEEA6 /* Samples */, 122 | ); 123 | path = Views; 124 | sourceTree = ""; 125 | }; 126 | 266DA2DD28599D5B008EEEA6 /* Samples */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 266DA2D3285997AB008EEEA6 /* SamplesView.swift */, 130 | 2617277E2859A59300D73331 /* ChartSamplesView.swift */, 131 | 266DA2D7285997C8008EEEA6 /* FormSamplesView.swift */, 132 | ); 133 | path = Samples; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXNativeTarget section */ 139 | 262A9B08285070AF00D99C71 /* SwiftUI Mac 2022 */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 262A9B18285070B000D99C71 /* Build configuration list for PBXNativeTarget "SwiftUI Mac 2022" */; 142 | buildPhases = ( 143 | 262A9B05285070AF00D99C71 /* Sources */, 144 | 262A9B06285070AF00D99C71 /* Frameworks */, 145 | 262A9B07285070AF00D99C71 /* Resources */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | ); 151 | name = "SwiftUI Mac 2022"; 152 | productName = "SwiftUI-Mac-13"; 153 | productReference = 262A9B09285070AF00D99C71 /* SwiftUI Mac 2022.app */; 154 | productType = "com.apple.product-type.application"; 155 | }; 156 | /* End PBXNativeTarget section */ 157 | 158 | /* Begin PBXProject section */ 159 | 262A9B01285070AF00D99C71 /* Project object */ = { 160 | isa = PBXProject; 161 | attributes = { 162 | BuildIndependentTargetsInParallel = 1; 163 | LastSwiftUpdateCheck = 1400; 164 | LastUpgradeCheck = 1400; 165 | TargetAttributes = { 166 | 262A9B08285070AF00D99C71 = { 167 | CreatedOnToolsVersion = 14.0; 168 | }; 169 | }; 170 | }; 171 | buildConfigurationList = 262A9B04285070AF00D99C71 /* Build configuration list for PBXProject "SwiftUI-Mac-2022" */; 172 | compatibilityVersion = "Xcode 14.0"; 173 | developmentRegion = en; 174 | hasScannedForEncodings = 0; 175 | knownRegions = ( 176 | en, 177 | Base, 178 | ); 179 | mainGroup = 262A9B00285070AF00D99C71; 180 | productRefGroup = 262A9B0A285070AF00D99C71 /* Products */; 181 | projectDirPath = ""; 182 | projectRoot = ""; 183 | targets = ( 184 | 262A9B08285070AF00D99C71 /* SwiftUI Mac 2022 */, 185 | ); 186 | }; 187 | /* End PBXProject section */ 188 | 189 | /* Begin PBXResourcesBuildPhase section */ 190 | 262A9B07285070AF00D99C71 /* Resources */ = { 191 | isa = PBXResourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | 262A9B14285070B000D99C71 /* Preview Assets.xcassets in Resources */, 195 | 262A9B1C285076D100D99C71 /* httpcodes.json in Resources */, 196 | 262A9B11285070B000D99C71 /* Assets.xcassets in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXResourcesBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 262A9B05285070AF00D99C71 /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 266DA2DA285997C8008EEEA6 /* FormSamplesView.swift in Sources */, 208 | 26172782285AF3C900D73331 /* MenuBarView.swift in Sources */, 209 | 262A9B0F285070AF00D99C71 /* ContentView.swift in Sources */, 210 | 262A9B20285077B800D99C71 /* HttpSection.swift in Sources */, 211 | 266DA2D028599362008EEEA6 /* Menus.swift in Sources */, 212 | 266DA2CD28598D50008EEEA6 /* CatImageView.swift in Sources */, 213 | 262A9B232850781800D99C71 /* Helper.swift in Sources */, 214 | 26B738ED285861A900F26D5D /* StatusView.swift in Sources */, 215 | 266DA2DC28599B24008EEEA6 /* SettingsView.swift in Sources */, 216 | 262A9B0D285070AF00D99C71 /* SwiftUI_Mac_2022App.swift in Sources */, 217 | 266DA2D4285997AB008EEEA6 /* SamplesView.swift in Sources */, 218 | 262A9B1E2850770900D99C71 /* HttpStatus.swift in Sources */, 219 | 262A9B252850789100D99C71 /* SidebarRowView.swift in Sources */, 220 | 26172780285AEE4700D73331 /* DetailView.swift in Sources */, 221 | 2617277F2859A59300D73331 /* ChartSamplesView.swift in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXSourcesBuildPhase section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | 262A9B16285070B000D99C71 /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | CLANG_ANALYZER_NONNULL = YES; 233 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 235 | CLANG_ENABLE_MODULES = YES; 236 | CLANG_ENABLE_OBJC_ARC = YES; 237 | CLANG_ENABLE_OBJC_WEAK = YES; 238 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 239 | CLANG_WARN_BOOL_CONVERSION = YES; 240 | CLANG_WARN_COMMA = YES; 241 | CLANG_WARN_CONSTANT_CONVERSION = YES; 242 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 243 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 244 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 245 | CLANG_WARN_EMPTY_BODY = YES; 246 | CLANG_WARN_ENUM_CONVERSION = YES; 247 | CLANG_WARN_INFINITE_RECURSION = YES; 248 | CLANG_WARN_INT_CONVERSION = YES; 249 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 251 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 253 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 254 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 255 | CLANG_WARN_STRICT_PROTOTYPES = YES; 256 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 257 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | COPY_PHASE_STRIP = NO; 261 | DEBUG_INFORMATION_FORMAT = dwarf; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | ENABLE_TESTABILITY = YES; 264 | GCC_C_LANGUAGE_STANDARD = gnu11; 265 | GCC_DYNAMIC_NO_PIC = NO; 266 | GCC_NO_COMMON_BLOCKS = YES; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_PREPROCESSOR_DEFINITIONS = ( 269 | "DEBUG=1", 270 | "$(inherited)", 271 | ); 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | MACOSX_DEPLOYMENT_TARGET = 13.0; 279 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 280 | MTL_FAST_MATH = YES; 281 | ONLY_ACTIVE_ARCH = YES; 282 | SDKROOT = macosx; 283 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 284 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 285 | }; 286 | name = Debug; 287 | }; 288 | 262A9B17285070B000D99C71 /* Release */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ALWAYS_SEARCH_USER_PATHS = NO; 292 | CLANG_ANALYZER_NONNULL = YES; 293 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 294 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 295 | CLANG_ENABLE_MODULES = YES; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_ENABLE_OBJC_WEAK = YES; 298 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 299 | CLANG_WARN_BOOL_CONVERSION = YES; 300 | CLANG_WARN_COMMA = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 304 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 305 | CLANG_WARN_EMPTY_BODY = YES; 306 | CLANG_WARN_ENUM_CONVERSION = YES; 307 | CLANG_WARN_INFINITE_RECURSION = YES; 308 | CLANG_WARN_INT_CONVERSION = YES; 309 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 313 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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 | COPY_PHASE_STRIP = NO; 321 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 322 | ENABLE_NS_ASSERTIONS = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu11; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | MACOSX_DEPLOYMENT_TARGET = 13.0; 333 | MTL_ENABLE_DEBUG_INFO = NO; 334 | MTL_FAST_MATH = YES; 335 | SDKROOT = macosx; 336 | SWIFT_COMPILATION_MODE = wholemodule; 337 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 338 | }; 339 | name = Release; 340 | }; 341 | 262A9B19285070B000D99C71 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 346 | CODE_SIGN_ENTITLEMENTS = "SwiftUI-Mac-2022/SwiftUI_Mac_2022.entitlements"; 347 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 348 | CODE_SIGN_STYLE = Automatic; 349 | COMBINE_HIDPI_IMAGES = YES; 350 | CURRENT_PROJECT_VERSION = 1; 351 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI-Mac-2022/Preview Content\""; 352 | DEVELOPMENT_TEAM = ""; 353 | ENABLE_HARDENED_RUNTIME = YES; 354 | ENABLE_PREVIEWS = YES; 355 | GENERATE_INFOPLIST_FILE = YES; 356 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 357 | LD_RUNPATH_SEARCH_PATHS = ( 358 | "$(inherited)", 359 | "@executable_path/../Frameworks", 360 | ); 361 | MARKETING_VERSION = 1.0; 362 | PRODUCT_BUNDLE_IDENTIFIER = "net.troz.SwiftUI-Mac-2022"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | SWIFT_EMIT_LOC_STRINGS = YES; 365 | SWIFT_VERSION = 5.0; 366 | }; 367 | name = Debug; 368 | }; 369 | 262A9B1A285070B000D99C71 /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 373 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 374 | CODE_SIGN_ENTITLEMENTS = "SwiftUI-Mac-2022/SwiftUI_Mac_2022.entitlements"; 375 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 376 | CODE_SIGN_STYLE = Automatic; 377 | COMBINE_HIDPI_IMAGES = YES; 378 | CURRENT_PROJECT_VERSION = 1; 379 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI-Mac-2022/Preview Content\""; 380 | DEVELOPMENT_TEAM = ""; 381 | ENABLE_HARDENED_RUNTIME = YES; 382 | ENABLE_PREVIEWS = YES; 383 | GENERATE_INFOPLIST_FILE = YES; 384 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 385 | LD_RUNPATH_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "@executable_path/../Frameworks", 388 | ); 389 | MARKETING_VERSION = 1.0; 390 | PRODUCT_BUNDLE_IDENTIFIER = "net.troz.SwiftUI-Mac-2022"; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | SWIFT_EMIT_LOC_STRINGS = YES; 393 | SWIFT_VERSION = 5.0; 394 | }; 395 | name = Release; 396 | }; 397 | /* End XCBuildConfiguration section */ 398 | 399 | /* Begin XCConfigurationList section */ 400 | 262A9B04285070AF00D99C71 /* Build configuration list for PBXProject "SwiftUI-Mac-2022" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | 262A9B16285070B000D99C71 /* Debug */, 404 | 262A9B17285070B000D99C71 /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | 262A9B18285070B000D99C71 /* Build configuration list for PBXNativeTarget "SwiftUI Mac 2022" */ = { 410 | isa = XCConfigurationList; 411 | buildConfigurations = ( 412 | 262A9B19285070B000D99C71 /* Debug */, 413 | 262A9B1A285070B000D99C71 /* Release */, 414 | ); 415 | defaultConfigurationIsVisible = 0; 416 | defaultConfigurationName = Release; 417 | }; 418 | /* End XCConfigurationList section */ 419 | }; 420 | rootObject = 262A9B01285070AF00D99C71 /* Project object */; 421 | } 422 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022.xcodeproj/project.xcworkspace/xcuserdata/sarah.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022.xcodeproj/project.xcworkspace/xcuserdata/sarah.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftUI-Mac-2022.xcodeproj/xcuserdata/sarah.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftUI Mac 2022.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | SwiftUI-Mac-13.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | SwiftUI-Mac-2022.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 0 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mac16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "mac32.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "mac32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "mac64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "mac128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "mac256.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "mac256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "mac512.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "mac512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "mac1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac1024.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac128.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac16.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac256.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac32.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac512.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/SwiftUI-Mac-2022/Assets.xcassets/AppIcon.appiconset/mac64.png -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Menus.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct Menus: Commands { 4 | var body: some Commands { 5 | SidebarCommands() 6 | 7 | CommandGroup(after: .textEditing) { 8 | Button("Flip Image") { 9 | NotificationCenter.default.post(name: .flipImage, object: nil) 10 | } 11 | .keyboardShortcut("f") 12 | } 13 | 14 | // CommandGroup(after: .newItem) { 15 | // NavigationLink(destination: SamplesView()) { 16 | // Text("Show UI Samples") 17 | // } 18 | // .keyboardShortcut("u") 19 | // } 20 | 21 | CommandGroup(replacing: .help) { 22 | Button("Read Accompanying Article at troz.net") { 23 | let articleUrl = URL(string: "https://troz.net/post/2022/swiftui-mac-2022/")! 24 | NSWorkspace.shared.open(articleUrl) 25 | } 26 | } 27 | } 28 | } 29 | 30 | extension Notification.Name { 31 | static let flipImage = Notification.Name("flipImage") 32 | } 33 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Models/Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helper.swift 3 | // A small collection of quick helpers to avoid repeating the same old code. 4 | // 5 | // Created by Paul Hudson on 23/06/2019. 6 | // Copyright © 2019 Hacking with Swift. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension Bundle { 12 | func decode(_ type: T.Type, from file: String) -> T { 13 | guard let url = self.url(forResource: file, withExtension: nil) else { 14 | fatalError("Failed to locate \(file) in bundle.") 15 | } 16 | 17 | guard let data = try? Data(contentsOf: url) else { 18 | fatalError("Failed to load \(file) from bundle.") 19 | } 20 | 21 | let decoder = JSONDecoder() 22 | 23 | guard let loaded = try? decoder.decode(T.self, from: data) else { 24 | fatalError("Failed to decode \(file) from bundle.") 25 | } 26 | 27 | return loaded 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Models/HttpSection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct HttpSection: Identifiable, Hashable { 4 | let id = UUID() 5 | let headerCode: String 6 | let headerText: String 7 | let statuses: [HttpStatus] 8 | } 9 | 10 | extension HttpSection: Decodable { 11 | enum CodingKeys: CodingKey { 12 | case headerCode 13 | case headerText 14 | case statuses 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Models/HttpStatus.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct HttpStatus: Identifiable, Hashable { 4 | let id = UUID() 5 | let code: String 6 | let title: String 7 | 8 | var imageUrl: URL { 9 | let address = "https://http.cat/\(code).jpg" 10 | return URL(string: address)! 11 | } 12 | 13 | var docsUrl: URL { 14 | let address = "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/\(code)" 15 | return URL(string: address)! 16 | } 17 | } 18 | 19 | extension HttpStatus: Decodable { 20 | enum CodingKeys: CodingKey { 21 | case code 22 | case title 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Models/httpcodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "headerCode": "1xx", 4 | "headerText": "Informational", 5 | "statuses": [ 6 | { 7 | "code": "100", 8 | "title": "Continue" 9 | }, 10 | { 11 | "code": "101", 12 | "title": "Switching Protocols" 13 | } 14 | ] 15 | }, 16 | { 17 | "headerCode": "2xx", 18 | "headerText": "Success", 19 | "statuses": [ 20 | { 21 | "code": "200", 22 | "title": "OK" 23 | }, 24 | { 25 | "code": "201", 26 | "title": "Created" 27 | }, 28 | { 29 | "code": "202", 30 | "title": "Accepted" 31 | }, 32 | { 33 | "code": "204", 34 | "title": "No Content" 35 | }, 36 | { 37 | "code": "206", 38 | "title": "Partial Content" 39 | }, 40 | { 41 | "code": "207", 42 | "title": "Multi-Status" 43 | } 44 | ] 45 | }, 46 | { 47 | "headerCode": "3xx", 48 | "headerText": "Redirection", 49 | "statuses": [ 50 | { 51 | "code": "300", 52 | "title": "Multiple Choices" 53 | }, 54 | { 55 | "code": "301", 56 | "title": "Moved Permanently" 57 | }, 58 | { 59 | "code": "302", 60 | "title": "Found" 61 | }, 62 | { 63 | "code": "303", 64 | "title": "See Other" 65 | }, 66 | { 67 | "code": "304", 68 | "title": "Not Modified" 69 | }, 70 | { 71 | "code": "305", 72 | "title": "Use Proxy" 73 | }, 74 | { 75 | "code": "307", 76 | "title": "Temporary Redirect" 77 | } 78 | ] 79 | }, 80 | { 81 | "headerCode": "4xx", 82 | "headerText": "Client Error", 83 | "statuses": [ 84 | { 85 | "code": "400", 86 | "title": "Bad Request" 87 | }, 88 | { 89 | "code": "401", 90 | "title": "Unauthorized" 91 | }, 92 | { 93 | "code": "402", 94 | "title": "Payment Required" 95 | }, 96 | { 97 | "code": "403", 98 | "title": "Forbidden" 99 | }, 100 | { 101 | "code": "404", 102 | "title": "Not Found" 103 | }, 104 | { 105 | "code": "405", 106 | "title": "Method Not Allowed" 107 | }, 108 | { 109 | "code": "406", 110 | "title": "Not Acceptable" 111 | }, 112 | { 113 | "code": "408", 114 | "title": "Request Timeout" 115 | }, 116 | { 117 | "code": "409", 118 | "title": "Conflict" 119 | }, 120 | { 121 | "code": "410", 122 | "title": "Gone" 123 | }, 124 | { 125 | "code": "411", 126 | "title": "Length Required" 127 | }, 128 | { 129 | "code": "412", 130 | "title": "Precondition Failed" 131 | }, 132 | { 133 | "code": "413", 134 | "title": "Payload Too Large" 135 | }, 136 | { 137 | "code": "414", 138 | "title": "Request-URI Too Long" 139 | }, 140 | { 141 | "code": "415", 142 | "title": "Unsupported Media Type" 143 | }, 144 | { 145 | "code": "416", 146 | "title": "Requested Range Not Satisfiable" 147 | }, 148 | { 149 | "code": "417", 150 | "title": "Expectation Failed" 151 | }, 152 | { 153 | "code": "418", 154 | "title": "I'm a teapot" 155 | }, 156 | { 157 | "code": "421", 158 | "title": "Misdirected Request" 159 | }, 160 | { 161 | "code": "422", 162 | "title": "Unprocessable Entity" 163 | }, 164 | { 165 | "code": "423", 166 | "title": "Locked" 167 | }, 168 | { 169 | "code": "424", 170 | "title": "Failed Dependency" 171 | }, 172 | { 173 | "code": "425", 174 | "title": "Unordered Collection" 175 | }, 176 | { 177 | "code": "426", 178 | "title": "Upgrade Required" 179 | }, 180 | { 181 | "code": "429", 182 | "title": "Too Many Requests" 183 | }, 184 | { 185 | "code": "431", 186 | "title": "Request Header Fields Too Large" 187 | }, 188 | { 189 | "code": "444", 190 | "title": "Connection Closed Without Response" 191 | }, 192 | { 193 | "code": "451", 194 | "title": "Unavailable For Legal Reasons" 195 | } 196 | ] 197 | }, 198 | { 199 | "headerCode": "5xx", 200 | "headerText": "Server Error", 201 | "statuses": [ 202 | { 203 | "code": "500", 204 | "title": "Internal Server Error" 205 | }, 206 | { 207 | "code": "501", 208 | "title": "Not Implemented" 209 | }, 210 | { 211 | "code": "502", 212 | "title": "Bad Gateway" 213 | }, 214 | { 215 | "code": "503", 216 | "title": "Service Unavailable" 217 | }, 218 | { 219 | "code": "504", 220 | "title": "Gateway Timeout" 221 | }, 222 | { 223 | "code": "506", 224 | "title": "Variant Also Negotiates" 225 | }, 226 | { 227 | "code": "507", 228 | "title": "Insufficient Storage" 229 | }, 230 | { 231 | "code": "508", 232 | "title": "Loop Detected" 233 | }, 234 | { 235 | "code": "509", 236 | "title": "Bandwidth Limit Exceeded" 237 | }, 238 | { 239 | "code": "510", 240 | "title": "Not Extended" 241 | }, 242 | { 243 | "code": "511", 244 | "title": "Network Authentication Required" 245 | }, 246 | { 247 | "code": "599", 248 | "title": "Network Connect Timeout Error" 249 | } 250 | ] 251 | } 252 | ] 253 | 254 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/SwiftUI_Mac_2022.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/SwiftUI_Mac_2022App.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct SwiftUI_Mac_2022App: App { 5 | @AppStorage("showMenuBar") var showMenuBar = true 6 | 7 | @State private var httpSections: [HttpSection] = [] 8 | 9 | var body: some Scene { 10 | WindowGroup { 11 | ContentView(httpSections: httpSections) 12 | .onAppear { 13 | httpSections = Bundle.main.decode([HttpSection].self, from: "httpcodes.json") 14 | } 15 | }.commands { 16 | Menus() 17 | } 18 | 19 | Settings { 20 | SettingsView() 21 | } 22 | 23 | // OPENING SECONDARY WINDOWS 24 | 25 | // Use this method if you want to allow the window to open multiple times 26 | // WindowGroup("Samples", id: "ui_samples") { 27 | // SamplesView() 28 | // } 29 | 30 | // This method will open one window for each different String passed 31 | // WindowGroup(for: String.self) { _ in 32 | // SamplesView() 33 | // } 34 | 35 | // This will only ever open a single window 36 | // and it will add a menu item to the Windows menu 37 | Window("Samples", id: "ui_samples") { 38 | SamplesView() 39 | } 40 | .keyboardShortcut("u") 41 | // The default modifiers don't appear to work 42 | .defaultPosition(.topLeading) 43 | .defaultSize(width: 600, height: 600) 44 | 45 | MenuBarExtra("HTTP Status Code", systemImage: "number.circle", isInserted: $showMenuBar) { 46 | MenuBarView(httpSections: $httpSections) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/CatImageView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CatImageView: View { 4 | @AppStorage("showCopyright") var showCopyright: Bool = false 5 | @Environment(\.controlActiveState) private var controlActiveState 6 | 7 | private let flipImageMenuItemSelected = NotificationCenter.default 8 | .publisher(for: .flipImage) 9 | .receive(on: RunLoop.main) 10 | 11 | let catImage: Image 12 | let statusCode: String 13 | 14 | @State private var imageIsFlipped = false 15 | 16 | var body: some View { 17 | VStack { 18 | catImage 19 | .resizable() 20 | .aspectRatio(contentMode: .fit) 21 | .rotation3DEffect(Angle(degrees: imageIsFlipped ? 180 : 0), 22 | axis: (x: 0, y: 1, z: 0)) 23 | .animation(.default, value: imageIsFlipped) 24 | .overlay( 25 | Text(showCopyright ? "Copyright © https" + "://http.cat" : "") 26 | .padding(6) 27 | .font(.caption) 28 | .foregroundColor(.white) 29 | .shadow(radius: 5) 30 | ,alignment: .bottomTrailing) 31 | } 32 | .onReceive(flipImageMenuItemSelected) { _ in 33 | if controlActiveState == .key || controlActiveState == .active { 34 | imageIsFlipped.toggle() 35 | } 36 | } 37 | } 38 | } 39 | 40 | struct CatImageView_Previews: PreviewProvider { 41 | static var previews: some View { 42 | let image = Image(systemName: "photo.artframe") 43 | return CatImageView(catImage: image, statusCode: "000") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | let httpSections: [HttpSection] 5 | 6 | @State private var selectedStatus: HttpStatus? 7 | @State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn 8 | 9 | var body: some View { 10 | NavigationSplitView(columnVisibility: $columnVisibility) { 11 | List(httpSections, selection: $selectedStatus) { section in 12 | Section(header: Text("\(section.headerCode) - \(section.headerText)")) { 13 | ForEach(section.statuses) { status in 14 | SidebarRowView(code: status.code, title: status.title) 15 | .tag(status) 16 | } 17 | } 18 | .font(.headline) 19 | } 20 | .frame(minWidth: 250) 21 | } detail: { 22 | DetailView(sectionTitle: sectionHeader, httpStatus: selectedStatus) 23 | } 24 | 25 | .frame(minWidth: 650, idealWidth: 800, maxWidth: .infinity, 26 | minHeight: 300, idealHeight: 400, maxHeight: .infinity) 27 | } 28 | 29 | var sectionHeader: String? { 30 | if let selectedStatus { 31 | let selectedSection = httpSections.first { section in 32 | section.statuses.contains(selectedStatus) 33 | } 34 | return selectedSection?.headerText 35 | } 36 | return nil 37 | } 38 | } 39 | 40 | struct ContentView_Previews: PreviewProvider { 41 | static var previews: some View { 42 | ContentView(httpSections: Bundle.main.decode([HttpSection].self, from: "httpcodes.json") 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/DetailView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct DetailView: View { 4 | let sectionTitle: String? 5 | let httpStatus: HttpStatus? 6 | @Environment(\.openWindow) private var openWindow 7 | 8 | var body: some View { 9 | VStack { 10 | if let sectionTitle, let httpStatus { 11 | StatusView(sectionTitle: sectionTitle, httpStatus: httpStatus) 12 | } else { 13 | Spacer() 14 | Text("Select a status.") 15 | .font(.largeTitle) 16 | .navigationTitle("HTTP Status") 17 | } 18 | 19 | Spacer() 20 | Button("Show UI Samples") { 21 | openWindow(id: "ui_samples") 22 | } 23 | .padding(.bottom) 24 | } 25 | } 26 | } 27 | 28 | struct OptionalDetailView_Previews: PreviewProvider { 29 | static var previews: some View { 30 | DetailView(sectionTitle: nil, httpStatus: nil) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/MenuBarView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct MenuBarView: View { 4 | @Binding var httpSections: [HttpSection] 5 | 6 | var body: some View { 7 | ForEach(httpSections) { section in 8 | Menu("\(section.headerCode) - \(section.headerText)") { 9 | ForEach(section.statuses) { status in 10 | Button("\(status.code) - \(status.title)") { 11 | NSWorkspace.shared.open(status.docsUrl) 12 | } 13 | } 14 | } 15 | } 16 | } 17 | } 18 | 19 | struct MenuBarView_Previews: PreviewProvider { 20 | static var previews: some View { 21 | MenuBarView(httpSections: .constant([])) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/OptionalDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailView.swift 3 | // SwiftUI-Mac-13 4 | // 5 | // Created by Sarah Reichelt on 15/6/2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DetailView: View { 11 | let sectionTitle: String? 12 | let httpStatus: HttpStatus? 13 | 14 | var body: some View { 15 | if let sectionTitle, let httpStatus { 16 | StatusView(sectionTitle: sectionTitle, httpStatus: httpStatus) 17 | } else { 18 | Text("Select a status") 19 | .font(.largeTitle) 20 | .navigationTitle("HTTP Status") 21 | } 22 | } 23 | } 24 | 25 | struct OptionalDetailView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | DetailView(sectionTitle: nil, httpStatus: nil) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/Samples/ChartSamplesView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Charts 3 | 4 | struct ChartSamplesView: View { 5 | @State private var chartData: [String: Int] = [:] 6 | @State private var drawHorizontal = false 7 | 8 | var body: some View { 9 | let chartView = Group { 10 | Text("Status codes by category") 11 | .font(.title3).bold() 12 | .padding() 13 | 14 | HStack { 15 | Text(drawHorizontal ? "Category" : "Count").bold() 16 | .rotationEffect(.degrees(270)) 17 | 18 | Chart { 19 | ForEach(chartData.keys.sorted(), id: \.self) { key in 20 | barMark(for: key) 21 | .foregroundStyle(by: .value( "Color", key)) 22 | } 23 | 24 | ruleMark() 25 | .foregroundStyle(.gray) 26 | } 27 | .chartForegroundStyleScale([ 28 | "1xx": .green, 29 | "2xx": .purple, 30 | "3xx": .blue, 31 | "4xx": .yellow, 32 | "5xx": .red 33 | ]) 34 | .chartLegend(.hidden) 35 | .padding(.trailing) 36 | } 37 | 38 | Text(drawHorizontal ? "Count" : "Category").bold() 39 | } 40 | 41 | VStack { 42 | chartView 43 | 44 | HStack { 45 | Picker("Orientation:", selection: $drawHorizontal) { 46 | Text("Vertical").tag(false) 47 | Text("Horizontal").tag(true) 48 | } 49 | .pickerStyle(.segmented) 50 | .padding() 51 | 52 | Button("Save Chart as Image") { 53 | let view = chartView 54 | .padding() 55 | .frame(width: 1200, height: 800) 56 | 57 | let renderer = ImageRenderer(content: view) 58 | if let exportImage = renderer.nsImage { 59 | saveImage(exportImage) 60 | } 61 | } 62 | } 63 | .padding(.horizontal) 64 | 65 | } 66 | .animation(.easeInOut, value: drawHorizontal) 67 | .onAppear(perform: readCodes) 68 | } 69 | 70 | func barMark(for key: String) -> BarMark { 71 | if drawHorizontal { 72 | return BarMark( 73 | x: .value("Count", chartData[key] ?? 0), 74 | y: .value("Category", key) 75 | ) 76 | } else { 77 | return BarMark( 78 | x: .value("Category", key), 79 | y: .value("Count", chartData[key] ?? 0) 80 | ) 81 | } 82 | } 83 | 84 | func ruleMark() -> RuleMark { 85 | if drawHorizontal { 86 | return RuleMark( 87 | x: .value("Threshold", 8) 88 | ) 89 | } else { 90 | return RuleMark( 91 | y: .value("Threshold", 8) 92 | ) 93 | } 94 | } 95 | 96 | func readCodes() { 97 | let httpSections = Bundle.main.decode([HttpSection].self, from: "httpcodes.json") 98 | 99 | for section in httpSections { 100 | chartData[section.headerCode] = section.statuses.count 101 | } 102 | } 103 | 104 | func saveImage(_ image: NSImage) { 105 | let saveURL = showSavePanel() 106 | if let saveURL { 107 | let imageRepresentation = NSBitmapImageRep(data: image.tiffRepresentation!) 108 | let jpgData = imageRepresentation?.representation(using: .jpeg, properties: [:]) 109 | try? jpgData?.write(to: saveURL) 110 | } 111 | } 112 | 113 | func showSavePanel() -> URL? { 114 | let savePanel = NSSavePanel() 115 | 116 | savePanel.allowedContentTypes = [.jpeg] 117 | savePanel.canCreateDirectories = true 118 | savePanel.isExtensionHidden = false 119 | 120 | savePanel.title = "Save your image" 121 | savePanel.message = "Choose a folder and a name to store the image." 122 | savePanel.nameFieldLabel = "Image file name:" 123 | savePanel.nameFieldStringValue = "Chart.jpg" 124 | 125 | let response = savePanel.runModal() 126 | return response == .OK ? savePanel.url : nil 127 | } 128 | } 129 | 130 | struct ChartSamplesView_Previews: PreviewProvider { 131 | static var previews: some View { 132 | ChartSamplesView() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/Samples/FormSamplesView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct FormSamplesView: View { 4 | @State private var email = "" 5 | @State private var password = "" 6 | @State private var selectedDate = Date() 7 | @State private var pickChoice = 1 8 | @State private var checkOne = false 9 | @State private var checkTwo = false 10 | @State private var selectedColor = Color.blue 11 | 12 | @FocusState private var emailFieldHasFocus: Bool 13 | 14 | var body: some View { 15 | VStack { 16 | GroupBox { 17 | Form { 18 | TextField("Enter your email address:", text: $email) 19 | .focused($emailFieldHasFocus) 20 | 21 | SecureField("Enter your password:", text: $password) 22 | 23 | DatePicker("Enter your birthday:", selection: $selectedDate, displayedComponents: [.date]) 24 | .padding(.bottom) 25 | 26 | Form { 27 | Toggle("Check 1:", isOn: $checkOne) 28 | 29 | Toggle("Check 2:", isOn: $checkTwo) 30 | 31 | Picker("Select One:", selection: $pickChoice) { 32 | Text("Option 1").tag(1) 33 | Text("Option 2").tag(2) 34 | Text("Option 3").tag(3) 35 | } 36 | } 37 | .toggleStyle(.switch) 38 | .formStyle(.grouped) 39 | 40 | HStack { 41 | ColorPicker(selection: $selectedColor, supportsOpacity: true) { 42 | Text("Choose a color:") 43 | } 44 | .frame(height: 50) 45 | 46 | Spacer(minLength: 50) 47 | 48 | Capsule() 49 | .fill(selectedColor.gradient) 50 | .frame(maxWidth: .infinity) 51 | .frame(height: 50) 52 | .shadow(radius: 5) 53 | } 54 | .padding(.top) 55 | } 56 | .formStyle(.columns) 57 | .textFieldStyle(.roundedBorder) 58 | .padding() 59 | } 60 | 61 | Spacer() 62 | } 63 | .padding() 64 | .frame(width: 500, height: 420) 65 | .onAppear { 66 | emailFieldHasFocus = true 67 | } 68 | } 69 | } 70 | 71 | struct FormSamplesView_Previews: PreviewProvider { 72 | static var previews: some View { 73 | FormSamplesView() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/Samples/SamplesView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SamplesView: View { 4 | @State private var selectedTab = 0 5 | 6 | var body: some View { 7 | VStack { 8 | TabView(selection: $selectedTab) { 9 | ChartSamplesView().tabItem { Text("Chart") }.tag(1) 10 | FormSamplesView().tabItem { Text("Form") }.tag(2) 11 | } 12 | 13 | Spacer() 14 | } 15 | .padding() 16 | .frame(minWidth: 600, minHeight: 550) 17 | } 18 | } 19 | 20 | struct Samplesview_Previews: PreviewProvider { 21 | static var previews: some View { 22 | SamplesView() 23 | } 24 | } 25 | 26 | struct ExplanatoryText: ViewModifier { 27 | func body(content: Content) -> some View { 28 | content 29 | .font(.subheadline) 30 | .foregroundColor(.secondary) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/SettingsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsView.swift 3 | // SwiftUI-Mac-13 4 | // 5 | // Created by Sarah Reichelt on 15/6/2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SettingsView: View { 11 | @AppStorage("showCopyright") var showCopyright: Bool = false 12 | @AppStorage("showMenuBar") var showMenuBar = true 13 | 14 | var body: some View { 15 | Form { 16 | Toggle(isOn: $showCopyright) { 17 | Text("Show Copyright Notice") 18 | } 19 | Toggle(isOn: $showMenuBar) { 20 | Text("Show Menu Bar App") 21 | } 22 | } 23 | .toggleStyle(.switch) 24 | .formStyle(.grouped) 25 | .frame(width: 300, height: 130) 26 | .navigationTitle("Settings") 27 | } 28 | } 29 | 30 | struct SettingsView_Previews: PreviewProvider { 31 | static var previews: some View { 32 | SettingsView() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/SidebarRowView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SidebarRowView: View { 4 | let code: String 5 | let title: String 6 | 7 | var body: some View { 8 | VStack(alignment: .leading) { 9 | Text(code) 10 | .font(.largeTitle) 11 | .foregroundColor(.primary) 12 | 13 | Text(title) 14 | .font(.title2) 15 | .truncationMode(.tail) 16 | } 17 | .padding(.horizontal) 18 | .padding(.vertical, 3) 19 | } 20 | } 21 | 22 | struct ListRowView_Previews: PreviewProvider { 23 | static var previews: some View { 24 | SidebarRowView(code: "418", title: "I'm a teapot") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SwiftUI-Mac-2022/Views/StatusView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct StatusView: View { 4 | let sectionTitle: String 5 | let httpStatus: HttpStatus 6 | 7 | @State private var catImage: NSImage? 8 | 9 | var body: some View { 10 | VStack { 11 | Text("HTTP Status Code: \(httpStatus.code)") 12 | .font(.headline) 13 | .padding() 14 | Text(httpStatus.title) 15 | .font(.title) 16 | 17 | AsyncImage(url: httpStatus.imageUrl) { img in 18 | CatImageView(catImage: img, statusCode: httpStatus.code) 19 | } placeholder: { 20 | Spacer() 21 | ProgressView() 22 | Spacer() 23 | } 24 | .id(httpStatus) // this resets the AsyncImage whenever httpStatus changes 25 | 26 | Spacer() 27 | } 28 | .frame(maxWidth: .infinity, maxHeight: .infinity) 29 | .navigationTitle("\(sectionTitle) - \(httpStatus.title)") 30 | } 31 | } 32 | 33 | struct DetailView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | StatusView(sectionTitle: "4xx", 36 | httpStatus: HttpStatus(code: "404", title: "Not Found")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /assets/navigation_2022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trozware/swiftui-mac-2022/a301437c169811f080fe09c39cc4f2b610dddc7f/assets/navigation_2022.jpg --------------------------------------------------------------------------------