├── README.md ├── SwiftUITextEditor.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── SwiftUITextEditor ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Document.swift ├── DocumentBrowserViewController.swift ├── DocumentView.swift ├── Info.plist ├── New Document.txt └── TextView.swift ├── SwiftUITextEditorTests ├── Info.plist └── SwiftUITextEditorTests.swift └── SwiftUITextEditorUITests ├── Info.plist └── SwiftUITextEditorUITests.swift /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUITextEditor 2 | 3 | This project is a text editor that you build in the following tutorial: 4 | 5 | [Creating Document-Based Apps with SwiftUI](https://www.swiftdevjournal.com/creating-document-based-apps-with-swiftui/) 6 | 7 | It's a simple plain text editor that demonstrates how to make a document-based iOS app with SwiftUI. You can use it as a foundation for making your own text editing apps. 8 | -------------------------------------------------------------------------------- /SwiftUITextEditor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4DA2847123AB1C9E00A44538 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2847023AB1C9E00A44538 /* AppDelegate.swift */; }; 11 | 4DA2847323AB1C9E00A44538 /* DocumentBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2847223AB1C9E00A44538 /* DocumentBrowserViewController.swift */; }; 12 | 4DA2847523AB1C9E00A44538 /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2847423AB1C9E00A44538 /* DocumentView.swift */; }; 13 | 4DA2847723AB1C9E00A44538 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2847623AB1C9E00A44538 /* Document.swift */; }; 14 | 4DA2847A23AB1C9E00A44538 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DA2847823AB1C9E00A44538 /* Main.storyboard */; }; 15 | 4DA2847C23AB1CA000A44538 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4DA2847B23AB1CA000A44538 /* Assets.xcassets */; }; 16 | 4DA2847F23AB1CA000A44538 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DA2847D23AB1CA000A44538 /* LaunchScreen.storyboard */; }; 17 | 4DA2848A23AB1CA000A44538 /* SwiftUITextEditorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2848923AB1CA000A44538 /* SwiftUITextEditorTests.swift */; }; 18 | 4DA2849523AB1CA100A44538 /* SwiftUITextEditorUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA2849423AB1CA100A44538 /* SwiftUITextEditorUITests.swift */; }; 19 | 4DA284A323AB1CD800A44538 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA284A223AB1CD800A44538 /* TextView.swift */; }; 20 | 4DA284A523AB237800A44538 /* New Document.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4DA284A423AB237800A44538 /* New Document.txt */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 4DA2848623AB1CA000A44538 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 4DA2846523AB1C9E00A44538 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 4DA2846C23AB1C9E00A44538; 29 | remoteInfo = SwiftUITextEditor; 30 | }; 31 | 4DA2849123AB1CA100A44538 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 4DA2846523AB1C9E00A44538 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 4DA2846C23AB1C9E00A44538; 36 | remoteInfo = SwiftUITextEditor; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 4DA2846D23AB1C9E00A44538 /* SwiftUITextEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUITextEditor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 4DA2847023AB1C9E00A44538 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 4DA2847223AB1C9E00A44538 /* DocumentBrowserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentBrowserViewController.swift; sourceTree = ""; }; 44 | 4DA2847423AB1C9E00A44538 /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = ""; }; 45 | 4DA2847623AB1C9E00A44538 /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 46 | 4DA2847923AB1C9E00A44538 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 4DA2847B23AB1CA000A44538 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 4DA2847E23AB1CA000A44538 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 4DA2848023AB1CA000A44538 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 4DA2848523AB1CA000A44538 /* SwiftUITextEditorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUITextEditorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 4DA2848923AB1CA000A44538 /* SwiftUITextEditorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUITextEditorTests.swift; sourceTree = ""; }; 52 | 4DA2848B23AB1CA000A44538 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 4DA2849023AB1CA100A44538 /* SwiftUITextEditorUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUITextEditorUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 4DA2849423AB1CA100A44538 /* SwiftUITextEditorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUITextEditorUITests.swift; sourceTree = ""; }; 55 | 4DA2849623AB1CA100A44538 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 4DA284A223AB1CD800A44538 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; 57 | 4DA284A423AB237800A44538 /* New Document.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "New Document.txt"; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 4DA2846A23AB1C9E00A44538 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | 4DA2848223AB1CA000A44538 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 4DA2848D23AB1CA100A44538 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 4DA2846423AB1C9E00A44538 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 4DA2846F23AB1C9E00A44538 /* SwiftUITextEditor */, 89 | 4DA2848823AB1CA000A44538 /* SwiftUITextEditorTests */, 90 | 4DA2849323AB1CA100A44538 /* SwiftUITextEditorUITests */, 91 | 4DA2846E23AB1C9E00A44538 /* Products */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 4DA2846E23AB1C9E00A44538 /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 4DA2846D23AB1C9E00A44538 /* SwiftUITextEditor.app */, 99 | 4DA2848523AB1CA000A44538 /* SwiftUITextEditorTests.xctest */, 100 | 4DA2849023AB1CA100A44538 /* SwiftUITextEditorUITests.xctest */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 4DA2846F23AB1C9E00A44538 /* SwiftUITextEditor */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 4DA2847023AB1C9E00A44538 /* AppDelegate.swift */, 109 | 4DA2847223AB1C9E00A44538 /* DocumentBrowserViewController.swift */, 110 | 4DA2847423AB1C9E00A44538 /* DocumentView.swift */, 111 | 4DA2847623AB1C9E00A44538 /* Document.swift */, 112 | 4DA284A223AB1CD800A44538 /* TextView.swift */, 113 | 4DA2847823AB1C9E00A44538 /* Main.storyboard */, 114 | 4DA2847B23AB1CA000A44538 /* Assets.xcassets */, 115 | 4DA2847D23AB1CA000A44538 /* LaunchScreen.storyboard */, 116 | 4DA2848023AB1CA000A44538 /* Info.plist */, 117 | 4DA284A423AB237800A44538 /* New Document.txt */, 118 | ); 119 | path = SwiftUITextEditor; 120 | sourceTree = ""; 121 | }; 122 | 4DA2848823AB1CA000A44538 /* SwiftUITextEditorTests */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 4DA2848923AB1CA000A44538 /* SwiftUITextEditorTests.swift */, 126 | 4DA2848B23AB1CA000A44538 /* Info.plist */, 127 | ); 128 | path = SwiftUITextEditorTests; 129 | sourceTree = ""; 130 | }; 131 | 4DA2849323AB1CA100A44538 /* SwiftUITextEditorUITests */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 4DA2849423AB1CA100A44538 /* SwiftUITextEditorUITests.swift */, 135 | 4DA2849623AB1CA100A44538 /* Info.plist */, 136 | ); 137 | path = SwiftUITextEditorUITests; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 4DA2846C23AB1C9E00A44538 /* SwiftUITextEditor */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 4DA2849923AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditor" */; 146 | buildPhases = ( 147 | 4DA2846923AB1C9E00A44538 /* Sources */, 148 | 4DA2846A23AB1C9E00A44538 /* Frameworks */, 149 | 4DA2846B23AB1C9E00A44538 /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = SwiftUITextEditor; 156 | productName = SwiftUITextEditor; 157 | productReference = 4DA2846D23AB1C9E00A44538 /* SwiftUITextEditor.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | 4DA2848423AB1CA000A44538 /* SwiftUITextEditorTests */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 4DA2849C23AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditorTests" */; 163 | buildPhases = ( 164 | 4DA2848123AB1CA000A44538 /* Sources */, 165 | 4DA2848223AB1CA000A44538 /* Frameworks */, 166 | 4DA2848323AB1CA000A44538 /* Resources */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | 4DA2848723AB1CA000A44538 /* PBXTargetDependency */, 172 | ); 173 | name = SwiftUITextEditorTests; 174 | productName = SwiftUITextEditorTests; 175 | productReference = 4DA2848523AB1CA000A44538 /* SwiftUITextEditorTests.xctest */; 176 | productType = "com.apple.product-type.bundle.unit-test"; 177 | }; 178 | 4DA2848F23AB1CA100A44538 /* SwiftUITextEditorUITests */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = 4DA2849F23AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditorUITests" */; 181 | buildPhases = ( 182 | 4DA2848C23AB1CA100A44538 /* Sources */, 183 | 4DA2848D23AB1CA100A44538 /* Frameworks */, 184 | 4DA2848E23AB1CA100A44538 /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | 4DA2849223AB1CA100A44538 /* PBXTargetDependency */, 190 | ); 191 | name = SwiftUITextEditorUITests; 192 | productName = SwiftUITextEditorUITests; 193 | productReference = 4DA2849023AB1CA100A44538 /* SwiftUITextEditorUITests.xctest */; 194 | productType = "com.apple.product-type.bundle.ui-testing"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | 4DA2846523AB1C9E00A44538 /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | LastSwiftUpdateCheck = 1130; 203 | LastUpgradeCheck = 1130; 204 | ORGANIZATIONNAME = "Swift Dev Journal"; 205 | TargetAttributes = { 206 | 4DA2846C23AB1C9E00A44538 = { 207 | CreatedOnToolsVersion = 11.3; 208 | }; 209 | 4DA2848423AB1CA000A44538 = { 210 | CreatedOnToolsVersion = 11.3; 211 | TestTargetID = 4DA2846C23AB1C9E00A44538; 212 | }; 213 | 4DA2848F23AB1CA100A44538 = { 214 | CreatedOnToolsVersion = 11.3; 215 | TestTargetID = 4DA2846C23AB1C9E00A44538; 216 | }; 217 | }; 218 | }; 219 | buildConfigurationList = 4DA2846823AB1C9E00A44538 /* Build configuration list for PBXProject "SwiftUITextEditor" */; 220 | compatibilityVersion = "Xcode 9.3"; 221 | developmentRegion = en; 222 | hasScannedForEncodings = 0; 223 | knownRegions = ( 224 | en, 225 | Base, 226 | ); 227 | mainGroup = 4DA2846423AB1C9E00A44538; 228 | productRefGroup = 4DA2846E23AB1C9E00A44538 /* Products */; 229 | projectDirPath = ""; 230 | projectRoot = ""; 231 | targets = ( 232 | 4DA2846C23AB1C9E00A44538 /* SwiftUITextEditor */, 233 | 4DA2848423AB1CA000A44538 /* SwiftUITextEditorTests */, 234 | 4DA2848F23AB1CA100A44538 /* SwiftUITextEditorUITests */, 235 | ); 236 | }; 237 | /* End PBXProject section */ 238 | 239 | /* Begin PBXResourcesBuildPhase section */ 240 | 4DA2846B23AB1C9E00A44538 /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 4DA2847F23AB1CA000A44538 /* LaunchScreen.storyboard in Resources */, 245 | 4DA2847C23AB1CA000A44538 /* Assets.xcassets in Resources */, 246 | 4DA2847A23AB1C9E00A44538 /* Main.storyboard in Resources */, 247 | 4DA284A523AB237800A44538 /* New Document.txt in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 4DA2848323AB1CA000A44538 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 4DA2848E23AB1CA100A44538 /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXResourcesBuildPhase section */ 266 | 267 | /* Begin PBXSourcesBuildPhase section */ 268 | 4DA2846923AB1C9E00A44538 /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 4DA2847723AB1C9E00A44538 /* Document.swift in Sources */, 273 | 4DA2847323AB1C9E00A44538 /* DocumentBrowserViewController.swift in Sources */, 274 | 4DA2847523AB1C9E00A44538 /* DocumentView.swift in Sources */, 275 | 4DA2847123AB1C9E00A44538 /* AppDelegate.swift in Sources */, 276 | 4DA284A323AB1CD800A44538 /* TextView.swift in Sources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 4DA2848123AB1CA000A44538 /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 4DA2848A23AB1CA000A44538 /* SwiftUITextEditorTests.swift in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | 4DA2848C23AB1CA100A44538 /* Sources */ = { 289 | isa = PBXSourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 4DA2849523AB1CA100A44538 /* SwiftUITextEditorUITests.swift in Sources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | /* End PBXSourcesBuildPhase section */ 297 | 298 | /* Begin PBXTargetDependency section */ 299 | 4DA2848723AB1CA000A44538 /* PBXTargetDependency */ = { 300 | isa = PBXTargetDependency; 301 | target = 4DA2846C23AB1C9E00A44538 /* SwiftUITextEditor */; 302 | targetProxy = 4DA2848623AB1CA000A44538 /* PBXContainerItemProxy */; 303 | }; 304 | 4DA2849223AB1CA100A44538 /* PBXTargetDependency */ = { 305 | isa = PBXTargetDependency; 306 | target = 4DA2846C23AB1C9E00A44538 /* SwiftUITextEditor */; 307 | targetProxy = 4DA2849123AB1CA100A44538 /* PBXContainerItemProxy */; 308 | }; 309 | /* End PBXTargetDependency section */ 310 | 311 | /* Begin PBXVariantGroup section */ 312 | 4DA2847823AB1C9E00A44538 /* Main.storyboard */ = { 313 | isa = PBXVariantGroup; 314 | children = ( 315 | 4DA2847923AB1C9E00A44538 /* Base */, 316 | ); 317 | name = Main.storyboard; 318 | sourceTree = ""; 319 | }; 320 | 4DA2847D23AB1CA000A44538 /* LaunchScreen.storyboard */ = { 321 | isa = PBXVariantGroup; 322 | children = ( 323 | 4DA2847E23AB1CA000A44538 /* Base */, 324 | ); 325 | name = LaunchScreen.storyboard; 326 | sourceTree = ""; 327 | }; 328 | /* End PBXVariantGroup section */ 329 | 330 | /* Begin XCBuildConfiguration section */ 331 | 4DA2849723AB1CA100A44538 /* Debug */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | ALWAYS_SEARCH_USER_PATHS = NO; 335 | CLANG_ANALYZER_NONNULL = YES; 336 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 338 | CLANG_CXX_LIBRARY = "libc++"; 339 | CLANG_ENABLE_MODULES = YES; 340 | CLANG_ENABLE_OBJC_ARC = YES; 341 | CLANG_ENABLE_OBJC_WEAK = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 347 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 348 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INFINITE_RECURSION = YES; 352 | CLANG_WARN_INT_CONVERSION = YES; 353 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 355 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 357 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 358 | CLANG_WARN_STRICT_PROTOTYPES = YES; 359 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 360 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 361 | CLANG_WARN_UNREACHABLE_CODE = YES; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | COPY_PHASE_STRIP = NO; 364 | DEBUG_INFORMATION_FORMAT = dwarf; 365 | ENABLE_STRICT_OBJC_MSGSEND = YES; 366 | ENABLE_TESTABILITY = YES; 367 | GCC_C_LANGUAGE_STANDARD = gnu11; 368 | GCC_DYNAMIC_NO_PIC = NO; 369 | GCC_NO_COMMON_BLOCKS = YES; 370 | GCC_OPTIMIZATION_LEVEL = 0; 371 | GCC_PREPROCESSOR_DEFINITIONS = ( 372 | "DEBUG=1", 373 | "$(inherited)", 374 | ); 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 382 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 383 | MTL_FAST_MATH = YES; 384 | ONLY_ACTIVE_ARCH = YES; 385 | SDKROOT = iphoneos; 386 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 387 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 388 | }; 389 | name = Debug; 390 | }; 391 | 4DA2849823AB1CA100A44538 /* Release */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ANALYZER_NONNULL = YES; 396 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 397 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 398 | CLANG_CXX_LIBRARY = "libc++"; 399 | CLANG_ENABLE_MODULES = YES; 400 | CLANG_ENABLE_OBJC_ARC = YES; 401 | CLANG_ENABLE_OBJC_WEAK = YES; 402 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_COMMA = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 407 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 408 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 409 | CLANG_WARN_EMPTY_BODY = YES; 410 | CLANG_WARN_ENUM_CONVERSION = YES; 411 | CLANG_WARN_INFINITE_RECURSION = YES; 412 | CLANG_WARN_INT_CONVERSION = YES; 413 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 414 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 415 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 416 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 417 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 418 | CLANG_WARN_STRICT_PROTOTYPES = YES; 419 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 420 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 421 | CLANG_WARN_UNREACHABLE_CODE = YES; 422 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 423 | COPY_PHASE_STRIP = NO; 424 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 425 | ENABLE_NS_ASSERTIONS = NO; 426 | ENABLE_STRICT_OBJC_MSGSEND = YES; 427 | GCC_C_LANGUAGE_STANDARD = gnu11; 428 | GCC_NO_COMMON_BLOCKS = YES; 429 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 430 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 431 | GCC_WARN_UNDECLARED_SELECTOR = YES; 432 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 433 | GCC_WARN_UNUSED_FUNCTION = YES; 434 | GCC_WARN_UNUSED_VARIABLE = YES; 435 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 436 | MTL_ENABLE_DEBUG_INFO = NO; 437 | MTL_FAST_MATH = YES; 438 | SDKROOT = iphoneos; 439 | SWIFT_COMPILATION_MODE = wholemodule; 440 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 441 | VALIDATE_PRODUCT = YES; 442 | }; 443 | name = Release; 444 | }; 445 | 4DA2849A23AB1CA100A44538 /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 449 | CODE_SIGN_STYLE = Automatic; 450 | DEVELOPMENT_TEAM = SJHNKGG86R; 451 | INFOPLIST_FILE = SwiftUITextEditor/Info.plist; 452 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 453 | LD_RUNPATH_SEARCH_PATHS = ( 454 | "$(inherited)", 455 | "@executable_path/Frameworks", 456 | ); 457 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditor; 458 | PRODUCT_NAME = "$(TARGET_NAME)"; 459 | SWIFT_VERSION = 5.0; 460 | TARGETED_DEVICE_FAMILY = "1,2"; 461 | }; 462 | name = Debug; 463 | }; 464 | 4DA2849B23AB1CA100A44538 /* Release */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | CODE_SIGN_STYLE = Automatic; 469 | DEVELOPMENT_TEAM = SJHNKGG86R; 470 | INFOPLIST_FILE = SwiftUITextEditor/Info.plist; 471 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 472 | LD_RUNPATH_SEARCH_PATHS = ( 473 | "$(inherited)", 474 | "@executable_path/Frameworks", 475 | ); 476 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditor; 477 | PRODUCT_NAME = "$(TARGET_NAME)"; 478 | SWIFT_VERSION = 5.0; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | }; 481 | name = Release; 482 | }; 483 | 4DA2849D23AB1CA100A44538 /* Debug */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 487 | BUNDLE_LOADER = "$(TEST_HOST)"; 488 | CODE_SIGN_STYLE = Automatic; 489 | DEVELOPMENT_TEAM = SJHNKGG86R; 490 | INFOPLIST_FILE = SwiftUITextEditorTests/Info.plist; 491 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 492 | LD_RUNPATH_SEARCH_PATHS = ( 493 | "$(inherited)", 494 | "@executable_path/Frameworks", 495 | "@loader_path/Frameworks", 496 | ); 497 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditorTests; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | SWIFT_VERSION = 5.0; 500 | TARGETED_DEVICE_FAMILY = "1,2"; 501 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUITextEditor.app/SwiftUITextEditor"; 502 | }; 503 | name = Debug; 504 | }; 505 | 4DA2849E23AB1CA100A44538 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 509 | BUNDLE_LOADER = "$(TEST_HOST)"; 510 | CODE_SIGN_STYLE = Automatic; 511 | DEVELOPMENT_TEAM = SJHNKGG86R; 512 | INFOPLIST_FILE = SwiftUITextEditorTests/Info.plist; 513 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 514 | LD_RUNPATH_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "@executable_path/Frameworks", 517 | "@loader_path/Frameworks", 518 | ); 519 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditorTests; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | SWIFT_VERSION = 5.0; 522 | TARGETED_DEVICE_FAMILY = "1,2"; 523 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUITextEditor.app/SwiftUITextEditor"; 524 | }; 525 | name = Release; 526 | }; 527 | 4DA284A023AB1CA100A44538 /* Debug */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 531 | CODE_SIGN_STYLE = Automatic; 532 | DEVELOPMENT_TEAM = SJHNKGG86R; 533 | INFOPLIST_FILE = SwiftUITextEditorUITests/Info.plist; 534 | LD_RUNPATH_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "@executable_path/Frameworks", 537 | "@loader_path/Frameworks", 538 | ); 539 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditorUITests; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | SWIFT_VERSION = 5.0; 542 | TARGETED_DEVICE_FAMILY = "1,2"; 543 | TEST_TARGET_NAME = SwiftUITextEditor; 544 | }; 545 | name = Debug; 546 | }; 547 | 4DA284A123AB1CA100A44538 /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 551 | CODE_SIGN_STYLE = Automatic; 552 | DEVELOPMENT_TEAM = SJHNKGG86R; 553 | INFOPLIST_FILE = SwiftUITextEditorUITests/Info.plist; 554 | LD_RUNPATH_SEARCH_PATHS = ( 555 | "$(inherited)", 556 | "@executable_path/Frameworks", 557 | "@loader_path/Frameworks", 558 | ); 559 | PRODUCT_BUNDLE_IDENTIFIER = com.SwiftDevJournal.SwiftUITextEditorUITests; 560 | PRODUCT_NAME = "$(TARGET_NAME)"; 561 | SWIFT_VERSION = 5.0; 562 | TARGETED_DEVICE_FAMILY = "1,2"; 563 | TEST_TARGET_NAME = SwiftUITextEditor; 564 | }; 565 | name = Release; 566 | }; 567 | /* End XCBuildConfiguration section */ 568 | 569 | /* Begin XCConfigurationList section */ 570 | 4DA2846823AB1C9E00A44538 /* Build configuration list for PBXProject "SwiftUITextEditor" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 4DA2849723AB1CA100A44538 /* Debug */, 574 | 4DA2849823AB1CA100A44538 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 4DA2849923AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditor" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 4DA2849A23AB1CA100A44538 /* Debug */, 583 | 4DA2849B23AB1CA100A44538 /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | 4DA2849C23AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditorTests" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | 4DA2849D23AB1CA100A44538 /* Debug */, 592 | 4DA2849E23AB1CA100A44538 /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | defaultConfigurationName = Release; 596 | }; 597 | 4DA2849F23AB1CA100A44538 /* Build configuration list for PBXNativeTarget "SwiftUITextEditorUITests" */ = { 598 | isa = XCConfigurationList; 599 | buildConfigurations = ( 600 | 4DA284A023AB1CA100A44538 /* Debug */, 601 | 4DA284A123AB1CA100A44538 /* Release */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | /* End XCConfigurationList section */ 607 | }; 608 | rootObject = 4DA2846523AB1C9E00A44538 /* Project object */; 609 | } 610 | -------------------------------------------------------------------------------- /SwiftUITextEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftUITextEditor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftUITextEditor/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftUITextEditor 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func application(_ app: UIApplication, open inputURL: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { 40 | // Ensure the URL is a file URL 41 | guard inputURL.isFileURL else { return false } 42 | 43 | // Reveal / import the document at the URL 44 | guard let documentBrowserViewController = window?.rootViewController as? DocumentBrowserViewController else { return false } 45 | 46 | documentBrowserViewController.revealDocument(at: inputURL, importIfNeeded: true) { (revealedDocumentURL, error) in 47 | if let error = error { 48 | // Handle the error appropriately 49 | print("Failed to reveal the document at URL \(inputURL) with error: '\(error)'") 50 | return 51 | } 52 | 53 | // Present the Document View Controller for the revealed URL 54 | documentBrowserViewController.presentDocument(at: revealedDocumentURL!) 55 | } 56 | 57 | return true 58 | } 59 | 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /SwiftUITextEditor/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 | } -------------------------------------------------------------------------------- /SwiftUITextEditor/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftUITextEditor/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 | -------------------------------------------------------------------------------- /SwiftUITextEditor/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 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SwiftUITextEditor/Document.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Document.swift 3 | // SwiftUITextEditor 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Document: UIDocument { 12 | var text = "" 13 | 14 | override func contents(forType typeName: String) throws -> Any { 15 | // Encode your document with an instance of NSData or NSFileWrapper 16 | return try NSKeyedArchiver.archivedData(withRootObject: text, requiringSecureCoding: true) 17 | } 18 | 19 | override func load(fromContents contents: Any, ofType typeName: String?) throws { 20 | // Load your document from contents 21 | guard let data = contents as? Data else { return } 22 | guard let fileContents = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? String else { return } 23 | text = fileContents 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /SwiftUITextEditor/DocumentBrowserViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DocumentBrowserViewController.swift 3 | // SwiftUITextEditor 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | delegate = self 18 | 19 | allowsDocumentCreation = true 20 | allowsPickingMultipleItems = false 21 | 22 | // Update the style of the UIDocumentBrowserViewController 23 | // browserUserInterfaceStyle = .dark 24 | // view.tintColor = .white 25 | 26 | // Specify the allowed content types of your application via the Info.plist. 27 | 28 | // Do any additional setup after loading the view. 29 | } 30 | 31 | 32 | // MARK: UIDocumentBrowserViewControllerDelegate 33 | 34 | func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) { 35 | let newDocumentURL: URL? = Bundle.main.url(forResource: "New Document", 36 | withExtension: "txt") 37 | 38 | // Set the URL for the new document here. Optionally, you can present a template chooser before calling the importHandler. 39 | // Make sure the importHandler is always called, even if the user cancels the creation request. 40 | if newDocumentURL != nil { 41 | importHandler(newDocumentURL, .copy) 42 | } else { 43 | importHandler(nil, .none) 44 | } 45 | } 46 | 47 | func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) { 48 | guard let sourceURL = documentURLs.first else { return } 49 | 50 | // Present the Document View Controller for the first document that was picked. 51 | // If you support picking multiple items, make sure you handle them all. 52 | presentDocument(at: sourceURL) 53 | } 54 | 55 | func documentBrowser(_ controller: UIDocumentBrowserViewController, didImportDocumentAt sourceURL: URL, toDestinationURL destinationURL: URL) { 56 | // Present the Document View Controller for the new newly created document 57 | presentDocument(at: destinationURL) 58 | } 59 | 60 | func documentBrowser(_ controller: UIDocumentBrowserViewController, failedToImportDocumentAt documentURL: URL, error: Error?) { 61 | // Make sure to handle the failed import appropriately, e.g., by presenting an error message to the user. 62 | } 63 | 64 | // MARK: Document Presentation 65 | 66 | func presentDocument(at documentURL: URL) { 67 | let document = Document(fileURL: documentURL) 68 | 69 | // Access the document 70 | document.open(completionHandler: { success in 71 | if success { 72 | // Display the content of the document: 73 | let view = DocumentView(document: document, dismiss: { 74 | self.closeDocument(document) 75 | }) 76 | 77 | let documentViewController = UIHostingController(rootView: view) 78 | self.present(documentViewController, animated: true, completion: nil) 79 | } else { 80 | // Make sure to handle the failed import appropriately, e.g., by presenting an error message to the user. 81 | } 82 | }) 83 | } 84 | 85 | func closeDocument(_ document: Document) { 86 | dismiss(animated: true) { 87 | document.close(completionHandler: nil) 88 | } 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /SwiftUITextEditor/DocumentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DocumentView.swift 3 | // SwiftUITextEditor 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct DocumentView: View { 12 | @State var document: Document 13 | var dismiss: () -> Void 14 | 15 | var body: some View { 16 | VStack { 17 | HStack { 18 | Text("File Name") 19 | .foregroundColor(.secondary) 20 | 21 | Text(document.fileURL.lastPathComponent) 22 | } 23 | TextView(document: $document) 24 | Button("Done", action: dismiss) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SwiftUITextEditor/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeIconFiles 11 | 12 | CFBundleTypeName 13 | PlainText 14 | CFBundleTypeRole 15 | Editor 16 | LSHandlerRank 17 | Alternate 18 | LSItemContentTypes 19 | 20 | public.plain-text 21 | 22 | 23 | 24 | CFBundleExecutable 25 | $(EXECUTABLE_NAME) 26 | CFBundleIdentifier 27 | $(PRODUCT_BUNDLE_IDENTIFIER) 28 | CFBundleInfoDictionaryVersion 29 | 6.0 30 | CFBundleName 31 | $(PRODUCT_NAME) 32 | CFBundlePackageType 33 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 34 | CFBundleShortVersionString 35 | 1.0 36 | CFBundleVersion 37 | 1 38 | LSRequiresIPhoneOS 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIMainStoryboardFile 43 | Main 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | UISupportsDocumentBrowser 62 | 63 | UTImportedTypeDeclarations 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /SwiftUITextEditor/New Document.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftDevJournal/SwiftUITextEditor/ed21a27c0447cb7b1d6206bb1f4ed476f6941df5/SwiftUITextEditor/New Document.txt -------------------------------------------------------------------------------- /SwiftUITextEditor/TextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextView.swift 3 | // SwiftUITextEditor 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import UIKit 11 | 12 | struct TextView: UIViewRepresentable { 13 | @Binding var document: Document 14 | 15 | func makeUIView(context: Context) -> UITextView { 16 | let view = UITextView() 17 | view.isScrollEnabled = true 18 | view.isEditable = true 19 | view.isUserInteractionEnabled = true 20 | view.contentInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 5) 21 | view.delegate = context.coordinator 22 | return view 23 | } 24 | 25 | func updateUIView(_ uiView: UITextView, context: Context) { 26 | uiView.text = document.text 27 | } 28 | 29 | func makeCoordinator() -> TextView.Coordinator { 30 | Coordinator(self) 31 | } 32 | 33 | class Coordinator: NSObject, UITextViewDelegate { 34 | var control: TextView 35 | 36 | init(_ control: TextView) { 37 | self.control = control 38 | } 39 | 40 | func textViewDidChange(_ textView: UITextView) { 41 | control.document.text = textView.text 42 | control.document.updateChangeCount(.done) 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SwiftUITextEditorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftUITextEditorTests/SwiftUITextEditorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUITextEditorTests.swift 3 | // SwiftUITextEditorTests 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftUITextEditor 11 | 12 | class SwiftUITextEditorTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SwiftUITextEditorUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftUITextEditorUITests/SwiftUITextEditorUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUITextEditorUITests.swift 3 | // SwiftUITextEditorUITests 4 | // 5 | // Created by mark on 12/18/19. 6 | // Copyright © 2019 Swift Dev Journal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftUITextEditorUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | --------------------------------------------------------------------------------