├── .codecov.yml ├── .gitignore ├── .travis.yml ├── Curator.podspec ├── Curator.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Curator.xcscheme ├── Curator ├── Curator.h ├── Curator.swift ├── CuratorError.swift ├── CuratorExtension.swift ├── CuratorExtension_Keepable.swift ├── CuratorExtension_URL.swift ├── CuratorKeepable.swift ├── CuratorKeyLocation.swift ├── CuratorLocation.swift ├── CuratorSupportedDirectory.swift ├── CuratorUtility.swift ├── Curator_EasyExtension.swift └── Info.plist ├── LICENSE ├── README.md └── Tests ├── CuratorLocationTests.swift ├── CuratorTests.swift ├── Info.plist ├── PerformanceTests.swift ├── SupportedDirectoryTests.swift ├── URLExtensionTests.swift └── UtilityTests.swift /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - Tests 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gitignore template from https://github.com/github/gitignore 2 | 3 | # Xcode 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | *.dSYM.zip 30 | *.dSYM 31 | 32 | ## Playgrounds 33 | timeline.xctimeline 34 | playground.xcworkspace 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | - IOS_FRAMEWORK_SCHEME="Curator" 8 | matrix: 9 | - DESTINATION="OS=10.0,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" 10 | # - DESTINATION="OS=8.4,name=iPhone 5" SCHEME="$IOS_FRAMEWORK_SCHEME" 11 | before_install: 12 | - gem install xcpretty -N 13 | script: 14 | - set -o pipefail 15 | - xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" -configuration Debug test | xcpretty 16 | - xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" -configuration Release | xcpretty 17 | after_success: 18 | - bash <(curl -s https://codecov.io/bash) 19 | -------------------------------------------------------------------------------- /Curator.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Curator' 3 | s.summary = 'Curator is a lightweight key-value file manager written in Swift.' 4 | s.version = '1.0' 5 | s.authors = { 'Yanzhi Wang' => 'yzwang.nj@gmail.com' } 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.homepage = "https://github.com/puttin/Curator" 8 | s.source = { :git => 'https://github.com/puttin/Curator.git', :tag => s.version } 9 | 10 | s.ios.deployment_target = '8.0' 11 | 12 | s.ios.source_files = 'Curator/*.swift' 13 | 14 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0' } 15 | end 16 | -------------------------------------------------------------------------------- /Curator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D2327A9A1D901A0E00A80734 /* CuratorLocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2327A991D901A0E00A80734 /* CuratorLocationTests.swift */; }; 11 | D2327A9C1D9177B600A80734 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2327A9B1D9177B600A80734 /* PerformanceTests.swift */; }; 12 | D2327A9E1D92ACF900A80734 /* UtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2327A9D1D92ACF900A80734 /* UtilityTests.swift */; }; 13 | D2327AA01D92B1FC00A80734 /* CuratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2327A9F1D92B1FC00A80734 /* CuratorTests.swift */; }; 14 | D29AD27C1D8ECC5F008AC999 /* SupportedDirectoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29AD27A1D8ECC37008AC999 /* SupportedDirectoryTests.swift */; }; 15 | D2D9997B1D8C05CF0097EC78 /* Curator.h in Headers */ = {isa = PBXBuildFile; fileRef = D2D999791D8C05CF0097EC78 /* Curator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | D2D999821D8C06CB0097EC78 /* CuratorSupportedDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999811D8C06CB0097EC78 /* CuratorSupportedDirectory.swift */; }; 17 | D2D999841D8C06E10097EC78 /* CuratorUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999831D8C06E10097EC78 /* CuratorUtility.swift */; }; 18 | D2D999861D8C088C0097EC78 /* CuratorKeyLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999851D8C088C0097EC78 /* CuratorKeyLocation.swift */; }; 19 | D2D999881D8C0D7B0097EC78 /* CuratorLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999871D8C0D7B0097EC78 /* CuratorLocation.swift */; }; 20 | D2D9998A1D8C0EF60097EC78 /* CuratorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999891D8C0EF60097EC78 /* CuratorError.swift */; }; 21 | D2D9998C1D8C14CA0097EC78 /* CuratorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D9998B1D8C14CA0097EC78 /* CuratorExtension.swift */; }; 22 | D2D9998E1D8CF7520097EC78 /* Curator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D9998D1D8CF7520097EC78 /* Curator.swift */; }; 23 | D2D999901D8D06300097EC78 /* CuratorKeepable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D9998F1D8D06300097EC78 /* CuratorKeepable.swift */; }; 24 | D2D999921D8D72EE0097EC78 /* CuratorExtension_Keepable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999911D8D72EE0097EC78 /* CuratorExtension_Keepable.swift */; }; 25 | D2D999941D8EBA600097EC78 /* CuratorExtension_URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999931D8EBA600097EC78 /* CuratorExtension_URL.swift */; }; 26 | D2D9999E1D8EC2E20097EC78 /* Curator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2D999761D8C05CF0097EC78 /* Curator.framework */; }; 27 | D2D999A61D8EC7740097EC78 /* URLExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D999A41D8EC6F70097EC78 /* URLExtensionTests.swift */; }; 28 | D2FFEE961D96CA5900F1D62E /* Curator_EasyExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FFEE951D96CA5900F1D62E /* Curator_EasyExtension.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | D2D9999F1D8EC2E20097EC78 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = D2D9996D1D8C05CF0097EC78 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = D2D999751D8C05CF0097EC78; 37 | remoteInfo = Curator; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | D2327A991D901A0E00A80734 /* CuratorLocationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorLocationTests.swift; sourceTree = ""; }; 43 | D2327A9B1D9177B600A80734 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = ""; }; 44 | D2327A9D1D92ACF900A80734 /* UtilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityTests.swift; sourceTree = ""; }; 45 | D2327A9F1D92B1FC00A80734 /* CuratorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorTests.swift; sourceTree = ""; }; 46 | D29AD27A1D8ECC37008AC999 /* SupportedDirectoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SupportedDirectoryTests.swift; sourceTree = ""; }; 47 | D2D999761D8C05CF0097EC78 /* Curator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Curator.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | D2D999791D8C05CF0097EC78 /* Curator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Curator.h; sourceTree = ""; }; 49 | D2D9997A1D8C05CF0097EC78 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | D2D999811D8C06CB0097EC78 /* CuratorSupportedDirectory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorSupportedDirectory.swift; sourceTree = ""; }; 51 | D2D999831D8C06E10097EC78 /* CuratorUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorUtility.swift; sourceTree = ""; }; 52 | D2D999851D8C088C0097EC78 /* CuratorKeyLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorKeyLocation.swift; sourceTree = ""; }; 53 | D2D999871D8C0D7B0097EC78 /* CuratorLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorLocation.swift; sourceTree = ""; }; 54 | D2D999891D8C0EF60097EC78 /* CuratorError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorError.swift; sourceTree = ""; }; 55 | D2D9998B1D8C14CA0097EC78 /* CuratorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorExtension.swift; sourceTree = ""; }; 56 | D2D9998D1D8CF7520097EC78 /* Curator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Curator.swift; sourceTree = ""; }; 57 | D2D9998F1D8D06300097EC78 /* CuratorKeepable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorKeepable.swift; sourceTree = ""; }; 58 | D2D999911D8D72EE0097EC78 /* CuratorExtension_Keepable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorExtension_Keepable.swift; sourceTree = ""; }; 59 | D2D999931D8EBA600097EC78 /* CuratorExtension_URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuratorExtension_URL.swift; sourceTree = ""; }; 60 | D2D999991D8EC2E20097EC78 /* CuratorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CuratorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | D2D9999D1D8EC2E20097EC78 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | D2D999A41D8EC6F70097EC78 /* URLExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLExtensionTests.swift; sourceTree = ""; }; 63 | D2FFEE951D96CA5900F1D62E /* Curator_EasyExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Curator_EasyExtension.swift; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | D2D999721D8C05CF0097EC78 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | D2D999961D8EC2E20097EC78 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | D2D9999E1D8EC2E20097EC78 /* Curator.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | D2D9996C1D8C05CF0097EC78 = { 86 | isa = PBXGroup; 87 | children = ( 88 | D2D999781D8C05CF0097EC78 /* Curator */, 89 | D2D9999A1D8EC2E20097EC78 /* CuratorTests */, 90 | D2D999771D8C05CF0097EC78 /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | D2D999771D8C05CF0097EC78 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | D2D999761D8C05CF0097EC78 /* Curator.framework */, 98 | D2D999991D8EC2E20097EC78 /* CuratorTests.xctest */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | D2D999781D8C05CF0097EC78 /* Curator */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | D2D9998D1D8CF7520097EC78 /* Curator.swift */, 107 | D2FFEE951D96CA5900F1D62E /* Curator_EasyExtension.swift */, 108 | D2D999811D8C06CB0097EC78 /* CuratorSupportedDirectory.swift */, 109 | D2D999871D8C0D7B0097EC78 /* CuratorLocation.swift */, 110 | D2D999851D8C088C0097EC78 /* CuratorKeyLocation.swift */, 111 | D2D9998F1D8D06300097EC78 /* CuratorKeepable.swift */, 112 | D2D999891D8C0EF60097EC78 /* CuratorError.swift */, 113 | D2D999831D8C06E10097EC78 /* CuratorUtility.swift */, 114 | D2D9998B1D8C14CA0097EC78 /* CuratorExtension.swift */, 115 | D2D999911D8D72EE0097EC78 /* CuratorExtension_Keepable.swift */, 116 | D2D999931D8EBA600097EC78 /* CuratorExtension_URL.swift */, 117 | D2D999791D8C05CF0097EC78 /* Curator.h */, 118 | D2D9997A1D8C05CF0097EC78 /* Info.plist */, 119 | ); 120 | path = Curator; 121 | sourceTree = ""; 122 | }; 123 | D2D9999A1D8EC2E20097EC78 /* CuratorTests */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | D2D999A41D8EC6F70097EC78 /* URLExtensionTests.swift */, 127 | D2327A991D901A0E00A80734 /* CuratorLocationTests.swift */, 128 | D29AD27A1D8ECC37008AC999 /* SupportedDirectoryTests.swift */, 129 | D2327A9B1D9177B600A80734 /* PerformanceTests.swift */, 130 | D2327A9F1D92B1FC00A80734 /* CuratorTests.swift */, 131 | D2327A9D1D92ACF900A80734 /* UtilityTests.swift */, 132 | D2D9999D1D8EC2E20097EC78 /* Info.plist */, 133 | ); 134 | name = CuratorTests; 135 | path = Tests; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXHeadersBuildPhase section */ 141 | D2D999731D8C05CF0097EC78 /* Headers */ = { 142 | isa = PBXHeadersBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | D2D9997B1D8C05CF0097EC78 /* Curator.h in Headers */, 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXHeadersBuildPhase section */ 150 | 151 | /* Begin PBXNativeTarget section */ 152 | D2D999751D8C05CF0097EC78 /* Curator */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = D2D9997E1D8C05CF0097EC78 /* Build configuration list for PBXNativeTarget "Curator" */; 155 | buildPhases = ( 156 | D2D999711D8C05CF0097EC78 /* Sources */, 157 | D2D999721D8C05CF0097EC78 /* Frameworks */, 158 | D2D999731D8C05CF0097EC78 /* Headers */, 159 | D2D999741D8C05CF0097EC78 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | ); 165 | name = Curator; 166 | productName = Curator; 167 | productReference = D2D999761D8C05CF0097EC78 /* Curator.framework */; 168 | productType = "com.apple.product-type.framework"; 169 | }; 170 | D2D999981D8EC2E20097EC78 /* CuratorTests */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = D2D999A11D8EC2E20097EC78 /* Build configuration list for PBXNativeTarget "CuratorTests" */; 173 | buildPhases = ( 174 | D2D999951D8EC2E20097EC78 /* Sources */, 175 | D2D999961D8EC2E20097EC78 /* Frameworks */, 176 | D2D999971D8EC2E20097EC78 /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | D2D999A01D8EC2E20097EC78 /* PBXTargetDependency */, 182 | ); 183 | name = CuratorTests; 184 | productName = CuratorTests; 185 | productReference = D2D999991D8EC2E20097EC78 /* CuratorTests.xctest */; 186 | productType = "com.apple.product-type.bundle.unit-test"; 187 | }; 188 | /* End PBXNativeTarget section */ 189 | 190 | /* Begin PBXProject section */ 191 | D2D9996D1D8C05CF0097EC78 /* Project object */ = { 192 | isa = PBXProject; 193 | attributes = { 194 | LastSwiftUpdateCheck = 0800; 195 | LastUpgradeCheck = 0800; 196 | ORGANIZATIONNAME = PuttinGear; 197 | TargetAttributes = { 198 | D2D999751D8C05CF0097EC78 = { 199 | CreatedOnToolsVersion = 8.0; 200 | LastSwiftMigration = 0800; 201 | ProvisioningStyle = Automatic; 202 | }; 203 | D2D999981D8EC2E20097EC78 = { 204 | CreatedOnToolsVersion = 8.0; 205 | ProvisioningStyle = Automatic; 206 | }; 207 | }; 208 | }; 209 | buildConfigurationList = D2D999701D8C05CF0097EC78 /* Build configuration list for PBXProject "Curator" */; 210 | compatibilityVersion = "Xcode 3.2"; 211 | developmentRegion = English; 212 | hasScannedForEncodings = 0; 213 | knownRegions = ( 214 | en, 215 | ); 216 | mainGroup = D2D9996C1D8C05CF0097EC78; 217 | productRefGroup = D2D999771D8C05CF0097EC78 /* Products */; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | D2D999751D8C05CF0097EC78 /* Curator */, 222 | D2D999981D8EC2E20097EC78 /* CuratorTests */, 223 | ); 224 | }; 225 | /* End PBXProject section */ 226 | 227 | /* Begin PBXResourcesBuildPhase section */ 228 | D2D999741D8C05CF0097EC78 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | D2D999971D8EC2E20097EC78 /* Resources */ = { 236 | isa = PBXResourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXResourcesBuildPhase section */ 243 | 244 | /* Begin PBXSourcesBuildPhase section */ 245 | D2D999711D8C05CF0097EC78 /* Sources */ = { 246 | isa = PBXSourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | D2D999821D8C06CB0097EC78 /* CuratorSupportedDirectory.swift in Sources */, 250 | D2D999901D8D06300097EC78 /* CuratorKeepable.swift in Sources */, 251 | D2D999881D8C0D7B0097EC78 /* CuratorLocation.swift in Sources */, 252 | D2D999921D8D72EE0097EC78 /* CuratorExtension_Keepable.swift in Sources */, 253 | D2D9998C1D8C14CA0097EC78 /* CuratorExtension.swift in Sources */, 254 | D2FFEE961D96CA5900F1D62E /* Curator_EasyExtension.swift in Sources */, 255 | D2D999841D8C06E10097EC78 /* CuratorUtility.swift in Sources */, 256 | D2D999861D8C088C0097EC78 /* CuratorKeyLocation.swift in Sources */, 257 | D2D9998A1D8C0EF60097EC78 /* CuratorError.swift in Sources */, 258 | D2D999941D8EBA600097EC78 /* CuratorExtension_URL.swift in Sources */, 259 | D2D9998E1D8CF7520097EC78 /* Curator.swift in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | D2D999951D8EC2E20097EC78 /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | D2327AA01D92B1FC00A80734 /* CuratorTests.swift in Sources */, 268 | D2327A9E1D92ACF900A80734 /* UtilityTests.swift in Sources */, 269 | D2327A9A1D901A0E00A80734 /* CuratorLocationTests.swift in Sources */, 270 | D29AD27C1D8ECC5F008AC999 /* SupportedDirectoryTests.swift in Sources */, 271 | D2327A9C1D9177B600A80734 /* PerformanceTests.swift in Sources */, 272 | D2D999A61D8EC7740097EC78 /* URLExtensionTests.swift in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXTargetDependency section */ 279 | D2D999A01D8EC2E20097EC78 /* PBXTargetDependency */ = { 280 | isa = PBXTargetDependency; 281 | target = D2D999751D8C05CF0097EC78 /* Curator */; 282 | targetProxy = D2D9999F1D8EC2E20097EC78 /* PBXContainerItemProxy */; 283 | }; 284 | /* End PBXTargetDependency section */ 285 | 286 | /* Begin XCBuildConfiguration section */ 287 | D2D9997C1D8C05CF0097EC78 /* Debug */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ALWAYS_SEARCH_USER_PATHS = NO; 291 | CLANG_ANALYZER_NONNULL = YES; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_CONSTANT_CONVERSION = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 300 | CLANG_WARN_EMPTY_BODY = YES; 301 | CLANG_WARN_ENUM_CONVERSION = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 306 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 310 | COPY_PHASE_STRIP = NO; 311 | CURRENT_PROJECT_VERSION = 1; 312 | DEBUG_INFORMATION_FORMAT = dwarf; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | ENABLE_TESTABILITY = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu99; 316 | GCC_DYNAMIC_NO_PIC = NO; 317 | GCC_NO_COMMON_BLOCKS = YES; 318 | GCC_OPTIMIZATION_LEVEL = 0; 319 | GCC_PREPROCESSOR_DEFINITIONS = ( 320 | "DEBUG=1", 321 | "$(inherited)", 322 | ); 323 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 324 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 325 | GCC_WARN_UNDECLARED_SELECTOR = YES; 326 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 327 | GCC_WARN_UNUSED_FUNCTION = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 330 | MTL_ENABLE_DEBUG_INFO = YES; 331 | ONLY_ACTIVE_ARCH = YES; 332 | SDKROOT = iphoneos; 333 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 334 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 335 | TARGETED_DEVICE_FAMILY = "1,2"; 336 | VERSIONING_SYSTEM = "apple-generic"; 337 | VERSION_INFO_PREFIX = ""; 338 | }; 339 | name = Debug; 340 | }; 341 | D2D9997D1D8C05CF0097EC78 /* Release */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ALWAYS_SEARCH_USER_PATHS = NO; 345 | CLANG_ANALYZER_NONNULL = YES; 346 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 347 | CLANG_CXX_LIBRARY = "libc++"; 348 | CLANG_ENABLE_MODULES = YES; 349 | CLANG_ENABLE_OBJC_ARC = YES; 350 | CLANG_WARN_BOOL_CONVERSION = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 354 | CLANG_WARN_EMPTY_BODY = YES; 355 | CLANG_WARN_ENUM_CONVERSION = YES; 356 | CLANG_WARN_INFINITE_RECURSION = YES; 357 | CLANG_WARN_INT_CONVERSION = YES; 358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 359 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 360 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 361 | CLANG_WARN_UNREACHABLE_CODE = YES; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | COPY_PHASE_STRIP = NO; 365 | CURRENT_PROJECT_VERSION = 1; 366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 367 | ENABLE_NS_ASSERTIONS = NO; 368 | ENABLE_STRICT_OBJC_MSGSEND = YES; 369 | GCC_C_LANGUAGE_STANDARD = gnu99; 370 | GCC_NO_COMMON_BLOCKS = YES; 371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 373 | GCC_WARN_UNDECLARED_SELECTOR = YES; 374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 375 | GCC_WARN_UNUSED_FUNCTION = YES; 376 | GCC_WARN_UNUSED_VARIABLE = YES; 377 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 378 | MTL_ENABLE_DEBUG_INFO = NO; 379 | SDKROOT = iphoneos; 380 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 381 | TARGETED_DEVICE_FAMILY = "1,2"; 382 | VALIDATE_PRODUCT = YES; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | VERSION_INFO_PREFIX = ""; 385 | }; 386 | name = Release; 387 | }; 388 | D2D9997F1D8C05CF0097EC78 /* Debug */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | CLANG_ENABLE_MODULES = YES; 392 | CODE_SIGN_IDENTITY = ""; 393 | DEFINES_MODULE = YES; 394 | DYLIB_COMPATIBILITY_VERSION = 1; 395 | DYLIB_CURRENT_VERSION = 1; 396 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 397 | INFOPLIST_FILE = Curator/Info.plist; 398 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 399 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 400 | PRODUCT_BUNDLE_IDENTIFIER = com.puttingear.utility.Curator; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | SKIP_INSTALL = YES; 403 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 404 | SWIFT_VERSION = 3.0; 405 | }; 406 | name = Debug; 407 | }; 408 | D2D999801D8C05CF0097EC78 /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | CLANG_ENABLE_MODULES = YES; 412 | CODE_SIGN_IDENTITY = ""; 413 | DEFINES_MODULE = YES; 414 | DYLIB_COMPATIBILITY_VERSION = 1; 415 | DYLIB_CURRENT_VERSION = 1; 416 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 417 | INFOPLIST_FILE = Curator/Info.plist; 418 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 419 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 420 | PRODUCT_BUNDLE_IDENTIFIER = com.puttingear.utility.Curator; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | SKIP_INSTALL = YES; 423 | SWIFT_VERSION = 3.0; 424 | }; 425 | name = Release; 426 | }; 427 | D2D999A21D8EC2E20097EC78 /* Debug */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | INFOPLIST_FILE = Tests/Info.plist; 431 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 432 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 433 | PRODUCT_BUNDLE_IDENTIFIER = com.puttingear.utility.CuratorTests; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | SWIFT_VERSION = 3.0; 436 | }; 437 | name = Debug; 438 | }; 439 | D2D999A31D8EC2E20097EC78 /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | INFOPLIST_FILE = Tests/Info.plist; 443 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 444 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 445 | PRODUCT_BUNDLE_IDENTIFIER = com.puttingear.utility.CuratorTests; 446 | PRODUCT_NAME = "$(TARGET_NAME)"; 447 | SWIFT_VERSION = 3.0; 448 | }; 449 | name = Release; 450 | }; 451 | /* End XCBuildConfiguration section */ 452 | 453 | /* Begin XCConfigurationList section */ 454 | D2D999701D8C05CF0097EC78 /* Build configuration list for PBXProject "Curator" */ = { 455 | isa = XCConfigurationList; 456 | buildConfigurations = ( 457 | D2D9997C1D8C05CF0097EC78 /* Debug */, 458 | D2D9997D1D8C05CF0097EC78 /* Release */, 459 | ); 460 | defaultConfigurationIsVisible = 0; 461 | defaultConfigurationName = Release; 462 | }; 463 | D2D9997E1D8C05CF0097EC78 /* Build configuration list for PBXNativeTarget "Curator" */ = { 464 | isa = XCConfigurationList; 465 | buildConfigurations = ( 466 | D2D9997F1D8C05CF0097EC78 /* Debug */, 467 | D2D999801D8C05CF0097EC78 /* Release */, 468 | ); 469 | defaultConfigurationIsVisible = 0; 470 | defaultConfigurationName = Release; 471 | }; 472 | D2D999A11D8EC2E20097EC78 /* Build configuration list for PBXNativeTarget "CuratorTests" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | D2D999A21D8EC2E20097EC78 /* Debug */, 476 | D2D999A31D8EC2E20097EC78 /* Release */, 477 | ); 478 | defaultConfigurationIsVisible = 0; 479 | defaultConfigurationName = Release; 480 | }; 481 | /* End XCConfigurationList section */ 482 | }; 483 | rootObject = D2D9996D1D8C05CF0097EC78 /* Project object */; 484 | } 485 | -------------------------------------------------------------------------------- /Curator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Curator.xcodeproj/xcshareddata/xcschemes/Curator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Curator/Curator.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | //! Project version number for Curator. 4 | FOUNDATION_EXPORT double CuratorVersionNumber; 5 | 6 | //! Project version string for Curator. 7 | FOUNDATION_EXPORT const unsigned char CuratorVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | 11 | 12 | -------------------------------------------------------------------------------- /Curator/Curator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Curator { 4 | 5 | } 6 | 7 | extension Curator { 8 | public static func save( 9 | _ keepable: CuratorKeepable, 10 | to location: CuratorLocation, 11 | options: Data.WritingOptions = [ .atomic] 12 | ) throws { 13 | let url = try location.standardizedFileURL() 14 | 15 | let fileExistResult = url.crt.fileExist 16 | if fileExistResult.fileExist { 17 | if fileExistResult.isDirectory { 18 | throw Error.locationIsDirectory(location) 19 | } 20 | else if options.contains( .withoutOverwriting) { 21 | throw Error.locationFileExist(location) 22 | } 23 | } 24 | else { 25 | try url.crt.createDirectory(fileExistResult: fileExistResult) 26 | } 27 | 28 | let data = try keepable.asData() 29 | 30 | try data.write(to: url, options: options) 31 | } 32 | 33 | public static func getData( 34 | from location: CuratorLocation, 35 | options: Data.ReadingOptions = [] 36 | ) throws -> Data { 37 | let url = try location.standardizedFileURL() 38 | 39 | let fileExistResult = url.crt.fileExist 40 | 41 | if !fileExistResult.fileExist { 42 | throw Error.locationFileNotExist(location) 43 | } 44 | 45 | if fileExistResult.isDirectory { 46 | throw Error.locationIsDirectory(location) 47 | } 48 | 49 | let data = try Data( 50 | contentsOf: url, 51 | options: options 52 | ) 53 | 54 | return data 55 | } 56 | 57 | private static func convertToFilePathURL(from location: CuratorLocation) throws -> URL { 58 | let locationURL = try location.standardizedFileURL() 59 | let locationNSURL = locationURL as NSURL 60 | 61 | guard let resultURL = locationNSURL.filePathURL else { 62 | throw Error.unableToConvertToFilePathURL(from: location) 63 | } 64 | 65 | return resultURL 66 | } 67 | 68 | public static func move( 69 | from src: CuratorLocation, 70 | to dst: CuratorLocation 71 | ) throws { 72 | let srcURL = try convertToFilePathURL(from: src) 73 | 74 | let srcFileExistResult = srcURL.crt.fileExist 75 | 76 | if !srcFileExistResult.fileExist { 77 | throw Error.locationFileNotExist(src) 78 | } 79 | 80 | let dstURL = try dst.standardizedFileURL() 81 | 82 | if srcURL == dstURL { 83 | return 84 | } 85 | 86 | let dstFileExistResult = dstURL.crt.fileExist 87 | 88 | if dstFileExistResult.fileExist { 89 | throw Error.locationFileExist(dst) 90 | } 91 | try dstURL.crt.createDirectory(fileExistResult: dstFileExistResult) 92 | 93 | try CuratorFileManager.moveItem(at: srcURL, to: dstURL) 94 | } 95 | 96 | public static func delete( 97 | location: CuratorLocation, 98 | allowDirectory: Bool = false 99 | ) throws { 100 | let url = try location.standardizedFileURL() 101 | 102 | let fileExistResult = url.crt.fileExist 103 | 104 | if !fileExistResult.fileExist { 105 | throw Error.locationFileNotExist(location) 106 | } 107 | 108 | if !allowDirectory && fileExistResult.isDirectory { 109 | throw Error.locationIsDirectory(location) 110 | } 111 | 112 | try CuratorFileManager.removeItem(at: url) 113 | } 114 | 115 | public static func link( 116 | from src: CuratorLocation, 117 | to dst: CuratorLocation 118 | ) throws { 119 | let srcURL = try convertToFilePathURL(from: src) 120 | 121 | let srcFileExistResult = srcURL.crt.fileExist 122 | 123 | if !srcFileExistResult.fileExist { 124 | throw Error.locationFileNotExist(src) 125 | } 126 | 127 | let dstURL = try dst.standardizedFileURL() 128 | 129 | if srcURL == dstURL { 130 | return 131 | } 132 | 133 | let dstFileExistResult = dstURL.crt.fileExist 134 | 135 | if dstFileExistResult.fileExist { 136 | throw Error.locationFileExist(dst) 137 | } 138 | try dstURL.crt.createDirectory(fileExistResult: dstFileExistResult) 139 | 140 | try CuratorFileManager.linkItem(at: srcURL, to: dstURL) 141 | } 142 | 143 | public static func createDirectory( 144 | at location: CuratorLocation 145 | ) throws { 146 | let url = try location.standardizedFileURL() 147 | 148 | let directoryURL = url.crt.getDirectoryURL(greedy: true) 149 | 150 | try directoryURL.crt.createDirectory() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Curator/CuratorError.swift: -------------------------------------------------------------------------------- 1 | extension Curator { 2 | public enum Error: Swift.Error { 3 | case invalidLocation(CuratorLocation) 4 | case unableToConvertToFileURL(from: CuratorLocation) 5 | case unableToConvertToFilePathURL(from: CuratorLocation) 6 | case unableToObtainFileReferenceURL(from: CuratorLocation) 7 | case locationIsFile(CuratorLocation) 8 | case locationIsDirectory(CuratorLocation) 9 | case locationFileExist(CuratorLocation) 10 | case locationFileNotExist(CuratorLocation) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Curator/CuratorExtension.swift: -------------------------------------------------------------------------------- 1 | public protocol CuratorExtensionProtocol { 2 | associatedtype Base 3 | var base: Base { get } 4 | } 5 | 6 | public struct CuratorExtension: CuratorExtensionProtocol { 7 | public let base: Base 8 | init(_ base: Base) { 9 | self.base = base 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Curator/CuratorExtension_Keepable.swift: -------------------------------------------------------------------------------- 1 | extension CuratorKeepable { 2 | public var crt: CuratorExtension { 3 | return CuratorExtension(self) 4 | } 5 | } 6 | 7 | extension CuratorExtensionProtocol where Base == CuratorKeepable { 8 | public func save(to location: CuratorLocation) throws { 9 | try Curator.save(base, to: location) 10 | } 11 | 12 | public func save(to key: String, in directory: Curator.SupportedDirectory) throws { 13 | let location = Curator.KeyLocation(key: key, directory: directory) 14 | try save(to: location) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Curator/CuratorExtension_URL.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension URL { 4 | var crt: CuratorExtension { 5 | return CuratorExtension(self) 6 | } 7 | } 8 | 9 | extension CuratorExtensionProtocol where Base == URL { 10 | var hasDirectoryPath: Bool { 11 | if #available(iOS 9.0, OSX 10.11, *) { 12 | return base.hasDirectoryPath 13 | } else { 14 | let url = base.standardized 15 | let absoluteString = url.absoluteString 16 | let lastPathComponent = url.lastPathComponent 17 | let lastRange = absoluteString.range(of: lastPathComponent, options: .backwards)! 18 | 19 | if absoluteString.substring(from: lastRange.upperBound).hasPrefix("/") { 20 | return true 21 | } 22 | return false 23 | } 24 | } 25 | 26 | func getDirectoryURL(greedy: Bool = false) -> URL { 27 | var url = base 28 | if url.crt.hasDirectoryPath { 29 | return url 30 | } 31 | 32 | if greedy { 33 | url.appendPathComponent("/") 34 | } 35 | else { 36 | url.deleteLastPathComponent() 37 | } 38 | 39 | return url 40 | } 41 | } 42 | 43 | extension Curator { 44 | struct FileExistResult { 45 | let fileExist: Bool 46 | let isDirectory: Bool 47 | } 48 | } 49 | 50 | extension CuratorExtensionProtocol where Base == URL { 51 | var fileExist: Curator.FileExistResult { 52 | //here we assume url gets from CuratorLocation.standardizedFileURL() throws 53 | let url = base 54 | var isDirectory: ObjCBool = false 55 | let fileExist = CuratorFileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) 56 | return Curator.FileExistResult( 57 | fileExist: fileExist, 58 | isDirectory: isDirectory.boolValue 59 | ) 60 | } 61 | 62 | func createDirectory( 63 | fileExistResult _urlFileExistResult: Curator.FileExistResult? = nil 64 | ) throws { 65 | //here we assume url gets from CuratorLocation.standardizedFileURL() throws 66 | let url = base 67 | let directoryURL = url.crt.getDirectoryURL() 68 | 69 | typealias FileExistResult = Curator.FileExistResult 70 | let directoryExistResult: FileExistResult 71 | if let urlFileExistResult = _urlFileExistResult, 72 | url == directoryURL { 73 | directoryExistResult = urlFileExistResult 74 | } 75 | else { 76 | directoryExistResult = directoryURL.crt.fileExist 77 | } 78 | 79 | if directoryExistResult.fileExist { 80 | if directoryExistResult.isDirectory { 81 | return 82 | } 83 | throw Curator.Error.locationIsFile(directoryURL) 84 | } 85 | 86 | //check parentDir 87 | var parentDirectoryURL = directoryURL.deletingLastPathComponent() 88 | while true { 89 | let parentDirExistResult = parentDirectoryURL.crt.fileExist 90 | if parentDirExistResult.fileExist { 91 | if parentDirExistResult.isDirectory { 92 | break 93 | } 94 | throw Curator.Error.locationIsFile(parentDirectoryURL) 95 | } 96 | 97 | parentDirectoryURL.deleteLastPathComponent() 98 | } 99 | 100 | try CuratorFileManager.createDirectory( 101 | at: directoryURL, 102 | withIntermediateDirectories: true 103 | ) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Curator/CuratorKeepable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol CuratorKeepable { 4 | func asData() throws -> Data 5 | } 6 | -------------------------------------------------------------------------------- /Curator/CuratorKeyLocation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Curator { 4 | public struct KeyLocation { 5 | public let key: String 6 | public let directory: SupportedDirectory 7 | public let isDirectory: Bool 8 | 9 | public init( 10 | key: String, 11 | directory: SupportedDirectory, 12 | isDirectory: Bool = false 13 | ) { 14 | self.key = key 15 | self.directory = directory 16 | self.isDirectory = isDirectory 17 | } 18 | } 19 | } 20 | 21 | extension Curator.KeyLocation: CuratorLocation { 22 | public func asURL() throws -> URL { 23 | return fileURL(of: key, inDirectory: directory, isDirectory: isDirectory) 24 | } 25 | } 26 | 27 | private func fileURL( 28 | of key: String, 29 | inDirectory directory: Curator.SupportedDirectory, 30 | isDirectory: Bool = false 31 | ) -> URL { 32 | return directory.url.appendingPathComponent(key, isDirectory: isDirectory) 33 | } 34 | 35 | extension Curator.KeyLocation: Equatable { 36 | public static func ==(lhs: Curator.KeyLocation, rhs: Curator.KeyLocation) -> Bool { 37 | return lhs.directory == rhs.directory && lhs.key == rhs.key 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Curator/CuratorLocation.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol CuratorLocation { 4 | func asURL() throws -> URL 5 | } 6 | 7 | extension CuratorLocation { 8 | func fileReferenceURL() throws -> URL { 9 | let url = try self.standardizedFileURL() as NSURL 10 | 11 | guard let fileReferenceURL = url.fileReferenceURL() else { 12 | throw Curator.Error.unableToObtainFileReferenceURL(from: self) 13 | } 14 | 15 | return fileReferenceURL as URL 16 | } 17 | 18 | func standardizedFileURL() throws -> URL { 19 | let location = self 20 | let url = try location.asURL() 21 | guard url.isFileURL else { 22 | throw Curator.Error.unableToConvertToFileURL(from: location) 23 | } 24 | 25 | let standardizedURL = url.standardized 26 | let path = standardizedURL.path 27 | guard !path.isEmpty else { 28 | throw Curator.Error.invalidLocation(location) 29 | } 30 | 31 | return standardizedURL 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Curator/CuratorSupportedDirectory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Curator { 4 | public enum SupportedDirectory { 5 | case documents 6 | case caches 7 | case applicationSupport 8 | case tmp 9 | } 10 | } 11 | 12 | public extension Curator.SupportedDirectory { 13 | var url: URL { 14 | switch self { 15 | case .documents: 16 | return documentsDirectory 17 | case .caches: 18 | return cacheDirectory 19 | case .applicationSupport: 20 | return applicationSupportDirectory 21 | case .tmp: 22 | return tmpDirectory 23 | } 24 | } 25 | } 26 | 27 | private func getURLInUserDomain( 28 | for directory: FileManager.SearchPathDirectory 29 | ) -> URL? { 30 | return CuratorFileManager.urls(for: directory, in: .userDomainMask).first 31 | } 32 | 33 | private let documentsDirectory: URL = { 34 | return getURLInUserDomain(for: .documentDirectory)! 35 | }() 36 | 37 | private let cacheDirectory: URL = { 38 | return getURLInUserDomain(for: .cachesDirectory)! 39 | }() 40 | 41 | private let applicationSupportDirectory: URL = { 42 | return getURLInUserDomain(for: .applicationSupportDirectory)! 43 | }() 44 | 45 | private let tmpDirectory: URL = { 46 | return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) 47 | }() 48 | -------------------------------------------------------------------------------- /Curator/CuratorUtility.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let CuratorFileManager = FileManager() 4 | 5 | extension URL: CuratorLocation { 6 | public func asURL() throws -> URL { 7 | return self 8 | } 9 | } 10 | 11 | extension URLComponents: CuratorLocation { 12 | public func asURL() throws -> URL { 13 | guard let url = url else { throw Curator.Error.invalidLocation(self) } 14 | return url 15 | } 16 | } 17 | 18 | extension Data: CuratorKeepable { 19 | public func asData() throws -> Data { 20 | return self 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Curator/Curator_EasyExtension.swift: -------------------------------------------------------------------------------- 1 | extension Curator { 2 | public static func getData(of key: String, in directory: SupportedDirectory) throws -> Data { 3 | let location = KeyLocation(key: key, directory: directory) 4 | return try getData(from: location) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Curator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Yanzhi Wang 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Curator 2 | [![Build Status](https://travis-ci.org/puttin/Curator.svg?branch=master)](https://travis-ci.org/puttin/Curator) 3 | [![Codecov](https://codecov.io/gh/puttin/Curator/branch/master/graph/badge.svg)](https://codecov.io/gh/puttin/Curator) 4 | 5 | Curator is a lightweight key-value file manager written in Swift. 6 | 7 | ## Requirements 8 | * iOS 8.0+ 9 | * Swift 3 10 | 11 | ## Usage 12 | ```swift 13 | import Curator 14 | 15 | data.crt.save(to: "key.data", in: .documents) 16 | 17 | let yourData = try! Curator.getData(of: "key.data", in: .documents) 18 | ``` 19 | 20 | more usage can be found in `Tests` 21 | 22 | ## Installation 23 | 24 | ### CocoaPods 25 | `pod 'Curator', :git => 'https://github.com/puttin/Curator.git'` 26 | 27 | ### Carthage 28 | `github "puttin/Curator"` 29 | 30 | ### Manually 31 | use `git submodule` or anyway you like to add Curator as `Embedded Framework` or vendor sources. 32 | 33 | ## License 34 | Curator is released under the MIT license. See LICENSE for details. 35 | -------------------------------------------------------------------------------- /Tests/CuratorLocationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Curator 3 | 4 | class CuratorLocationTests: XCTestCase { 5 | func testStandardizedFileURL() { 6 | let keyLocation = Curator.KeyLocation(key: "file", directory: .tmp) 7 | let keyLocationStandardizedFileURL = try! keyLocation.standardizedFileURL() 8 | let keyLocationURL = try! keyLocation.asURL() 9 | XCTAssertEqual(keyLocationURL, keyLocationStandardizedFileURL) 10 | 11 | let webURL = URL(string: "https://apple.com")! 12 | do { 13 | let _ = try webURL.standardizedFileURL() 14 | XCTFail() 15 | } catch Curator.Error.unableToConvertToFileURL(_) {} 16 | catch { XCTFail() } 17 | 18 | 19 | let emptyPathURL = URL(string: "file://user:password@localhost")! 20 | do { 21 | let _ = try emptyPathURL.standardizedFileURL() 22 | XCTFail() 23 | } catch Curator.Error.invalidLocation(_) {} 24 | catch { XCTFail() } 25 | } 26 | 27 | func testFileReferenceURL() { 28 | let tmpDirLocation = Curator.KeyLocation(key: ".", directory: .tmp) 29 | let _ = try! tmpDirLocation.fileReferenceURL() 30 | 31 | let notExistLocation = Curator.KeyLocation(key: "file", directory: .tmp) 32 | do { 33 | let _ = try notExistLocation.fileReferenceURL() 34 | XCTFail() 35 | } catch Curator.Error.unableToObtainFileReferenceURL(_) {} 36 | catch { XCTFail() } 37 | } 38 | 39 | func testKeyLocationEquatable() { 40 | typealias Location = Curator.KeyLocation 41 | XCTAssertEqual(Location(key: "foo", directory: .tmp), Location(key: "foo", directory: .tmp)) 42 | XCTAssertNotEqual(Location(key: "foo", directory: .tmp), Location(key: "bar", directory: .tmp)) 43 | XCTAssertNotEqual(Location(key: "foo", directory: .tmp), Location(key: "foo", directory: .caches)) 44 | XCTAssertNotEqual(Location(key: "foo", directory: .tmp), Location(key: "bar", directory: .caches)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/CuratorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Curator 3 | 4 | class CuratorTests: XCTestCase { 5 | typealias Location = Curator.KeyLocation 6 | 7 | let uuidString: String = { 8 | let uuidString = UUID().uuidString 9 | let testDirLocation = Location(key: uuidString, directory: .tmp) 10 | try! Curator.createDirectory(at: testDirLocation) 11 | return uuidString 12 | }() 13 | 14 | func testSave() { 15 | let data = Data(count: 100) 16 | let fileName = UUID().uuidString 17 | let location = Location(key: "\(uuidString)/\(fileName)", directory: .tmp) 18 | try! Curator.save(data, to: location) 19 | 20 | //normally it should allow overwriting 21 | try! Curator.save(data, to: location) 22 | 23 | do { 24 | try Curator.save(data, to: location, options: .withoutOverwriting) 25 | XCTFail() 26 | } catch Curator.Error.locationFileExist(_) {} 27 | catch { XCTFail() } 28 | 29 | do { 30 | try Curator.save(data, to: Curator.SupportedDirectory.tmp.url) 31 | XCTFail() 32 | } catch Curator.Error.locationIsDirectory(_) {} 33 | catch { XCTFail() } 34 | } 35 | 36 | func testGet() { 37 | let data = Data(count: 100) 38 | let fileName = UUID().uuidString 39 | let location = Location(key: "\(uuidString)/\(fileName)", directory: .tmp) 40 | try! Curator.save(data, to: location) 41 | 42 | let dataGetted = try! Curator.getData(from: location) 43 | XCTAssertEqual(data, dataGetted) 44 | 45 | do { 46 | let _ = try Curator.getData(from: Curator.SupportedDirectory.tmp.url) 47 | XCTFail() 48 | } catch Curator.Error.locationIsDirectory(_) {} 49 | catch { XCTFail() } 50 | 51 | do { 52 | let notExistFileLocation = Location(key: "\(uuidString)/notExistFile", directory: .tmp) 53 | let _ = try Curator.getData(from: notExistFileLocation) 54 | XCTFail() 55 | } catch Curator.Error.locationFileNotExist(_) {} 56 | catch { XCTFail() } 57 | } 58 | 59 | func testDelete() { 60 | let data = Data(count: 100) 61 | let fileName = UUID().uuidString 62 | let location = Location(key: "\(uuidString)/\(fileName)", directory: .tmp) 63 | try! Curator.save(data, to: location) 64 | 65 | try! Curator.delete(location: location) 66 | 67 | do { 68 | try Curator.delete(location: location) 69 | XCTFail() 70 | } catch Curator.Error.locationFileNotExist(_) {} 71 | catch { XCTFail() } 72 | 73 | let dirLocation = Location(key: "\(uuidString)/dir", directory: .tmp) 74 | try! Curator.createDirectory(at: dirLocation) 75 | 76 | do { 77 | try Curator.delete(location: dirLocation) 78 | XCTFail() 79 | } catch Curator.Error.locationIsDirectory(_) {} 80 | catch { XCTFail() } 81 | 82 | try! Curator.delete( 83 | location: dirLocation, 84 | allowDirectory: true 85 | ) 86 | } 87 | 88 | func testMove() { 89 | let data = Data(count: 100) 90 | let fileName1 = UUID().uuidString 91 | let location1 = Location(key: "\(uuidString)/\(fileName1)", directory: .tmp) 92 | try! Curator.save(data, to: location1) 93 | 94 | try! Curator.move(from: location1, to: location1) 95 | 96 | let fileName2 = UUID().uuidString 97 | let location2 = Location(key: "\(uuidString)/\(fileName2)", directory: .tmp) 98 | try! Curator.move(from: location1, to: location2) 99 | 100 | do { 101 | let fileName2 = UUID().uuidString 102 | let location2 = Location(key: "\(uuidString)/\(fileName2)", directory: .tmp) 103 | try Curator.move(from: location1, to: location2) 104 | XCTFail() 105 | } catch Curator.Error.locationFileNotExist(_) {} 106 | catch { XCTFail() } 107 | 108 | try! Curator.save(data, to: location1) 109 | 110 | do { 111 | try Curator.move(from: location1, to: location2) 112 | XCTFail() 113 | } catch Curator.Error.locationFileExist(_) {} 114 | catch { XCTFail() } 115 | 116 | let dirLocation = Location(key: "\(uuidString)/dir", directory: .tmp, isDirectory: true) 117 | try! Curator.createDirectory(at: dirLocation) 118 | 119 | let dirLocation2 = Location(key: "\(uuidString)/dir2", directory: .tmp) 120 | 121 | try! Curator.move(from: dirLocation, to: dirLocation2) 122 | 123 | //A test to info me when `mv file dir/` style can be passed 124 | do { 125 | try! Curator.createDirectory(at: dirLocation) 126 | try Curator.move(from: dirLocation, to: dirLocation2) 127 | XCTFail("the mv style!") 128 | } catch {} 129 | } 130 | 131 | func testLink() { 132 | let data = Data(count: 100) 133 | let fileName1 = UUID().uuidString 134 | let location1 = Location(key: "\(uuidString)/\(fileName1)", directory: .tmp) 135 | try! Curator.save(data, to: location1) 136 | 137 | //it should be fine to link to same position 138 | try! Curator.link(from: location1, to: location1) 139 | 140 | //it should be find to link file 141 | let fileName2 = UUID().uuidString 142 | let location2 = Location(key: "\(uuidString)/\(fileName2)", directory: .tmp) 143 | try! Curator.link(from: location1, to: location2) 144 | 145 | //it cannot link to exist file 146 | do { 147 | try Curator.link(from: location1, to: location2) 148 | XCTFail() 149 | } catch Curator.Error.locationFileExist(_) {} 150 | catch { XCTFail() } 151 | 152 | //it cannot link from not exist file 153 | do { 154 | try Curator.delete(location: location1) 155 | try Curator.link(from: location1, to: location2) 156 | XCTFail() 157 | } catch Curator.Error.locationFileNotExist(_) {} 158 | catch { XCTFail() } 159 | 160 | //it should support link dir 161 | let dirLocation = Location(key: "\(uuidString)/dir", directory: .tmp, isDirectory: true) 162 | try! Curator.createDirectory(at: dirLocation) 163 | 164 | let dirLocation2 = Location(key: "\(uuidString)/dir2", directory: .tmp) 165 | try! Curator.link(from: dirLocation, to: dirLocation2) 166 | 167 | //A test to info me when `ln file dir/` style can be passed 168 | do { 169 | try Curator.save(data, to: location1) 170 | try Curator.link(from: location1, to: dirLocation) 171 | XCTFail("the ln style!") 172 | } catch {} 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Curator 3 | 4 | class PerformanceTests: XCTestCase { 5 | typealias Location = Curator.KeyLocation 6 | 7 | let uuidString: String = { 8 | let uuidString = UUID().uuidString 9 | let testDirLocation = Location(key: uuidString, directory: .tmp) 10 | try! Curator.createDirectory(at: testDirLocation) 11 | return uuidString 12 | }() 13 | 14 | func testSave() { 15 | let data = Data(count: 100) 16 | let uuidString = self.uuidString 17 | self.measure { 18 | let fileName = UUID().uuidString 19 | let location = Location(key: "\(uuidString)/\(fileName)", directory: .tmp) 20 | try! Curator.save(data, to: location) 21 | } 22 | } 23 | 24 | func testGet() { 25 | let data = Data(count: 1000) 26 | let location = Location(key: "\(uuidString)/\(UUID().uuidString)", directory: .tmp) 27 | try! Curator.save(data, to: location) 28 | 29 | self.measure { 30 | let _ = try! Curator.getData(from: location, options: .uncached) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/SupportedDirectoryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Curator 3 | 4 | class SupportedDirectoryTests: XCTestCase { 5 | func testURL() { 6 | let documentDirURL = Curator.SupportedDirectory.documents.url 7 | XCTAssertTrue(documentDirURL.absoluteString.hasSuffix("Documents/")) 8 | 9 | let cachesDirURL = Curator.SupportedDirectory.caches.url 10 | XCTAssertTrue(cachesDirURL.absoluteString.hasSuffix("Caches/")) 11 | 12 | let applicationSupportDirURL = Curator.SupportedDirectory.applicationSupport.url 13 | let applicationSupportSuffix = "Application Support/".addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)! 14 | XCTAssertTrue(applicationSupportDirURL.absoluteString.hasSuffix(applicationSupportSuffix)) 15 | 16 | let tmpDirURL = Curator.SupportedDirectory.tmp.url 17 | XCTAssertTrue(tmpDirURL.absoluteString.hasSuffix("tmp/")) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/URLExtensionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Curator 3 | 4 | class URLExtensionTests: XCTestCase { 5 | let fileURL = URL(string: "file:///file")! 6 | let directoryURL = URL(string: "file:///dir/")! 7 | 8 | func testCuratorExtension() { 9 | XCTAssertEqual(fileURL, fileURL.crt.base) 10 | } 11 | 12 | func testHasDirectoryPath() { 13 | XCTAssertFalse(fileURL.crt.hasDirectoryPath) 14 | XCTAssertTrue(directoryURL.crt.hasDirectoryPath) 15 | 16 | let directoryCurrentDirURL = directoryURL.appendingPathComponent(".", isDirectory: true) 17 | let directoryCurrentDirURL2 = directoryURL.appendingPathComponent(".", isDirectory: false) 18 | XCTAssertTrue(directoryCurrentDirURL.crt.hasDirectoryPath) 19 | XCTAssertTrue(directoryCurrentDirURL2.crt.hasDirectoryPath) 20 | 21 | let webURL = URL(string: "https://api.shop.com/category/toy/../phone/./iphone/?json=true")! 22 | XCTAssertTrue(webURL.crt.hasDirectoryPath) 23 | } 24 | 25 | func testGetDirectoryURL() { 26 | XCTAssertEqual(directoryURL, directoryURL.crt.getDirectoryURL(greedy: false)) 27 | XCTAssertEqual(directoryURL, directoryURL.crt.getDirectoryURL(greedy: true)) 28 | 29 | let rootURL = URL(string: "file:///")! 30 | XCTAssertEqual(rootURL, fileURL.crt.getDirectoryURL(greedy: false)) 31 | 32 | let fileDirectoryURL = URL(string: "file:///file/")! 33 | XCTAssertEqual(fileDirectoryURL, fileURL.crt.getDirectoryURL(greedy: true)) 34 | } 35 | 36 | func testFileExist() { 37 | let tmpDirURL = Curator.SupportedDirectory.tmp.url 38 | let tmpDirExistResult = tmpDirURL.crt.fileExist 39 | XCTAssertTrue(tmpDirExistResult.fileExist) 40 | XCTAssertTrue(tmpDirExistResult.isDirectory) 41 | 42 | let notExistFileURL = URL(string: "file:///notExistFile")! 43 | let notExistFileExistResult = notExistFileURL.crt.fileExist 44 | XCTAssertFalse(notExistFileExistResult.fileExist) 45 | XCTAssertFalse(notExistFileExistResult.isDirectory) 46 | 47 | let hostsFileURL = URL(string: "file:///etc/hosts")! 48 | let hostsFileExistResult = hostsFileURL.crt.fileExist 49 | XCTAssertTrue(hostsFileExistResult.fileExist) 50 | XCTAssertFalse(hostsFileExistResult.isDirectory) 51 | } 52 | 53 | func testCreateDirectory() { 54 | //create testDir first 55 | let uuidString = UUID().uuidString 56 | typealias Location = Curator.KeyLocation 57 | let testDirLocation = Location(key: uuidString, directory: .tmp) 58 | let testDirURL = try! testDirLocation.standardizedFileURL().crt.getDirectoryURL(greedy: true) 59 | XCTAssertFalse(testDirURL.crt.fileExist.fileExist) 60 | try! testDirURL.crt.createDirectory() 61 | XCTAssertTrue(testDirURL.crt.fileExist.isDirectory) 62 | 63 | //save a file for test 64 | let data = Data(count: 1) 65 | let fileLocation = Location(key: "\(uuidString)/file", directory: .tmp) 66 | try! Curator.save(data, to: fileLocation) 67 | let fileLocationURL = try! fileLocation.standardizedFileURL() 68 | XCTAssertTrue(fileLocationURL.crt.fileExist.fileExist) 69 | 70 | //check locationIsFile 71 | let fileLocationFakeDirURL = fileLocationURL.crt.getDirectoryURL(greedy: true) 72 | do { 73 | try fileLocationFakeDirURL.crt.createDirectory() 74 | XCTFail() 75 | } catch Curator.Error.locationIsFile(let location) { 76 | let url = location as! URL 77 | XCTAssertEqual(url, fileLocationFakeDirURL) 78 | } catch { XCTFail() } 79 | 80 | //check parent locationIsFile 81 | let fileLocation2 = Location(key: "\(uuidString)/file/dir/dir/foo", directory: .tmp) 82 | let fileLocation2URL = try! fileLocation2.standardizedFileURL() 83 | XCTAssertFalse(fileLocation2URL.crt.fileExist.fileExist) 84 | do { 85 | try fileLocation2URL.crt.createDirectory() 86 | XCTFail() 87 | } catch Curator.Error.locationIsFile(let location) { 88 | let url = location as! URL 89 | XCTAssertEqual(url, fileLocationFakeDirURL) 90 | } catch { XCTFail() } 91 | 92 | //reuse fileExistResult 93 | let testDirExistResult = testDirURL.crt.fileExist 94 | try! testDirURL.crt.createDirectory(fileExistResult: testDirExistResult) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tests/UtilityTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Curator 3 | 4 | class UtilityTests: XCTestCase { 5 | func testFileManager() { 6 | let fileManager = CuratorFileManager 7 | let defaultFileManager = FileManager.default 8 | XCTAssertNotEqual(fileManager, defaultFileManager) 9 | } 10 | 11 | func testURLComponents_CuratorLocation() { 12 | var goodURLComponents = URLComponents() 13 | goodURLComponents.scheme = "file" 14 | goodURLComponents.host = "" 15 | goodURLComponents.path = "/file" 16 | let _ = try! (goodURLComponents as CuratorLocation).asURL() 17 | 18 | var badURLComponents = URLComponents() 19 | badURLComponents.path = "//" 20 | do { 21 | let _ = try (badURLComponents as CuratorLocation).asURL() 22 | XCTFail() 23 | } catch Curator.Error.invalidLocation(_) {} 24 | catch { XCTFail() } 25 | } 26 | 27 | func testKeepableExtension() { 28 | let data = Data(count: 100) 29 | try! data.crt.save(to: "\(UUID().uuidString)", in: .tmp) 30 | } 31 | 32 | func testCuratorEasyGetData() { 33 | let data = Data(count: 100) 34 | let uuidString = UUID().uuidString 35 | 36 | try! data.crt.save(to: uuidString, in: .tmp) 37 | let _ = try! Curator.getData(of: uuidString, in: .tmp) 38 | } 39 | } 40 | --------------------------------------------------------------------------------