├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── -en-bug-report-.md
│ ├── -en-feature-request-.md
│ └── -en-q-a-.md
├── issue_template.md
└── pull_request_template.md
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── .travis.yml
├── BuildScripts
└── lint.swift
├── ClaretCache.podspec
├── ClaretCacheDemo.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
└── xcshareddata
│ └── xcschemes
│ ├── ClaretCacheDemo.xcscheme
│ └── ClaretCacheDemoTests.xcscheme
├── ClaretCacheDemo.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ └── Package.resolved
├── ClaretCacheDemo
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
├── ClaretCacheDemoTests
├── ClaretCacheDemoTests.swift
└── Info.plist
├── Design
└── banner.png
├── LICENSE
├── Package.swift
├── Podfile
├── Podfile.lock
├── Pods
├── Manifest.lock
├── Pods.xcodeproj
│ └── project.pbxproj
├── SwiftLint
│ ├── LICENSE
│ └── swiftlint
└── Target Support Files
│ ├── Pods-ClaretCacheDemo
│ ├── Pods-ClaretCacheDemo-Info.plist
│ ├── Pods-ClaretCacheDemo-acknowledgements.markdown
│ ├── Pods-ClaretCacheDemo-acknowledgements.plist
│ ├── Pods-ClaretCacheDemo-dummy.m
│ ├── Pods-ClaretCacheDemo-umbrella.h
│ ├── Pods-ClaretCacheDemo.debug.xcconfig
│ ├── Pods-ClaretCacheDemo.modulemap
│ └── Pods-ClaretCacheDemo.release.xcconfig
│ └── SwiftLint
│ └── SwiftLint.xcconfig
├── README.md
├── Sources
└── ClaretCache
│ ├── ClaretCache.swift
│ ├── KVStorage.swift
│ └── MemoryCache.swift
├── Swift_Style_Guide.md
├── Tests
├── ClaretCacheTests
│ ├── ClaretCacheTests.swift
│ └── XCTestManifests.swift
└── LinuxMain.swift
└── configs
└── .swiftlint.yml
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Swift gitattributes
2 | *.pbxproj merge=union
3 | *.swift text diff=swift
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/-en-bug-report-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "[EN]Bug report:"
3 | about: "[CN]bug反馈:"
4 | title: "[CN]bug反馈."
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | 
11 |
12 | ----------
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ----------
33 |
34 |
35 | ## Base Info for this issue
36 |
37 |
38 |
39 |
40 |
41 | 1. Version:Latest Version as [here](https://github.com/iteatimeteam/ClaretCache/blob/master/ClaretCache.podspec)
42 | 2. main language of App :Objective-C/Swift
43 | 4. iOS System Version:iOS12
44 | 5. Prototype(是否是真机):YES
45 | 6. Issue Type:Crash、Bug、Enhancement(希望能支持一个新需求)、Q-A
46 |
47 | ## 1. How to reproduce the problem.
48 |
49 |
50 | ## 2. Please help me in this way.
51 |
52 |
53 | ## 3. Here is a Demo and screenshots.
54 |
55 |
56 |
57 | ## 4. Here is my Debug log
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | ----------
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/-en-feature-request-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "[EN]Feature request:"
3 | about: "[CN]希望添加新功能:"
4 | title: "[CN]希望添加新功能:"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | --------------------------------------------
11 | 
12 |
13 | ----------
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ----------
34 | ----------
35 |
36 |
37 |
38 | **Is your feature request related to a problem? Please describe.**
39 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
40 |
41 | **Describe the solution you'd like**
42 | A clear and concise description of what you want to happen.
43 |
44 | **Describe alternatives you've considered**
45 | A clear and concise description of any alternative solutions or features you've considered.
46 |
47 | **Additional context**
48 | Add any other context or screenshots about the feature request here.
49 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/-en-q-a-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "[EN]Q-A:"
3 | about: "[CN]Q-A问题询问,使用方法或者无法确认是否为bug,可以选择该模版。"
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | --------------------------------------------
11 | 
12 |
13 | ----------
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ----------
34 |
35 | ## My issue:
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 
5 |
6 |
9 |
10 | ----------
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ----------
31 |
32 |
33 | ## Base Info for this issue
34 |
35 |
36 |
37 |
38 |
39 | 1. Version:Latest Version as [here](https://github.com/iteatimeteam/ClaretCache/blob/master/ClaretCache.podspec)
40 | 2. main language of App :Objective-C/Swift
41 | 4. iOS System Version:iOS12
42 | 5. Prototype(是否是真机):YES
43 | 6. Issue Type:Crash、Bug、Enhancement(希望能支持一个新需求)、Q-A
44 |
45 | ## 1. How to reproduce the problem.
46 |
47 |
48 | ## 2. Please help me in this way.
49 |
50 |
51 | ## 3. Here is a Demo and screenshots.
52 |
53 |
54 |
55 | ## 4. Here is my Debug log
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | ----------
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | ## CheckList
5 |
6 |
7 | Thanks for considering to this repository. Before you submit your issue, please confirm these boxes are checked.
8 |
9 | - [ ] I have read [《[EN]Style guide for Swift repo [CN]Swift项目代码规范、规约选型》](https://github.com/iteatimeteam/ClaretCache/issues/3).
10 | - [ ] 我遵守一下 Merge 规则:邀请群组中任意一人进行review,即可合并。让 reviewer 在 PR 所在页面写下review意见表示通过,即可合并。最简单写一句 LGTM 也可以。不按照本规则执行,自己 PR 自己 Merge 并引入问题,会被收回 Merge 权限。
11 |
12 |
13 | ## My issue:
14 | -------
15 |
16 |
17 |
18 | ## What I have done:
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Created by https://www.gitignore.io/api/swift
4 | # Edit at https://www.gitignore.io/?templates=swift
5 |
6 | ### Swift ###
7 | # Xcode
8 | #
9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
10 |
11 | ## Build generated
12 | build/
13 | DerivedData/
14 |
15 | ## Various settings
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 | xcuserdata/
25 |
26 | ## Other
27 | *.moved-aside
28 | *.xccheckout
29 | *.xcscmblueprint
30 |
31 | ## Obj-C/Swift specific
32 | *.hmap
33 | *.ipa
34 | *.dSYM.zip
35 | *.dSYM
36 |
37 | ## Playgrounds
38 | timeline.xctimeline
39 | playground.xcworkspace
40 |
41 | # Swift Package Manager
42 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
43 | # Packages/
44 | # Package.pins
45 | # Package.resolved
46 | .build/
47 |
48 | # CocoaPods
49 | # We recommend against adding the Pods directory to your .gitignore. However
50 | # you should judge for yourself, the pros and cons are mentioned at:
51 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
52 | # Pods/
53 | # Add this line if you want to avoid checking in source code from the Xcode workspace
54 | # *.xcworkspace
55 |
56 | # Carthage
57 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
58 | # Carthage/Checkouts
59 |
60 | Carthage/Build
61 |
62 | # Accio dependency management
63 | Dependencies/
64 | .accio/
65 |
66 | # fastlane
67 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
68 | # screenshots whenever they are needed.
69 | # For more information about the recommended setup visit:
70 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
71 |
72 | fastlane/report.xml
73 | fastlane/Preview.html
74 | fastlane/screenshots/**/*.png
75 | fastlane/test_output
76 |
77 | # Code Injection
78 | # After new code Injection tools there's a generated folder /iOSInjectionProject
79 | # https://github.com/johnno1962/injectionforxcode
80 |
81 | iOSInjectionProject/
82 |
83 | # End of https://www.gitignore.io/api/swift
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode11
2 | language: swift
3 |
4 | env:
5 | - PROJECT_NAME=ClaretCacheDemo
6 |
7 | jobs:
8 | include:
9 | # - stage: Lint
10 | install:
11 | - wget --output-document /tmp/SwiftLint.pkg https://github.com/realm/SwiftLint/releases/download/0.33.1/SwiftLint.pkg && sudo installer -pkg /tmp/SwiftLint.pkg -target /
12 | script:
13 | - swiftlint --config configs/.swiftlint.yml
14 | - swift test
15 | - swift build
16 | - xcodebuild -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone XR,OS=12.1' build
17 | # stages:
18 | # - Lint
19 | # - Test
20 |
--------------------------------------------------------------------------------
/BuildScripts/lint.swift:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env xcrun --sdk macosx swift
2 |
3 | import Foundation
4 |
5 | func run(targetPath: String) {
6 | let relativePath = CommandLine.arguments[0] as NSString
7 | print(relativePath)
8 | let configFilePath = relativePath.deletingLastPathComponent + "/../configs/.swiftlint.yml"
9 | let configFileUrl = URL(fileURLWithPath: configFilePath)
10 | let configPath = configFileUrl.path
11 | let launchPath = relativePath.deletingLastPathComponent.components(separatedBy: "/..").first! + "/../Pods/SwiftLint/swiftlint"
12 |
13 | let process = Process()
14 | process.launchPath = launchPath
15 | process.arguments = ["lint", "--config", configPath, "--path", targetPath]
16 | process.launch()
17 | process.waitUntilExit()
18 |
19 | let status = process.terminationStatus
20 | exit(status)
21 | }
22 |
23 | run(targetPath: CommandLine.arguments[1])
24 |
--------------------------------------------------------------------------------
/ClaretCache.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'ClaretCache'
3 | s.summary = 'High performance cache framework for iOS.'
4 | s.version = '0.0.1'
5 | s.license = { :type => 'MIT', :file => 'LICENSE' }
6 | s.authors = { 'iTeaTime(技术清谈)' => 'luohanchenyilong@163.com' }
7 | s.social_media_url = 'https://github.com/iteatimeteam/ClaretCache'
8 | s.homepage = 'https://github.com/iteatimeteam/ClaretCache'
9 | s.source = { :git => 'https://github.com/iteatimeteam/ClaretCache.git', :tag => s.version.to_s }
10 |
11 | s.platform = :ios, '10.0'
12 | s.ios.deployment_target = '10.0'
13 |
14 | s.swift_version = '5.0'
15 |
16 | s.source_files = 'Sources/ClaretCache/*.swift'
17 |
18 | s.libraries = 'sqlite3'
19 | s.frameworks = 'UIKit', 'CoreFoundation', 'QuartzCore'
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 01C75DEC22E0723E00C7D03F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C75DEB22E0723E00C7D03F /* AppDelegate.swift */; };
11 | 01C75DEE22E0723E00C7D03F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C75DED22E0723E00C7D03F /* ViewController.swift */; };
12 | 01C75DF122E0723E00C7D03F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01C75DEF22E0723E00C7D03F /* Main.storyboard */; };
13 | 01C75DF322E0724000C7D03F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C75DF222E0724000C7D03F /* Assets.xcassets */; };
14 | 01C75DF622E0724000C7D03F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01C75DF422E0724000C7D03F /* LaunchScreen.storyboard */; };
15 | 01FA686D22E719DB008E24FC /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA686B22E719DB008E24FC /* MemoryCache.swift */; };
16 | 01FA686E22E719DB008E24FC /* ClaretCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA686C22E719DB008E24FC /* ClaretCache.swift */; };
17 | 8E077CCCBC628B6EF406E97A /* Pods_ClaretCacheDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BF5D3C4BBDD1FACD276DAA1 /* Pods_ClaretCacheDemo.framework */; };
18 | C30EB06522ED8E8A0064ED79 /* KVStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C30EB06422ED8E8A0064ED79 /* KVStorage.swift */; };
19 | C3BC07DC22EF369600345659 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C3BC07DB22EF369600345659 /* libsqlite3.tbd */; };
20 | F8C53E7722EB9EBC00B53664 /* ClaretCacheDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C53E7622EB9EBC00B53664 /* ClaretCacheDemoTests.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXContainerItemProxy section */
24 | F8C53E7922EB9EBC00B53664 /* PBXContainerItemProxy */ = {
25 | isa = PBXContainerItemProxy;
26 | containerPortal = 01C75DE022E0723E00C7D03F /* Project object */;
27 | proxyType = 1;
28 | remoteGlobalIDString = 01C75DE722E0723E00C7D03F;
29 | remoteInfo = ClaretCacheDemo;
30 | };
31 | /* End PBXContainerItemProxy section */
32 |
33 | /* Begin PBXFileReference section */
34 | 01C75DE822E0723E00C7D03F /* ClaretCacheDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClaretCacheDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 01C75DEB22E0723E00C7D03F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
36 | 01C75DED22E0723E00C7D03F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
37 | 01C75DF022E0723E00C7D03F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
38 | 01C75DF222E0724000C7D03F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
39 | 01C75DF522E0724000C7D03F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
40 | 01C75DF722E0724000C7D03F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
41 | 01FA686B22E719DB008E24FC /* MemoryCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = ""; };
42 | 01FA686C22E719DB008E24FC /* ClaretCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClaretCache.swift; sourceTree = ""; };
43 | 1BF5D3C4BBDD1FACD276DAA1 /* Pods_ClaretCacheDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ClaretCacheDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 71DE9F8F64DFC38FD8ABCF96 /* Pods-ClaretCacheDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClaretCacheDemo.release.xcconfig"; path = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.release.xcconfig"; sourceTree = ""; };
45 | 96892E93F01C8D0A4FEC2EC1 /* Pods-ClaretCacheDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClaretCacheDemo.debug.xcconfig"; path = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.debug.xcconfig"; sourceTree = ""; };
46 | C30EB06422ED8E8A0064ED79 /* KVStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KVStorage.swift; sourceTree = ""; };
47 | C3BC07DB22EF369600345659 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
48 | F8C53E7422EB9EBC00B53664 /* ClaretCacheDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClaretCacheDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
49 | F8C53E7622EB9EBC00B53664 /* ClaretCacheDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaretCacheDemoTests.swift; sourceTree = ""; };
50 | F8C53E7822EB9EBC00B53664 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | /* End PBXFileReference section */
52 |
53 | /* Begin PBXFrameworksBuildPhase section */
54 | 01C75DE522E0723E00C7D03F /* Frameworks */ = {
55 | isa = PBXFrameworksBuildPhase;
56 | buildActionMask = 2147483647;
57 | files = (
58 | C3BC07DC22EF369600345659 /* libsqlite3.tbd in Frameworks */,
59 | 8E077CCCBC628B6EF406E97A /* Pods_ClaretCacheDemo.framework in Frameworks */,
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | F8C53E7122EB9EBC00B53664 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | );
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXFrameworksBuildPhase section */
71 |
72 | /* Begin PBXGroup section */
73 | 01C75DDF22E0723E00C7D03F = {
74 | isa = PBXGroup;
75 | children = (
76 | 01C75DEA22E0723E00C7D03F /* ClaretCacheDemo */,
77 | F8C53E7522EB9EBC00B53664 /* ClaretCacheDemoTests */,
78 | 01C75DE922E0723E00C7D03F /* Products */,
79 | 79D8C1A4BEAF0AE66614A083 /* Pods */,
80 | 53C8A8FFD2D1D8A4F837C496 /* Frameworks */,
81 | );
82 | sourceTree = "";
83 | };
84 | 01C75DE922E0723E00C7D03F /* Products */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 01C75DE822E0723E00C7D03F /* ClaretCacheDemo.app */,
88 | F8C53E7422EB9EBC00B53664 /* ClaretCacheDemoTests.xctest */,
89 | );
90 | name = Products;
91 | sourceTree = "";
92 | };
93 | 01C75DEA22E0723E00C7D03F /* ClaretCacheDemo */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 01C75DEB22E0723E00C7D03F /* AppDelegate.swift */,
97 | 01C75DED22E0723E00C7D03F /* ViewController.swift */,
98 | 01C75DEF22E0723E00C7D03F /* Main.storyboard */,
99 | 01FA686922E719DB008E24FC /* Sources */,
100 | 01C75DF222E0724000C7D03F /* Assets.xcassets */,
101 | 01C75DF422E0724000C7D03F /* LaunchScreen.storyboard */,
102 | 01C75DF722E0724000C7D03F /* Info.plist */,
103 | );
104 | path = ClaretCacheDemo;
105 | sourceTree = "";
106 | };
107 | 01FA686922E719DB008E24FC /* Sources */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 01FA686A22E719DB008E24FC /* ClaretCache */,
111 | );
112 | path = Sources;
113 | sourceTree = SOURCE_ROOT;
114 | };
115 | 01FA686A22E719DB008E24FC /* ClaretCache */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 01FA686B22E719DB008E24FC /* MemoryCache.swift */,
119 | 01FA686C22E719DB008E24FC /* ClaretCache.swift */,
120 | C30EB06422ED8E8A0064ED79 /* KVStorage.swift */,
121 | );
122 | path = ClaretCache;
123 | sourceTree = "";
124 | };
125 | 53C8A8FFD2D1D8A4F837C496 /* Frameworks */ = {
126 | isa = PBXGroup;
127 | children = (
128 | C3BC07DB22EF369600345659 /* libsqlite3.tbd */,
129 | 1BF5D3C4BBDD1FACD276DAA1 /* Pods_ClaretCacheDemo.framework */,
130 | );
131 | name = Frameworks;
132 | sourceTree = "";
133 | };
134 | 79D8C1A4BEAF0AE66614A083 /* Pods */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 96892E93F01C8D0A4FEC2EC1 /* Pods-ClaretCacheDemo.debug.xcconfig */,
138 | 71DE9F8F64DFC38FD8ABCF96 /* Pods-ClaretCacheDemo.release.xcconfig */,
139 | );
140 | path = Pods;
141 | sourceTree = "";
142 | };
143 | F8C53E7522EB9EBC00B53664 /* ClaretCacheDemoTests */ = {
144 | isa = PBXGroup;
145 | children = (
146 | F8C53E7622EB9EBC00B53664 /* ClaretCacheDemoTests.swift */,
147 | F8C53E7822EB9EBC00B53664 /* Info.plist */,
148 | );
149 | path = ClaretCacheDemoTests;
150 | sourceTree = "";
151 | };
152 | /* End PBXGroup section */
153 |
154 | /* Begin PBXNativeTarget section */
155 | 01C75DE722E0723E00C7D03F /* ClaretCacheDemo */ = {
156 | isa = PBXNativeTarget;
157 | buildConfigurationList = 01C75DFA22E0724000C7D03F /* Build configuration list for PBXNativeTarget "ClaretCacheDemo" */;
158 | buildPhases = (
159 | F3E5A49D06B895518A09FF37 /* [CP] Check Pods Manifest.lock */,
160 | 01C75DE422E0723E00C7D03F /* Sources */,
161 | 01C75DE522E0723E00C7D03F /* Frameworks */,
162 | 01C75DE622E0723E00C7D03F /* Resources */,
163 | 01FA686F22E719F3008E24FC /* Lint Script */,
164 | );
165 | buildRules = (
166 | );
167 | dependencies = (
168 | );
169 | name = ClaretCacheDemo;
170 | packageProductDependencies = (
171 | );
172 | productName = ClaretCacheDemo;
173 | productReference = 01C75DE822E0723E00C7D03F /* ClaretCacheDemo.app */;
174 | productType = "com.apple.product-type.application";
175 | };
176 | F8C53E7322EB9EBC00B53664 /* ClaretCacheDemoTests */ = {
177 | isa = PBXNativeTarget;
178 | buildConfigurationList = F8C53E7D22EB9EBC00B53664 /* Build configuration list for PBXNativeTarget "ClaretCacheDemoTests" */;
179 | buildPhases = (
180 | F8C53E7022EB9EBC00B53664 /* Sources */,
181 | F8C53E7122EB9EBC00B53664 /* Frameworks */,
182 | F8C53E7222EB9EBC00B53664 /* Resources */,
183 | F8C53E7E22EB9ECC00B53664 /* Lint Script */,
184 | );
185 | buildRules = (
186 | );
187 | dependencies = (
188 | F8C53E7A22EB9EBC00B53664 /* PBXTargetDependency */,
189 | );
190 | name = ClaretCacheDemoTests;
191 | productName = ClaretCacheDemoTests;
192 | productReference = F8C53E7422EB9EBC00B53664 /* ClaretCacheDemoTests.xctest */;
193 | productType = "com.apple.product-type.bundle.unit-test";
194 | };
195 | /* End PBXNativeTarget section */
196 |
197 | /* Begin PBXProject section */
198 | 01C75DE022E0723E00C7D03F /* Project object */ = {
199 | isa = PBXProject;
200 | attributes = {
201 | LastSwiftUpdateCheck = 1100;
202 | LastUpgradeCheck = 1020;
203 | ORGANIZATIONNAME = com.ClaretCache;
204 | TargetAttributes = {
205 | 01C75DE722E0723E00C7D03F = {
206 | CreatedOnToolsVersion = 10.2.1;
207 | };
208 | F8C53E7322EB9EBC00B53664 = {
209 | CreatedOnToolsVersion = 11.0;
210 | TestTargetID = 01C75DE722E0723E00C7D03F;
211 | };
212 | };
213 | };
214 | buildConfigurationList = 01C75DE322E0723E00C7D03F /* Build configuration list for PBXProject "ClaretCacheDemo" */;
215 | compatibilityVersion = "Xcode 9.3";
216 | developmentRegion = en;
217 | hasScannedForEncodings = 0;
218 | knownRegions = (
219 | en,
220 | Base,
221 | );
222 | mainGroup = 01C75DDF22E0723E00C7D03F;
223 | packageReferences = (
224 | );
225 | productRefGroup = 01C75DE922E0723E00C7D03F /* Products */;
226 | projectDirPath = "";
227 | projectRoot = "";
228 | targets = (
229 | 01C75DE722E0723E00C7D03F /* ClaretCacheDemo */,
230 | F8C53E7322EB9EBC00B53664 /* ClaretCacheDemoTests */,
231 | );
232 | };
233 | /* End PBXProject section */
234 |
235 | /* Begin PBXResourcesBuildPhase section */
236 | 01C75DE622E0723E00C7D03F /* Resources */ = {
237 | isa = PBXResourcesBuildPhase;
238 | buildActionMask = 2147483647;
239 | files = (
240 | 01C75DF622E0724000C7D03F /* LaunchScreen.storyboard in Resources */,
241 | 01C75DF322E0724000C7D03F /* Assets.xcassets in Resources */,
242 | 01C75DF122E0723E00C7D03F /* Main.storyboard in Resources */,
243 | );
244 | runOnlyForDeploymentPostprocessing = 0;
245 | };
246 | F8C53E7222EB9EBC00B53664 /* Resources */ = {
247 | isa = PBXResourcesBuildPhase;
248 | buildActionMask = 2147483647;
249 | files = (
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXResourcesBuildPhase section */
254 |
255 | /* Begin PBXShellScriptBuildPhase section */
256 | 01FA686F22E719F3008E24FC /* Lint Script */ = {
257 | isa = PBXShellScriptBuildPhase;
258 | buildActionMask = 12;
259 | files = (
260 | );
261 | inputFileListPaths = (
262 | );
263 | inputPaths = (
264 | );
265 | name = "Lint Script";
266 | outputFileListPaths = (
267 | );
268 | outputPaths = (
269 | );
270 | runOnlyForDeploymentPostprocessing = 0;
271 | shellPath = /bin/sh;
272 | shellScript = "\"${SRCROOT}/BuildScripts/lint.swift\" \"${SRCROOT}\"\n";
273 | };
274 | F3E5A49D06B895518A09FF37 /* [CP] Check Pods Manifest.lock */ = {
275 | isa = PBXShellScriptBuildPhase;
276 | buildActionMask = 2147483647;
277 | files = (
278 | );
279 | inputFileListPaths = (
280 | );
281 | inputPaths = (
282 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
283 | "${PODS_ROOT}/Manifest.lock",
284 | );
285 | name = "[CP] Check Pods Manifest.lock";
286 | outputFileListPaths = (
287 | );
288 | outputPaths = (
289 | "$(DERIVED_FILE_DIR)/Pods-ClaretCacheDemo-checkManifestLockResult.txt",
290 | );
291 | runOnlyForDeploymentPostprocessing = 0;
292 | shellPath = /bin/sh;
293 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
294 | showEnvVarsInLog = 0;
295 | };
296 | F8C53E7E22EB9ECC00B53664 /* Lint Script */ = {
297 | isa = PBXShellScriptBuildPhase;
298 | buildActionMask = 2147483647;
299 | files = (
300 | );
301 | inputFileListPaths = (
302 | );
303 | inputPaths = (
304 | );
305 | name = "Lint Script";
306 | outputFileListPaths = (
307 | );
308 | outputPaths = (
309 | );
310 | runOnlyForDeploymentPostprocessing = 0;
311 | shellPath = /bin/sh;
312 | shellScript = "\"${SRCROOT}/BuildScripts/lint.swift\" \"${SRCROOT}\"\n";
313 | };
314 | /* End PBXShellScriptBuildPhase section */
315 |
316 | /* Begin PBXSourcesBuildPhase section */
317 | 01C75DE422E0723E00C7D03F /* Sources */ = {
318 | isa = PBXSourcesBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | 01FA686D22E719DB008E24FC /* MemoryCache.swift in Sources */,
322 | 01C75DEE22E0723E00C7D03F /* ViewController.swift in Sources */,
323 | C30EB06522ED8E8A0064ED79 /* KVStorage.swift in Sources */,
324 | 01C75DEC22E0723E00C7D03F /* AppDelegate.swift in Sources */,
325 | 01FA686E22E719DB008E24FC /* ClaretCache.swift in Sources */,
326 | );
327 | runOnlyForDeploymentPostprocessing = 0;
328 | };
329 | F8C53E7022EB9EBC00B53664 /* Sources */ = {
330 | isa = PBXSourcesBuildPhase;
331 | buildActionMask = 2147483647;
332 | files = (
333 | F8C53E7722EB9EBC00B53664 /* ClaretCacheDemoTests.swift in Sources */,
334 | );
335 | runOnlyForDeploymentPostprocessing = 0;
336 | };
337 | /* End PBXSourcesBuildPhase section */
338 |
339 | /* Begin PBXTargetDependency section */
340 | F8C53E7A22EB9EBC00B53664 /* PBXTargetDependency */ = {
341 | isa = PBXTargetDependency;
342 | target = 01C75DE722E0723E00C7D03F /* ClaretCacheDemo */;
343 | targetProxy = F8C53E7922EB9EBC00B53664 /* PBXContainerItemProxy */;
344 | };
345 | /* End PBXTargetDependency section */
346 |
347 | /* Begin PBXVariantGroup section */
348 | 01C75DEF22E0723E00C7D03F /* Main.storyboard */ = {
349 | isa = PBXVariantGroup;
350 | children = (
351 | 01C75DF022E0723E00C7D03F /* Base */,
352 | );
353 | name = Main.storyboard;
354 | sourceTree = "";
355 | };
356 | 01C75DF422E0724000C7D03F /* LaunchScreen.storyboard */ = {
357 | isa = PBXVariantGroup;
358 | children = (
359 | 01C75DF522E0724000C7D03F /* Base */,
360 | );
361 | name = LaunchScreen.storyboard;
362 | sourceTree = "";
363 | };
364 | /* End PBXVariantGroup section */
365 |
366 | /* Begin XCBuildConfiguration section */
367 | 01C75DF822E0724000C7D03F /* Debug */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | ALWAYS_SEARCH_USER_PATHS = NO;
371 | CLANG_ANALYZER_NONNULL = YES;
372 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
373 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
374 | CLANG_CXX_LIBRARY = "libc++";
375 | CLANG_ENABLE_MODULES = YES;
376 | CLANG_ENABLE_OBJC_ARC = YES;
377 | CLANG_ENABLE_OBJC_WEAK = YES;
378 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
379 | CLANG_WARN_BOOL_CONVERSION = YES;
380 | CLANG_WARN_COMMA = YES;
381 | CLANG_WARN_CONSTANT_CONVERSION = YES;
382 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
384 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
385 | CLANG_WARN_EMPTY_BODY = YES;
386 | CLANG_WARN_ENUM_CONVERSION = YES;
387 | CLANG_WARN_INFINITE_RECURSION = YES;
388 | CLANG_WARN_INT_CONVERSION = YES;
389 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
390 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
391 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
393 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
394 | CLANG_WARN_STRICT_PROTOTYPES = YES;
395 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
396 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
397 | CLANG_WARN_UNREACHABLE_CODE = YES;
398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
399 | CODE_SIGN_IDENTITY = "iPhone Developer";
400 | COPY_PHASE_STRIP = NO;
401 | DEBUG_INFORMATION_FORMAT = dwarf;
402 | ENABLE_STRICT_OBJC_MSGSEND = YES;
403 | ENABLE_TESTABILITY = YES;
404 | GCC_C_LANGUAGE_STANDARD = gnu11;
405 | GCC_DYNAMIC_NO_PIC = NO;
406 | GCC_NO_COMMON_BLOCKS = YES;
407 | GCC_OPTIMIZATION_LEVEL = 0;
408 | GCC_PREPROCESSOR_DEFINITIONS = (
409 | "DEBUG=1",
410 | "$(inherited)",
411 | );
412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
414 | GCC_WARN_UNDECLARED_SELECTOR = YES;
415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
416 | GCC_WARN_UNUSED_FUNCTION = YES;
417 | GCC_WARN_UNUSED_VARIABLE = YES;
418 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
419 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
420 | MTL_FAST_MATH = YES;
421 | ONLY_ACTIVE_ARCH = YES;
422 | SDKROOT = iphoneos;
423 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
424 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
425 | };
426 | name = Debug;
427 | };
428 | 01C75DF922E0724000C7D03F /* Release */ = {
429 | isa = XCBuildConfiguration;
430 | buildSettings = {
431 | ALWAYS_SEARCH_USER_PATHS = NO;
432 | CLANG_ANALYZER_NONNULL = YES;
433 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
435 | CLANG_CXX_LIBRARY = "libc++";
436 | CLANG_ENABLE_MODULES = YES;
437 | CLANG_ENABLE_OBJC_ARC = YES;
438 | CLANG_ENABLE_OBJC_WEAK = YES;
439 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
440 | CLANG_WARN_BOOL_CONVERSION = YES;
441 | CLANG_WARN_COMMA = YES;
442 | CLANG_WARN_CONSTANT_CONVERSION = YES;
443 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
445 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
446 | CLANG_WARN_EMPTY_BODY = YES;
447 | CLANG_WARN_ENUM_CONVERSION = YES;
448 | CLANG_WARN_INFINITE_RECURSION = YES;
449 | CLANG_WARN_INT_CONVERSION = YES;
450 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
451 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
452 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
453 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
454 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
455 | CLANG_WARN_STRICT_PROTOTYPES = YES;
456 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
457 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
458 | CLANG_WARN_UNREACHABLE_CODE = YES;
459 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
460 | CODE_SIGN_IDENTITY = "iPhone Developer";
461 | COPY_PHASE_STRIP = NO;
462 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
463 | ENABLE_NS_ASSERTIONS = NO;
464 | ENABLE_STRICT_OBJC_MSGSEND = YES;
465 | GCC_C_LANGUAGE_STANDARD = gnu11;
466 | GCC_NO_COMMON_BLOCKS = YES;
467 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
468 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
469 | GCC_WARN_UNDECLARED_SELECTOR = YES;
470 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
471 | GCC_WARN_UNUSED_FUNCTION = YES;
472 | GCC_WARN_UNUSED_VARIABLE = YES;
473 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
474 | MTL_ENABLE_DEBUG_INFO = NO;
475 | MTL_FAST_MATH = YES;
476 | SDKROOT = iphoneos;
477 | SWIFT_COMPILATION_MODE = wholemodule;
478 | SWIFT_OPTIMIZATION_LEVEL = "-O";
479 | VALIDATE_PRODUCT = YES;
480 | };
481 | name = Release;
482 | };
483 | 01C75DFB22E0724000C7D03F /* Debug */ = {
484 | isa = XCBuildConfiguration;
485 | baseConfigurationReference = 96892E93F01C8D0A4FEC2EC1 /* Pods-ClaretCacheDemo.debug.xcconfig */;
486 | buildSettings = {
487 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
488 | CODE_SIGN_STYLE = Automatic;
489 | DEVELOPMENT_TEAM = 3289Y6BF27;
490 | INFOPLIST_FILE = ClaretCacheDemo/Info.plist;
491 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
492 | LD_RUNPATH_SEARCH_PATHS = (
493 | "$(inherited)",
494 | "@executable_path/Frameworks",
495 | );
496 | PRODUCT_BUNDLE_IDENTIFIER = "com.ClaretCache.-222";
497 | PRODUCT_NAME = "$(TARGET_NAME)";
498 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG ClaretCacheLOG";
499 | SWIFT_VERSION = 5.0;
500 | TARGETED_DEVICE_FAMILY = "1,2";
501 | };
502 | name = Debug;
503 | };
504 | 01C75DFC22E0724000C7D03F /* Release */ = {
505 | isa = XCBuildConfiguration;
506 | baseConfigurationReference = 71DE9F8F64DFC38FD8ABCF96 /* Pods-ClaretCacheDemo.release.xcconfig */;
507 | buildSettings = {
508 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
509 | CODE_SIGN_STYLE = Automatic;
510 | DEVELOPMENT_TEAM = 3289Y6BF27;
511 | INFOPLIST_FILE = ClaretCacheDemo/Info.plist;
512 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
513 | LD_RUNPATH_SEARCH_PATHS = (
514 | "$(inherited)",
515 | "@executable_path/Frameworks",
516 | );
517 | PRODUCT_BUNDLE_IDENTIFIER = "com.ClaretCache.-222";
518 | PRODUCT_NAME = "$(TARGET_NAME)";
519 | SWIFT_VERSION = 5.0;
520 | TARGETED_DEVICE_FAMILY = "1,2";
521 | };
522 | name = Release;
523 | };
524 | F8C53E7B22EB9EBC00B53664 /* Debug */ = {
525 | isa = XCBuildConfiguration;
526 | buildSettings = {
527 | BUNDLE_LOADER = "$(TEST_HOST)";
528 | CODE_SIGN_STYLE = Automatic;
529 | INFOPLIST_FILE = ClaretCacheDemoTests/Info.plist;
530 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
531 | LD_RUNPATH_SEARCH_PATHS = (
532 | "$(inherited)",
533 | "@executable_path/Frameworks",
534 | "@loader_path/Frameworks",
535 | );
536 | PRODUCT_BUNDLE_IDENTIFIER = com.ClaretCache.ClaretCacheDemoTests;
537 | PRODUCT_NAME = "$(TARGET_NAME)";
538 | SWIFT_VERSION = 5.0;
539 | TARGETED_DEVICE_FAMILY = "1,2";
540 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ClaretCacheDemo.app/ClaretCacheDemo";
541 | };
542 | name = Debug;
543 | };
544 | F8C53E7C22EB9EBC00B53664 /* Release */ = {
545 | isa = XCBuildConfiguration;
546 | buildSettings = {
547 | BUNDLE_LOADER = "$(TEST_HOST)";
548 | CODE_SIGN_STYLE = Automatic;
549 | INFOPLIST_FILE = ClaretCacheDemoTests/Info.plist;
550 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
551 | LD_RUNPATH_SEARCH_PATHS = (
552 | "$(inherited)",
553 | "@executable_path/Frameworks",
554 | "@loader_path/Frameworks",
555 | );
556 | PRODUCT_BUNDLE_IDENTIFIER = com.ClaretCache.ClaretCacheDemoTests;
557 | PRODUCT_NAME = "$(TARGET_NAME)";
558 | SWIFT_VERSION = 5.0;
559 | TARGETED_DEVICE_FAMILY = "1,2";
560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ClaretCacheDemo.app/ClaretCacheDemo";
561 | };
562 | name = Release;
563 | };
564 | /* End XCBuildConfiguration section */
565 |
566 | /* Begin XCConfigurationList section */
567 | 01C75DE322E0723E00C7D03F /* Build configuration list for PBXProject "ClaretCacheDemo" */ = {
568 | isa = XCConfigurationList;
569 | buildConfigurations = (
570 | 01C75DF822E0724000C7D03F /* Debug */,
571 | 01C75DF922E0724000C7D03F /* Release */,
572 | );
573 | defaultConfigurationIsVisible = 0;
574 | defaultConfigurationName = Release;
575 | };
576 | 01C75DFA22E0724000C7D03F /* Build configuration list for PBXNativeTarget "ClaretCacheDemo" */ = {
577 | isa = XCConfigurationList;
578 | buildConfigurations = (
579 | 01C75DFB22E0724000C7D03F /* Debug */,
580 | 01C75DFC22E0724000C7D03F /* Release */,
581 | );
582 | defaultConfigurationIsVisible = 0;
583 | defaultConfigurationName = Release;
584 | };
585 | F8C53E7D22EB9EBC00B53664 /* Build configuration list for PBXNativeTarget "ClaretCacheDemoTests" */ = {
586 | isa = XCConfigurationList;
587 | buildConfigurations = (
588 | F8C53E7B22EB9EBC00B53664 /* Debug */,
589 | F8C53E7C22EB9EBC00B53664 /* Release */,
590 | );
591 | defaultConfigurationIsVisible = 0;
592 | defaultConfigurationName = Release;
593 | };
594 | /* End XCConfigurationList section */
595 | };
596 | rootObject = 01C75DE022E0723E00C7D03F /* Project object */;
597 | }
598 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "ClaretCache",
6 | "repositoryURL": "https://github.com/iteatimeteam/ClaretCache.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "f4afb1f10d1d35076eef323354cd4c8678bb7317",
10 | "version": null
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/xcshareddata/xcschemes/ClaretCacheDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcodeproj/xcshareddata/xcschemes/ClaretCacheDemoTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
16 |
17 |
19 |
25 |
26 |
27 |
28 |
29 |
39 |
41 |
47 |
48 |
49 |
50 |
56 |
57 |
63 |
64 |
65 |
66 |
68 |
69 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ClaretCacheDemo.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "ClaretCache",
6 | "repositoryURL": "https://github.com/iteatimeteam/ClaretCache.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "f4afb1f10d1d35076eef323354cd4c8678bb7317",
10 | "version": null
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/ClaretCacheDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ClaretCacheDemo
4 | //
5 | // Created by BirdMichael on 2019/7/18.
6 | // Copyright © 2019 com.ClaretCache. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // 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.
23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // 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.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
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 applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/ClaretCacheDemo/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 | }
--------------------------------------------------------------------------------
/ClaretCacheDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ClaretCacheDemo/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 |
--------------------------------------------------------------------------------
/ClaretCacheDemo/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 |
--------------------------------------------------------------------------------
/ClaretCacheDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ClaretCacheDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ClaretCacheDemo
4 | //
5 | // Created by BirdMichael on 2019/7/18.
6 | // Copyright © 2019 com.ClaretCache. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view.
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/ClaretCacheDemoTests/ClaretCacheDemoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClaretCacheDemoTests.swift
3 | // ClaretCacheDemoTests
4 | //
5 | // Created by DearLan on 27/07/19.
6 | // Copyright © 2019 com.ClaretCache. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ClaretCacheDemo
11 |
12 | class ClaretCacheDemoTests: XCTestCase {
13 |
14 | let cache: MemoryCache = MemoryCache()
15 |
16 | override func setUp() {
17 | // Put setup code here. This method is called before the invocation of each test method in the class.
18 | // self.cache = MemoryCache()
19 | }
20 |
21 | override func tearDown() {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | }
24 |
25 | func testInsert() {
26 | // This is an example of a functional test case.
27 | // Use XCTAssert and related functions to verify your tests produce the correct results.
28 | self.cache.countLimit = 800
29 |
30 | for idx in 1...1000 {
31 | self.cache[idx] = idx
32 | }
33 |
34 | assert(self.cache.totalCount == 800, "淘汰策略出错")
35 | }
36 |
37 | func testRead() {
38 | self.cache.removeAll()
39 |
40 | self.cache.countLimit = 800
41 |
42 | for idx in 1...1000 {
43 | self.cache[idx] = idx
44 | }
45 | // print("key: 1, value: \(self.cache[1] ?? -1)")
46 | // print("key: 888, value: \(self.cache[888] ?? -1)")
47 | // print("key: 777, value: \(self.cache[777] ?? -1)")
48 | // print("key: 999, value: \(self.cache[999] ?? -1)")
49 | assert(self.cache[1] == nil, "淘汰策略出错")
50 | assert((self.cache[888] ?? -1) == 888, "读取key为888,失败")
51 | assert((self.cache[777] ?? -1) == 777, "读取key为777,失败")
52 | assert((self.cache[999] ?? -1) == 999, "读取key为999,失败")
53 | }
54 |
55 | // func testPerformanceExample() {
56 | // // This is an example of a performance test case.
57 | // measure(metrics: [XCTCPUMetric()]) {
58 | // // Put the code whose CPU performance you want to measure here.
59 | // for idx in 1...100_000 {
60 | // self.cache[idx] = idx
61 | // }
62 | // }
63 | // }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/ClaretCacheDemoTests/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 |
--------------------------------------------------------------------------------
/Design/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iteatimeteam/ClaretCache/e573fd4c1655907c0260c545de8c3f9cc670a882/Design/banner.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 iTeaTime(技术清谈)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "ClaretCache",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "ClaretCache",
12 | targets: ["ClaretCache"])
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "ClaretCache",
23 | dependencies: []),
24 | .testTarget(
25 | name: "ClaretCacheTests",
26 | dependencies: ["ClaretCache"])
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | use_frameworks!
3 |
4 | def swiftlint_pods
5 | pod 'SwiftLint'
6 | end
7 |
8 | target 'ClaretCacheDemo' do
9 | swiftlint_pods
10 | end
11 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - SwiftLint (0.34.0)
3 |
4 | DEPENDENCIES:
5 | - SwiftLint
6 |
7 | SPEC REPOS:
8 | https://github.com/cocoapods/specs.git:
9 | - SwiftLint
10 |
11 | SPEC CHECKSUMS:
12 | SwiftLint: 79d48a17c6565dc286c37efb8322c7b450f95c67
13 |
14 | PODFILE CHECKSUM: 350c6113d834d0113b948683438820eaac86c165
15 |
16 | COCOAPODS: 1.7.5
17 |
--------------------------------------------------------------------------------
/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - SwiftLint (0.34.0)
3 |
4 | DEPENDENCIES:
5 | - SwiftLint
6 |
7 | SPEC REPOS:
8 | https://github.com/cocoapods/specs.git:
9 | - SwiftLint
10 |
11 | SPEC CHECKSUMS:
12 | SwiftLint: 79d48a17c6565dc286c37efb8322c7b450f95c67
13 |
14 | PODFILE CHECKSUM: 350c6113d834d0113b948683438820eaac86c165
15 |
16 | COCOAPODS: 1.7.5
17 |
--------------------------------------------------------------------------------
/Pods/Pods.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | 52B60EC2A583F24ACBB69C113F5488B9 /* SwiftLint */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = AE7B4FB01588B9E6DF09CB79FC7CE7BD /* Build configuration list for PBXAggregateTarget "SwiftLint" */;
13 | buildPhases = (
14 | );
15 | dependencies = (
16 | );
17 | name = SwiftLint;
18 | };
19 | /* End PBXAggregateTarget section */
20 |
21 | /* Begin PBXBuildFile section */
22 | 1545FA0F372F7F536A95C412391296FA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; };
23 | D08F2B30E6E3EEDAEA20B9D6C7E19FEA /* Pods-ClaretCacheDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7925E73964F85077B86B1627D7143318 /* Pods-ClaretCacheDemo-dummy.m */; };
24 | FB45D196FE39F2F4092F5B5AC9AE608E /* Pods-ClaretCacheDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 51693845BD2FA4CD6E10BE86CE1A412F /* Pods-ClaretCacheDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXContainerItemProxy section */
28 | B22D5EB1EC5051B2E7711A49C1048584 /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = 52B60EC2A583F24ACBB69C113F5488B9;
33 | remoteInfo = SwiftLint;
34 | };
35 | /* End PBXContainerItemProxy section */
36 |
37 | /* Begin PBXFileReference section */
38 | 0C8A551FDDC9A8FE3B053CCE857B24EA /* Pods-ClaretCacheDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-ClaretCacheDemo.modulemap"; sourceTree = ""; };
39 | 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
40 | 51693845BD2FA4CD6E10BE86CE1A412F /* Pods-ClaretCacheDemo-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-ClaretCacheDemo-umbrella.h"; sourceTree = ""; };
41 | 6DD1E28C6C36FFC83305E6C849CED21A /* Pods-ClaretCacheDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ClaretCacheDemo.release.xcconfig"; sourceTree = ""; };
42 | 709AB956124B9D6A556A971AC9DF2A9F /* Pods-ClaretCacheDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-ClaretCacheDemo-acknowledgements.markdown"; sourceTree = ""; };
43 | 7665F48DF774A41833998E41BA1D3C54 /* Pods_ClaretCacheDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_ClaretCacheDemo.framework; path = "Pods-ClaretCacheDemo.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 7925E73964F85077B86B1627D7143318 /* Pods-ClaretCacheDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-ClaretCacheDemo-dummy.m"; sourceTree = ""; };
45 | 89F85ACF3D78B39506B75103B4694B36 /* SwiftLint.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftLint.xcconfig; sourceTree = ""; };
46 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
47 | AAEA8C8384B11749144512E4E7A315C7 /* Pods-ClaretCacheDemo-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ClaretCacheDemo-Info.plist"; sourceTree = ""; };
48 | B0F004D9A78B6D873D312EB2D9B5002C /* Pods-ClaretCacheDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-ClaretCacheDemo-acknowledgements.plist"; sourceTree = ""; };
49 | E4E9FE683D5E75026B361E1A2E7ED6D9 /* Pods-ClaretCacheDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-ClaretCacheDemo.debug.xcconfig"; sourceTree = ""; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 7893321BD4F33A28DEB9CB51B6A4DF9F /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | 1545FA0F372F7F536A95C412391296FA /* Foundation.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 127668E3C5A20E1E2535F705BDFC5935 /* Targets Support Files */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 52B682B36B5025B805B1CA0BBB032C15 /* Pods-ClaretCacheDemo */,
68 | );
69 | name = "Targets Support Files";
70 | sourceTree = "";
71 | };
72 | 52B682B36B5025B805B1CA0BBB032C15 /* Pods-ClaretCacheDemo */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 0C8A551FDDC9A8FE3B053CCE857B24EA /* Pods-ClaretCacheDemo.modulemap */,
76 | 709AB956124B9D6A556A971AC9DF2A9F /* Pods-ClaretCacheDemo-acknowledgements.markdown */,
77 | B0F004D9A78B6D873D312EB2D9B5002C /* Pods-ClaretCacheDemo-acknowledgements.plist */,
78 | 7925E73964F85077B86B1627D7143318 /* Pods-ClaretCacheDemo-dummy.m */,
79 | AAEA8C8384B11749144512E4E7A315C7 /* Pods-ClaretCacheDemo-Info.plist */,
80 | 51693845BD2FA4CD6E10BE86CE1A412F /* Pods-ClaretCacheDemo-umbrella.h */,
81 | E4E9FE683D5E75026B361E1A2E7ED6D9 /* Pods-ClaretCacheDemo.debug.xcconfig */,
82 | 6DD1E28C6C36FFC83305E6C849CED21A /* Pods-ClaretCacheDemo.release.xcconfig */,
83 | );
84 | name = "Pods-ClaretCacheDemo";
85 | path = "Target Support Files/Pods-ClaretCacheDemo";
86 | sourceTree = "";
87 | };
88 | 965877409E01FB3D85D85E90E6B30185 /* Pods */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 9AED61B9A1CFA211C061344F741DBA07 /* SwiftLint */,
92 | );
93 | name = Pods;
94 | sourceTree = "";
95 | };
96 | 9AED61B9A1CFA211C061344F741DBA07 /* SwiftLint */ = {
97 | isa = PBXGroup;
98 | children = (
99 | EEA599FE5C15138FF0981CCFB01E55AA /* Support Files */,
100 | );
101 | name = SwiftLint;
102 | path = SwiftLint;
103 | sourceTree = "";
104 | };
105 | C0834CEBB1379A84116EF29F93051C60 /* iOS */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */,
109 | );
110 | name = iOS;
111 | sourceTree = "";
112 | };
113 | CF1408CF629C7361332E53B88F7BD30C = {
114 | isa = PBXGroup;
115 | children = (
116 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */,
117 | D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */,
118 | 965877409E01FB3D85D85E90E6B30185 /* Pods */,
119 | DDF9FCB28C5C05272C13C9F6D61BE6B5 /* Products */,
120 | 127668E3C5A20E1E2535F705BDFC5935 /* Targets Support Files */,
121 | );
122 | sourceTree = "";
123 | };
124 | D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = {
125 | isa = PBXGroup;
126 | children = (
127 | C0834CEBB1379A84116EF29F93051C60 /* iOS */,
128 | );
129 | name = Frameworks;
130 | sourceTree = "";
131 | };
132 | DDF9FCB28C5C05272C13C9F6D61BE6B5 /* Products */ = {
133 | isa = PBXGroup;
134 | children = (
135 | 7665F48DF774A41833998E41BA1D3C54 /* Pods_ClaretCacheDemo.framework */,
136 | );
137 | name = Products;
138 | sourceTree = "";
139 | };
140 | EEA599FE5C15138FF0981CCFB01E55AA /* Support Files */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 89F85ACF3D78B39506B75103B4694B36 /* SwiftLint.xcconfig */,
144 | );
145 | name = "Support Files";
146 | path = "../Target Support Files/SwiftLint";
147 | sourceTree = "";
148 | };
149 | /* End PBXGroup section */
150 |
151 | /* Begin PBXHeadersBuildPhase section */
152 | A77453827E66D7565EB820308273BFED /* Headers */ = {
153 | isa = PBXHeadersBuildPhase;
154 | buildActionMask = 2147483647;
155 | files = (
156 | FB45D196FE39F2F4092F5B5AC9AE608E /* Pods-ClaretCacheDemo-umbrella.h in Headers */,
157 | );
158 | runOnlyForDeploymentPostprocessing = 0;
159 | };
160 | /* End PBXHeadersBuildPhase section */
161 |
162 | /* Begin PBXNativeTarget section */
163 | 1697114A1DE7BB63D9CAD67693D44DBF /* Pods-ClaretCacheDemo */ = {
164 | isa = PBXNativeTarget;
165 | buildConfigurationList = B490CF3B9554414A29BDFD3239957AF0 /* Build configuration list for PBXNativeTarget "Pods-ClaretCacheDemo" */;
166 | buildPhases = (
167 | A77453827E66D7565EB820308273BFED /* Headers */,
168 | 6679A687BBDFA6C201E69746FFE5AA2E /* Sources */,
169 | 7893321BD4F33A28DEB9CB51B6A4DF9F /* Frameworks */,
170 | B0DD032C0D4533BC51C9D454BFBD7384 /* Resources */,
171 | );
172 | buildRules = (
173 | );
174 | dependencies = (
175 | 1D2338711D541CFBD66E54FA4F34A5DB /* PBXTargetDependency */,
176 | );
177 | name = "Pods-ClaretCacheDemo";
178 | productName = "Pods-ClaretCacheDemo";
179 | productReference = 7665F48DF774A41833998E41BA1D3C54 /* Pods_ClaretCacheDemo.framework */;
180 | productType = "com.apple.product-type.framework";
181 | };
182 | /* End PBXNativeTarget section */
183 |
184 | /* Begin PBXProject section */
185 | BFDFE7DC352907FC980B868725387E98 /* Project object */ = {
186 | isa = PBXProject;
187 | attributes = {
188 | LastSwiftUpdateCheck = 1100;
189 | LastUpgradeCheck = 1100;
190 | };
191 | buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */;
192 | compatibilityVersion = "Xcode 9.3";
193 | developmentRegion = en;
194 | hasScannedForEncodings = 0;
195 | knownRegions = (
196 | en,
197 | );
198 | mainGroup = CF1408CF629C7361332E53B88F7BD30C;
199 | productRefGroup = DDF9FCB28C5C05272C13C9F6D61BE6B5 /* Products */;
200 | projectDirPath = "";
201 | projectRoot = "";
202 | targets = (
203 | 1697114A1DE7BB63D9CAD67693D44DBF /* Pods-ClaretCacheDemo */,
204 | 52B60EC2A583F24ACBB69C113F5488B9 /* SwiftLint */,
205 | );
206 | };
207 | /* End PBXProject section */
208 |
209 | /* Begin PBXResourcesBuildPhase section */
210 | B0DD032C0D4533BC51C9D454BFBD7384 /* Resources */ = {
211 | isa = PBXResourcesBuildPhase;
212 | buildActionMask = 2147483647;
213 | files = (
214 | );
215 | runOnlyForDeploymentPostprocessing = 0;
216 | };
217 | /* End PBXResourcesBuildPhase section */
218 |
219 | /* Begin PBXSourcesBuildPhase section */
220 | 6679A687BBDFA6C201E69746FFE5AA2E /* Sources */ = {
221 | isa = PBXSourcesBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | D08F2B30E6E3EEDAEA20B9D6C7E19FEA /* Pods-ClaretCacheDemo-dummy.m in Sources */,
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | /* End PBXSourcesBuildPhase section */
229 |
230 | /* Begin PBXTargetDependency section */
231 | 1D2338711D541CFBD66E54FA4F34A5DB /* PBXTargetDependency */ = {
232 | isa = PBXTargetDependency;
233 | name = SwiftLint;
234 | target = 52B60EC2A583F24ACBB69C113F5488B9 /* SwiftLint */;
235 | targetProxy = B22D5EB1EC5051B2E7711A49C1048584 /* PBXContainerItemProxy */;
236 | };
237 | /* End PBXTargetDependency section */
238 |
239 | /* Begin XCBuildConfiguration section */
240 | 84D7C4574E8F0F3095623F0E06F5B402 /* Release */ = {
241 | isa = XCBuildConfiguration;
242 | baseConfigurationReference = 89F85ACF3D78B39506B75103B4694B36 /* SwiftLint.xcconfig */;
243 | buildSettings = {
244 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
245 | CODE_SIGN_IDENTITY = "iPhone Developer";
246 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
247 | LD_RUNPATH_SEARCH_PATHS = (
248 | "$(inherited)",
249 | "@executable_path/Frameworks",
250 | );
251 | SDKROOT = iphoneos;
252 | TARGETED_DEVICE_FAMILY = "1,2";
253 | VALIDATE_PRODUCT = YES;
254 | };
255 | name = Release;
256 | };
257 | 8F17DC3A99F99FBAD606CE6963886315 /* Release */ = {
258 | isa = XCBuildConfiguration;
259 | buildSettings = {
260 | ALWAYS_SEARCH_USER_PATHS = NO;
261 | CLANG_ANALYZER_NONNULL = YES;
262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
264 | CLANG_CXX_LIBRARY = "libc++";
265 | CLANG_ENABLE_MODULES = YES;
266 | CLANG_ENABLE_OBJC_ARC = YES;
267 | CLANG_ENABLE_OBJC_WEAK = YES;
268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
269 | CLANG_WARN_BOOL_CONVERSION = YES;
270 | CLANG_WARN_COMMA = YES;
271 | CLANG_WARN_CONSTANT_CONVERSION = YES;
272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
275 | CLANG_WARN_EMPTY_BODY = YES;
276 | CLANG_WARN_ENUM_CONVERSION = YES;
277 | CLANG_WARN_INFINITE_RECURSION = YES;
278 | CLANG_WARN_INT_CONVERSION = YES;
279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
284 | CLANG_WARN_STRICT_PROTOTYPES = YES;
285 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
286 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
287 | CLANG_WARN_UNREACHABLE_CODE = YES;
288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
289 | COPY_PHASE_STRIP = NO;
290 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
291 | ENABLE_NS_ASSERTIONS = NO;
292 | ENABLE_STRICT_OBJC_MSGSEND = YES;
293 | GCC_C_LANGUAGE_STANDARD = gnu11;
294 | GCC_NO_COMMON_BLOCKS = YES;
295 | GCC_PREPROCESSOR_DEFINITIONS = (
296 | "POD_CONFIGURATION_RELEASE=1",
297 | "$(inherited)",
298 | );
299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
301 | GCC_WARN_UNDECLARED_SELECTOR = YES;
302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
303 | GCC_WARN_UNUSED_FUNCTION = YES;
304 | GCC_WARN_UNUSED_VARIABLE = YES;
305 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
306 | MTL_ENABLE_DEBUG_INFO = NO;
307 | MTL_FAST_MATH = YES;
308 | PRODUCT_NAME = "$(TARGET_NAME)";
309 | STRIP_INSTALLED_PRODUCT = NO;
310 | SWIFT_COMPILATION_MODE = wholemodule;
311 | SWIFT_OPTIMIZATION_LEVEL = "-O";
312 | SWIFT_VERSION = 5.0;
313 | SYMROOT = "${SRCROOT}/../build";
314 | };
315 | name = Release;
316 | };
317 | 916E0404255105F480DC4950B7625F7A /* Debug */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ALWAYS_SEARCH_USER_PATHS = NO;
321 | CLANG_ANALYZER_NONNULL = YES;
322 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
324 | CLANG_CXX_LIBRARY = "libc++";
325 | CLANG_ENABLE_MODULES = YES;
326 | CLANG_ENABLE_OBJC_ARC = YES;
327 | CLANG_ENABLE_OBJC_WEAK = YES;
328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
329 | CLANG_WARN_BOOL_CONVERSION = YES;
330 | CLANG_WARN_COMMA = YES;
331 | CLANG_WARN_CONSTANT_CONVERSION = YES;
332 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
334 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
335 | CLANG_WARN_EMPTY_BODY = YES;
336 | CLANG_WARN_ENUM_CONVERSION = YES;
337 | CLANG_WARN_INFINITE_RECURSION = YES;
338 | CLANG_WARN_INT_CONVERSION = YES;
339 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
340 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
341 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
343 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
344 | CLANG_WARN_STRICT_PROTOTYPES = YES;
345 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
346 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
347 | CLANG_WARN_UNREACHABLE_CODE = YES;
348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
349 | COPY_PHASE_STRIP = NO;
350 | DEBUG_INFORMATION_FORMAT = dwarf;
351 | ENABLE_STRICT_OBJC_MSGSEND = YES;
352 | ENABLE_TESTABILITY = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu11;
354 | GCC_DYNAMIC_NO_PIC = NO;
355 | GCC_NO_COMMON_BLOCKS = YES;
356 | GCC_OPTIMIZATION_LEVEL = 0;
357 | GCC_PREPROCESSOR_DEFINITIONS = (
358 | "POD_CONFIGURATION_DEBUG=1",
359 | "DEBUG=1",
360 | "$(inherited)",
361 | );
362 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
363 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
364 | GCC_WARN_UNDECLARED_SELECTOR = YES;
365 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
366 | GCC_WARN_UNUSED_FUNCTION = YES;
367 | GCC_WARN_UNUSED_VARIABLE = YES;
368 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
369 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
370 | MTL_FAST_MATH = YES;
371 | ONLY_ACTIVE_ARCH = YES;
372 | PRODUCT_NAME = "$(TARGET_NAME)";
373 | STRIP_INSTALLED_PRODUCT = NO;
374 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
375 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
376 | SWIFT_VERSION = 5.0;
377 | SYMROOT = "${SRCROOT}/../build";
378 | };
379 | name = Debug;
380 | };
381 | C1EF790ADE2ABA9BA5FB669FFE7702D1 /* Debug */ = {
382 | isa = XCBuildConfiguration;
383 | baseConfigurationReference = E4E9FE683D5E75026B361E1A2E7ED6D9 /* Pods-ClaretCacheDemo.debug.xcconfig */;
384 | buildSettings = {
385 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
386 | CODE_SIGN_IDENTITY = "";
387 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
389 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
390 | CURRENT_PROJECT_VERSION = 1;
391 | DEFINES_MODULE = YES;
392 | DYLIB_COMPATIBILITY_VERSION = 1;
393 | DYLIB_CURRENT_VERSION = 1;
394 | DYLIB_INSTALL_NAME_BASE = "@rpath";
395 | INFOPLIST_FILE = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-Info.plist";
396 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
397 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
398 | LD_RUNPATH_SEARCH_PATHS = (
399 | "$(inherited)",
400 | "@executable_path/Frameworks",
401 | "@loader_path/Frameworks",
402 | );
403 | MACH_O_TYPE = staticlib;
404 | MODULEMAP_FILE = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.modulemap";
405 | OTHER_LDFLAGS = "";
406 | OTHER_LIBTOOLFLAGS = "";
407 | PODS_ROOT = "$(SRCROOT)";
408 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
409 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
410 | SDKROOT = iphoneos;
411 | SKIP_INSTALL = YES;
412 | TARGETED_DEVICE_FAMILY = "1,2";
413 | VERSIONING_SYSTEM = "apple-generic";
414 | VERSION_INFO_PREFIX = "";
415 | };
416 | name = Debug;
417 | };
418 | C9BE49D9EA22DAC17AD4F5188F4CD81F /* Release */ = {
419 | isa = XCBuildConfiguration;
420 | baseConfigurationReference = 6DD1E28C6C36FFC83305E6C849CED21A /* Pods-ClaretCacheDemo.release.xcconfig */;
421 | buildSettings = {
422 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
423 | CODE_SIGN_IDENTITY = "";
424 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
425 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
426 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
427 | CURRENT_PROJECT_VERSION = 1;
428 | DEFINES_MODULE = YES;
429 | DYLIB_COMPATIBILITY_VERSION = 1;
430 | DYLIB_CURRENT_VERSION = 1;
431 | DYLIB_INSTALL_NAME_BASE = "@rpath";
432 | INFOPLIST_FILE = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-Info.plist";
433 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
434 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
435 | LD_RUNPATH_SEARCH_PATHS = (
436 | "$(inherited)",
437 | "@executable_path/Frameworks",
438 | "@loader_path/Frameworks",
439 | );
440 | MACH_O_TYPE = staticlib;
441 | MODULEMAP_FILE = "Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.modulemap";
442 | OTHER_LDFLAGS = "";
443 | OTHER_LIBTOOLFLAGS = "";
444 | PODS_ROOT = "$(SRCROOT)";
445 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
446 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
447 | SDKROOT = iphoneos;
448 | SKIP_INSTALL = YES;
449 | TARGETED_DEVICE_FAMILY = "1,2";
450 | VALIDATE_PRODUCT = YES;
451 | VERSIONING_SYSTEM = "apple-generic";
452 | VERSION_INFO_PREFIX = "";
453 | };
454 | name = Release;
455 | };
456 | DEED47E09AF743F48544C1C4FEADEF47 /* Debug */ = {
457 | isa = XCBuildConfiguration;
458 | baseConfigurationReference = 89F85ACF3D78B39506B75103B4694B36 /* SwiftLint.xcconfig */;
459 | buildSettings = {
460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
461 | CODE_SIGN_IDENTITY = "iPhone Developer";
462 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
463 | LD_RUNPATH_SEARCH_PATHS = (
464 | "$(inherited)",
465 | "@executable_path/Frameworks",
466 | );
467 | SDKROOT = iphoneos;
468 | TARGETED_DEVICE_FAMILY = "1,2";
469 | };
470 | name = Debug;
471 | };
472 | /* End XCBuildConfiguration section */
473 |
474 | /* Begin XCConfigurationList section */
475 | 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = {
476 | isa = XCConfigurationList;
477 | buildConfigurations = (
478 | 916E0404255105F480DC4950B7625F7A /* Debug */,
479 | 8F17DC3A99F99FBAD606CE6963886315 /* Release */,
480 | );
481 | defaultConfigurationIsVisible = 0;
482 | defaultConfigurationName = Release;
483 | };
484 | AE7B4FB01588B9E6DF09CB79FC7CE7BD /* Build configuration list for PBXAggregateTarget "SwiftLint" */ = {
485 | isa = XCConfigurationList;
486 | buildConfigurations = (
487 | DEED47E09AF743F48544C1C4FEADEF47 /* Debug */,
488 | 84D7C4574E8F0F3095623F0E06F5B402 /* Release */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | B490CF3B9554414A29BDFD3239957AF0 /* Build configuration list for PBXNativeTarget "Pods-ClaretCacheDemo" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | C1EF790ADE2ABA9BA5FB669FFE7702D1 /* Debug */,
497 | C9BE49D9EA22DAC17AD4F5188F4CD81F /* Release */,
498 | );
499 | defaultConfigurationIsVisible = 0;
500 | defaultConfigurationName = Release;
501 | };
502 | /* End XCConfigurationList section */
503 | };
504 | rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */;
505 | }
506 |
--------------------------------------------------------------------------------
/Pods/SwiftLint/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Realm Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Pods/SwiftLint/swiftlint:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iteatimeteam/ClaretCache/e573fd4c1655907c0260c545de8c3f9cc670a882/Pods/SwiftLint/swiftlint
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-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.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## SwiftLint
5 |
6 | The MIT License (MIT)
7 |
8 | Copyright (c) 2015 Realm Inc.
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 |
28 | Generated by CocoaPods - https://cocoapods.org
29 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | The MIT License (MIT)
18 |
19 | Copyright (c) 2015 Realm Inc.
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy
22 | of this software and associated documentation files (the "Software"), to deal
23 | in the Software without restriction, including without limitation the rights
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the Software is
26 | furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all
29 | copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | SOFTWARE.
38 |
39 | License
40 | MIT
41 | Title
42 | SwiftLint
43 | Type
44 | PSGroupSpecifier
45 |
46 |
47 | FooterText
48 | Generated by CocoaPods - https://cocoapods.org
49 | Title
50 |
51 | Type
52 | PSGroupSpecifier
53 |
54 |
55 | StringsTable
56 | Acknowledgements
57 | Title
58 | Acknowledgements
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_ClaretCacheDemo : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_ClaretCacheDemo
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_ClaretCacheDemoVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_ClaretCacheDemoVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.debug.xcconfig:
--------------------------------------------------------------------------------
1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
3 | PODS_BUILD_DIR = ${BUILD_DIR}
4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
6 | PODS_ROOT = ${SRCROOT}/Pods
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_ClaretCacheDemo {
2 | umbrella header "Pods-ClaretCacheDemo-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-ClaretCacheDemo/Pods-ClaretCacheDemo.release.xcconfig:
--------------------------------------------------------------------------------
1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
3 | PODS_BUILD_DIR = ${BUILD_DIR}
4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
6 | PODS_ROOT = ${SRCROOT}/Pods
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/SwiftLint/SwiftLint.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftLint
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | PODS_BUILD_DIR = ${BUILD_DIR}
4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
5 | PODS_ROOT = ${SRCROOT}
6 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftLint
7 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
8 | SKIP_INSTALL = YES
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ClaretCache | YYKit-Cache模块 Swift 版本
2 |
3 | 【声明:未发布,勿使用,预计2020年10月底发布】
4 |
5 |
6 | --------------------------------------------
7 |
8 | 
9 |
10 |
13 |
14 | --------------------------------------------
15 | 
16 |
17 | iTeaTime(技术清谈)团队出品。
18 |
19 | 项目简介
20 | --------------------------------------------
21 |
22 | 本项目意在将 YYKit-Cache 模块迁移至 Swift 版本,与 YYKit 作者沟通得知暂无 Swift 版本计划,这就是本项目的来源。
23 |
24 | 本库在版本发布后会陆续解决原仓库已存 issue, YYKit 原作者暂时未参与本项目开发,故本库与 YYKit-Cache 模块为两个独立项目,本仓库新引入的问题请在本仓库提交issue,以免给 YYKit 作者增加额外的维护成本。
25 |
26 |
27 | 进度
28 | --------------------------------------------
29 |
30 | ### 项目进度以及任务认领:
31 |
32 | 人员 | 地址
33 | :-------------:|:-------------:
34 | iTeaTime(技术清谈)团队内成员 | [**GitHub team Discussions**](https://github.com/orgs/iteatimeteam/teams/iteatime)
35 | 团队外成员 | [GitHub Projects 详情]( https://github.com/iteatimeteam/ClaretCache/projects).
36 |
37 |
38 |
39 | deadline
40 | --------------------------------------------
41 |
42 | 如果 2020年12月1日还未看到发布alpha版本,那么本项目可能就是黄了,那就只能说声抱歉了,请在项目发布后再 star。
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Sources/ClaretCache/ClaretCache.swift:
--------------------------------------------------------------------------------
1 | struct ClaretCache {
2 | var text = "Hello, World!"
3 | }
4 |
--------------------------------------------------------------------------------
/Sources/ClaretCache/KVStorage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KVStorage.swift
3 | // ClaretCacheDemo
4 | //
5 | // Created by HZheng on 2019/7/28.
6 | // Copyright © 2019 com.ClaretCache. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SQLite3
11 |
12 | #if canImport(UIKit)
13 | import UIKit.UIApplication
14 | #endif
15 |
16 | #if canImport(QuartzCore)
17 | import QuartzCore.CABase
18 | #endif
19 |
20 | var isAppExtension: Bool = {
21 | return Bundle.main.bundleURL.pathExtension == "appex"
22 | }()
23 |
24 | public enum KVStorageType {
25 | case file
26 | case sqlite
27 | case mixed
28 | }
29 |
30 | /**
31 | KVStorageItem is used by `KVStorage` to store key-value pair and meta data.
32 | Typically, you should not use this class directly.
33 | */
34 | public class KVStorageItem {
35 | var key: String? ///< key
36 | var value: Data? ///< value
37 | var fileName: String? ///< fileName (nil if inline)
38 | var size: Int = 0 ///< value's size in bytes
39 | var modTime: Int = 0 ///< modification unix timestamp
40 | var accessTime: Int = 0 ///< last access unix timestamp
41 | var extendedData: Data? ///< extended data (nil if no extended data)
42 | }
43 |
44 | /*
45 | File:
46 | /path/
47 | /manifest.sqlite
48 | /manifest.sqlite-shm
49 | /manifest.sqlite-wal
50 | /data/
51 | /e10adc3949ba59abbe56e057f20f883e
52 | /e10adc3949ba59abbe56e057f20f883e
53 | /trash/
54 | /unused_file_or_folder
55 |
56 | SQL:
57 | create table if not exists manifest (
58 | key text,
59 | filename text,
60 | size integer,
61 | inline_data blob,
62 | modification_time integer,
63 | last_access_time integer,
64 | extended_data blob,
65 | primary key(key)
66 | );
67 | create index if not exists last_access_time_idx on manifest(last_access_time);
68 | */
69 |
70 | public class KVStorage {
71 | fileprivate let kMaxErrorRetryCount = 8
72 | fileprivate let kMinRetryTimeInterval = 2.0
73 | fileprivate let kPathLengthMax = PATH_MAX - 64
74 | fileprivate let kDBFileName = "manifest.sqlite"
75 | fileprivate let kDBShmFileName = "manifest.sqlite-shm"
76 | fileprivate let kDBWalFileName = "manifest.sqlite-wal"
77 | fileprivate let kDataDirectoryName = "data"
78 | fileprivate let kTrashDirectoryName = "trash"
79 |
80 | fileprivate var trashQueue: DispatchQueue
81 | fileprivate var path: URL
82 | fileprivate var dbPath: URL
83 | fileprivate var dataPath: URL
84 | fileprivate var trashPath: URL
85 | fileprivate var database: OpaquePointer?
86 | fileprivate var dbStmtCache: [String: Any]?
87 | fileprivate var dbLastOpenErrorTime: TimeInterval = 0
88 | fileprivate var dbOpenErrorCount: UInt = 0
89 | fileprivate(set) var type: KVStorageType
90 | fileprivate var errorLogsEnabled: Bool = true
91 |
92 | fileprivate let fileManger = FileManager.default
93 |
94 | init?(path: URL, type: KVStorageType) {
95 | guard !path.absoluteString.isEmpty, path.absoluteString.count <= kPathLengthMax else {
96 | print("KVStorage init error: invalid path: [\(path)].")
97 | return nil
98 | }
99 |
100 | self.path = path
101 | self.type = type
102 | trashQueue = OS_dispatch_queue_serial(label: "com.iteatime.cache.disk.trash")
103 | dataPath = path.appendingPathComponent(kDataDirectoryName)
104 | trashPath = path.appendingPathComponent(kTrashDirectoryName)
105 | dbPath = path.appendingPathComponent(kDBFileName)
106 | do {
107 | try fileManger.createDirectory(at: path, withIntermediateDirectories: true, attributes: nil)
108 | try fileManger.createDirectory(at: dataPath, withIntermediateDirectories: true, attributes: nil)
109 | try fileManger.createDirectory(at: trashPath, withIntermediateDirectories: true, attributes: nil)
110 | } catch {
111 | return nil
112 | }
113 |
114 | if !dbOpen() || !dbInitialize() {
115 | // db file may broken...
116 | dbClose()
117 | reset() // rebuild
118 | if !dbOpen() || !dbInitialize() {
119 | dbClose()
120 | log("KVStorage init error: fail to open sqlite db.")
121 | return nil
122 | }
123 | }
124 | fileEmptyTrashInBackground()
125 | }
126 |
127 | #if canImport(UIKit)
128 | func sharedExtensionApplication() -> UIApplication? {
129 | return isAppExtension ? nil : UIApplication.shared
130 | }
131 | #endif
132 |
133 | deinit {
134 | #if canImport(UIKit)
135 | let taskID = sharedExtensionApplication()?.beginBackgroundTask(expirationHandler: nil)
136 | #endif
137 | dbClose()
138 | #if canImport(UIKit)
139 | if let task = taskID {
140 | sharedExtensionApplication()?.endBackgroundTask(task)
141 | }
142 | #endif
143 | }
144 |
145 | // MARK: private
146 | fileprivate func reset() {
147 | do {
148 | try fileManger.removeItem(at: path.appendingPathComponent(kDBFileName))
149 | try fileManger.removeItem(at: path.appendingPathComponent(kDBShmFileName))
150 | try fileManger.removeItem(at: path.appendingPathComponent(kDBWalFileName))
151 | fileMoveAllToTrash()
152 | fileEmptyTrashInBackground()
153 | } catch {
154 | log("reset error: \(error)")
155 | }
156 | }
157 |
158 | fileprivate final func currentTime() -> TimeInterval {
159 | #if canImport(QuartzCore)
160 | return CACurrentMediaTime()
161 | #else
162 | return Date().timeIntervalSince1970
163 | #endif
164 | }
165 |
166 | fileprivate func log(_ items: Any..., separator: String = " ", terminator: String = "\n") {
167 | if errorLogsEnabled {
168 | print(items, separator, terminator)
169 | }
170 | }
171 |
172 | // MARK: File
173 |
174 | fileprivate func fileWrite(fileName: String, data: Data) -> Bool {
175 | do {
176 | try data.write(to: dataPath.appendingPathComponent(fileName))
177 | } catch {
178 | log("\(#function) line:(\(#line) file write error. fileName: (\(fileName)")
179 | return false
180 | }
181 | return true
182 | }
183 |
184 | fileprivate func fileRead(fileName: String) -> Data? {
185 | do {
186 | return try Data(contentsOf: dataPath.appendingPathComponent(fileName))
187 | } catch {
188 | log("\(#function) line:(\(#line) file read error. fileName: (\(fileName)")
189 | return nil
190 | }
191 | }
192 |
193 | @discardableResult
194 | fileprivate func fileDelete(fileName: String) -> Bool {
195 | do {
196 | try fileManger.removeItem(at: dataPath.appendingPathComponent(fileName))
197 | } catch {
198 | log("\(#function) line:(\(#line) file delete error. fileName: (\(fileName)")
199 | return false
200 | }
201 | return true
202 | }
203 |
204 | @discardableResult
205 | fileprivate func fileMoveAllToTrash() -> Bool {
206 | let uuid = UUID().uuidString
207 | let tmpPath = trashPath.appendingPathComponent(uuid)
208 | do {
209 | try fileManger.moveItem(at: dataPath, to: tmpPath)
210 | try fileManger.createDirectory(at: dataPath, withIntermediateDirectories: true, attributes: nil)
211 | } catch {
212 | log("\(#function) line:(\(#line) file move all to trash error.")
213 | return false
214 | }
215 | return true
216 | }
217 |
218 | // empty the trash if failed at last time
219 | fileprivate func fileEmptyTrashInBackground() {
220 | let trashPath = self.trashPath
221 | DispatchQueue.global().async {
222 | do {
223 | let directoryContents = try self.fileManger.contentsOfDirectory(atPath: trashPath.absoluteString)
224 | for path in directoryContents {
225 | let fullPath = trashPath.appendingPathComponent(path)
226 | try self.fileManger.removeItem(at: fullPath)
227 | }
228 | } catch {
229 | self.log("remove trash error: \(error)")
230 | }
231 | }
232 | }
233 |
234 | // MARK: DataBase
235 |
236 | fileprivate func dbOpen() -> Bool {
237 | guard database == nil else { return true }
238 | let result = sqlite3_open(dbPath.absoluteString, &database)
239 | guard result == SQLITE_OK else {
240 | database = nil
241 | dbStmtCache = nil
242 | dbLastOpenErrorTime = currentTime()
243 | dbOpenErrorCount+=1
244 | log("\(#function) line:\(#line) sqlite open failed (\(result)).")
245 | return false
246 | }
247 | dbStmtCache = Dictionary()
248 | dbLastOpenErrorTime = 0
249 | dbOpenErrorCount = 0
250 | return true
251 | }
252 |
253 | @discardableResult
254 | fileprivate func dbClose() -> Bool {
255 | guard let database = database else { return true }
256 | var retry = false
257 | var stmtFinalized = false
258 | dbStmtCache = nil
259 | repeat {
260 | retry = false
261 | let result = sqlite3_close(database)
262 | if result == SQLITE_BUSY || result == SQLITE_LOCKED {
263 | if !stmtFinalized {
264 | stmtFinalized = true
265 | var stmt = sqlite3_next_stmt(database, nil)
266 | while stmt != nil {
267 | sqlite3_finalize(stmt)
268 | stmt = sqlite3_next_stmt(database, nil)
269 | retry = true
270 | }
271 | }
272 | } else if result != SQLITE_OK {
273 | log("\(#function) line:\(#line) sqlite close failed (\(result).")
274 | }
275 | } while(retry)
276 | self.database = nil
277 | return true
278 | }
279 |
280 | fileprivate func dbCheck() -> Bool {
281 | guard database == nil else { return true }
282 | if dbOpenErrorCount < kMaxErrorRetryCount &&
283 | currentTime() - dbLastOpenErrorTime > kMinRetryTimeInterval {
284 | return dbOpen() && dbInitialize()
285 | } else {
286 | return false
287 | }
288 | }
289 |
290 | fileprivate func dbInitialize() -> Bool {
291 | let sql = "pragma journal_mode = wal; pragma synchronous = normal;"
292 | + " create table if not exists manifest (key text, filename text, size integer, inline_data blob, modification_time integer, last_access_time integer, extended_data blob, primary key(key));"
293 | + " create index if not exists last_access_time_idx on manifest(last_access_time);"
294 | return dbExecute(sql)
295 | }
296 |
297 | fileprivate func dbCheckpoint() {
298 | guard dbCheck() else { return }
299 | // Cause a checkpoint to occur, merge `sqlite-wal` file to `sqlite` file.
300 | sqlite3_wal_checkpoint(database, nil)
301 | }
302 |
303 | fileprivate func dbExecute(_ sql: String) -> Bool {
304 | guard !sql.isEmpty, dbCheck() else { return false }
305 | return sqlite3_exec(database, sql, nil, nil, nil) == SQLITE_OK
306 | }
307 |
308 | fileprivate func dbPrepareStmt(_ sql: String) -> OpaquePointer? {
309 | guard dbCheck(), !sql.isEmpty, dbStmtCache != nil else { return nil }
310 | var stmt = dbStmtCache?[sql] as? OpaquePointer
311 | if stmt == nil {
312 | let result = sqlite3_prepare_v2(database, sql, -1, &stmt, nil)
313 | guard result == SQLITE_OK else {
314 | log("\(#function) line:\(#line) sqlite stmt prepare error (\(result)): \(errorMessage)")
315 | return nil
316 | }
317 | dbStmtCache?[sql] = stmt
318 | } else {
319 | sqlite3_reset(stmt)
320 | }
321 | return stmt
322 | }
323 |
324 | fileprivate var errorMessage: String {
325 | if let errorPointer = sqlite3_errmsg(database) {
326 | let errorMessage = String(cString: errorPointer)
327 | return errorMessage
328 | } else {
329 | return "No error message provided from sqlite."
330 | }
331 | }
332 |
333 | fileprivate func dbJoinedKeys(_ keys: [Any]) -> String {
334 | var string = ""
335 | let max = keys.count
336 | for index in 0 ..< max {
337 | string.append("?")
338 | if index + 1 != max {
339 | string.append(",")
340 | }
341 | }
342 | return string
343 | }
344 |
345 | fileprivate func dbBindJoinedKeys(keys: [String], stmt: OpaquePointer, fromIndex index: Int) {
346 | let max = keys.count
347 | for index in 0 ..< max {
348 | let key = keys[index] as NSString
349 | sqlite3_bind_text(stmt, Int32(index + index), key.utf8String, -1, nil)
350 | }
351 | }
352 |
353 | fileprivate func dbSave(key: String, value: Data, fileName: String?, extendedData: Data?) -> Bool {
354 | let sql = "insert or replace into manifest (key, filename, size, inline_data, modification_time, last_access_time, extended_data) values (?1, ?2, ?3, ?4, ?5, ?6, ?7);"
355 | guard let stmt = dbPrepareStmt(sql) else { return false }
356 | let timestamp = Int32(time(nil))
357 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
358 | if let file = fileName {
359 | sqlite3_bind_text(stmt, 2, (file as NSString).utf8String, -1, nil)
360 | } else {
361 | sqlite3_bind_text(stmt, 2, nil, -1, nil)
362 | }
363 | sqlite3_bind_int(stmt, 3, Int32(value.count))
364 | if fileName?.isEmpty ?? false {
365 | sqlite3_bind_blob(stmt, 4, (value as NSData).bytes, Int32(value.count), nil)
366 | } else {
367 | sqlite3_bind_blob(stmt, 4, nil, 0, nil)
368 | }
369 | sqlite3_bind_int(stmt, 5, timestamp)
370 | sqlite3_bind_int(stmt, 6, timestamp)
371 | if let exData = extendedData {
372 | sqlite3_bind_blob(stmt, 7, (exData as NSData).bytes, Int32(exData.count), nil)
373 | } else {
374 | sqlite3_bind_blob(stmt, 7, nil, 0, nil)
375 | }
376 |
377 | let result = sqlite3_step(stmt)
378 | if result != SQLITE_DONE {
379 | log("\(#function) line:(\(#line) sqlite insert error (\(result): (\(errorMessage))")
380 | return false
381 | }
382 | return true
383 | }
384 |
385 | @discardableResult
386 | fileprivate func dbUpdateAccessTime(_ key: String) -> Bool {
387 | let sql = "update manifest set last_access_time = ?1 where key = ?2;"
388 | guard let stmt = dbPrepareStmt(sql) else { return false }
389 | sqlite3_bind_int(stmt, 1, Int32(time(nil)))
390 | sqlite3_bind_text(stmt, 2, (key as NSString).utf8String, -1, nil)
391 | let result = sqlite3_step(stmt)
392 | if (result != SQLITE_DONE) {
393 | log("\(#function) line:(\(#line) sqlite update error (\(result): (\(errorMessage))")
394 | return false
395 | }
396 | return true
397 | }
398 |
399 | @discardableResult
400 | fileprivate func dbUpdateAccessTimes(_ keys: [String]) -> Bool {
401 | guard dbCheck() else { return false }
402 | let sql = "update manifest set last_access_time = \(Int32(time(nil))) where key in (\(dbJoinedKeys(keys)));"
403 | var stmtPointer: OpaquePointer?
404 | var result = sqlite3_prepare_v2(database, sql, -1, &stmtPointer, nil)
405 | guard result == SQLITE_OK, let stmt = stmtPointer else {
406 | log("\(#function) line:(\(#line) sqlite stmt prepare error (\(result): (\(errorMessage))")
407 | return false
408 | }
409 | dbBindJoinedKeys(keys: keys, stmt: stmt, fromIndex: 1)
410 | result = sqlite3_step(stmt)
411 | sqlite3_finalize(stmt)
412 | if (result != SQLITE_DONE) {
413 | log("\(#function) line:(\(#line) sqlite update error (\(result): (\(errorMessage))")
414 | return false
415 | }
416 | return true
417 | }
418 |
419 | @discardableResult
420 | fileprivate func dbDeleteItem(_ key: String) -> Bool {
421 | let sql = "delete from manifest where key = ?1;"
422 | guard let stmt = dbPrepareStmt(sql) else { return false }
423 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
424 | let result = sqlite3_step(stmt)
425 | if (result != SQLITE_DONE) {
426 | log("\(#function) line:(\(#line) sqlite delete error (\(result): (\(errorMessage))")
427 | return false
428 | }
429 | return true
430 | }
431 |
432 | fileprivate func dbDeleteItems(_ keys: [String]) -> Bool {
433 | guard dbCheck() else { return false }
434 | let sql = "delete from manifest where key in (\(dbJoinedKeys(keys));"
435 | var stmtPointer: OpaquePointer?
436 | var result = sqlite3_prepare_v2(database, sql, -1, &stmtPointer, nil)
437 | guard result == SQLITE_OK, let stmt = stmtPointer else {
438 | log("\(#function) line:(\(#line) sqlite stmt prepare error (\(result): (\(errorMessage))")
439 | return false
440 | }
441 | dbBindJoinedKeys(keys: keys, stmt: stmt, fromIndex: 1)
442 | result = sqlite3_step(stmt)
443 | sqlite3_finalize(stmt)
444 | if (result == SQLITE_ERROR) {
445 | log("\(#function) line:(\(#line) sqlite delete error (\(result): (\(errorMessage))")
446 | return false
447 | }
448 | return true
449 | }
450 |
451 | fileprivate func dbDeleteItem(sql: String, param: Int32) -> Bool {
452 | guard let stmt = dbPrepareStmt(sql) else { return false }
453 | sqlite3_bind_int(stmt, 1, param)
454 | let result = sqlite3_step(stmt)
455 | if (result != SQLITE_DONE) {
456 | log("\(#function) line:(\(#line) sqlite delete error (\(result): (\(errorMessage))")
457 | return false
458 | }
459 | return true
460 | }
461 |
462 | fileprivate func dbDeleteItemsWithSizeLargerThan(_ size: Int) -> Bool {
463 | return dbDeleteItem(sql: "delete from manifest where size > ?1;", param: Int32(size))
464 | }
465 |
466 | fileprivate func dbDeleteItemsWithTimeEarlierThan(_ time: Int) -> Bool {
467 | return dbDeleteItem(sql: "delete from manifest where last_access_time < ?1;", param: Int32(time))
468 | }
469 |
470 | fileprivate func dbGetItemFromStmt(stmt: OpaquePointer, excludeInlineData: Bool) -> KVStorageItem {
471 | let item = KVStorageItem()
472 | var index: Int32 = 0
473 | item.key = String(cString: UnsafePointer(sqlite3_column_text(stmt, index)))
474 | index += 1
475 | item.fileName = String(cString: UnsafePointer(sqlite3_column_text(stmt, index)))
476 | index += 1
477 | item.size = Int(sqlite3_column_int(stmt, index))
478 | index += 1
479 | let inlineData: UnsafeRawPointer? = excludeInlineData ? nil : sqlite3_column_blob(stmt, index)
480 | let inlineDataLength = excludeInlineData ? 0 : sqlite3_column_bytes(stmt, index)
481 | index += 1
482 | if inlineDataLength > 0 && (inlineData != nil) {
483 | item.value = NSData(bytes: inlineData, length: Int(inlineDataLength)) as Data
484 | }
485 | item.modTime = Int(sqlite3_column_int(stmt, index))
486 | index += 1
487 | item.accessTime = Int(sqlite3_column_int(stmt, index))
488 | index += 1
489 | let extendedData: UnsafeRawPointer? = sqlite3_column_blob(stmt, index)
490 | let extendedDataLength = sqlite3_column_bytes(stmt, index)
491 | if extendedDataLength > 0 && (extendedData != nil) {
492 | item.extendedData = NSData(bytes: extendedData, length: Int(extendedDataLength)) as Data
493 | }
494 | return item
495 | }
496 |
497 | fileprivate func dbGetItem(key: String, excludeInlineData: Bool) -> KVStorageItem? {
498 | let sql = excludeInlineData ? "select key, filename, size, modification_time, last_access_time, extended_data from manifest where key = ?1;"
499 | : "select key, filename, size, inline_data, modification_time, last_access_time, extended_data from manifest where key = ?1;"
500 | guard let stmt = dbPrepareStmt(sql) else { return nil }
501 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
502 | var item: KVStorageItem?
503 | let result = sqlite3_step(stmt)
504 | if (result == SQLITE_ROW) {
505 | item = dbGetItemFromStmt(stmt: stmt, excludeInlineData: excludeInlineData)
506 | } else {
507 | if (result != SQLITE_DONE) {
508 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
509 | }
510 | }
511 | return item
512 | }
513 |
514 | fileprivate func dbGetItems(keys: [String], excludeInlineData: Bool) -> [KVStorageItem]? {
515 | guard dbCheck() else { return nil }
516 | let sql: String
517 | if (excludeInlineData) {
518 | sql = "select key, filename, size, modification_time, last_access_time, extended_data from manifest where key in (\(dbJoinedKeys(keys)));"
519 | } else {
520 | sql = "select key, filename, size, inline_data, modification_time, last_access_time, extended_data from manifest where key in (\(dbJoinedKeys(keys))"
521 | }
522 | var stmtPointer: OpaquePointer?
523 | var result = sqlite3_prepare_v2(database, sql, -1, &stmtPointer, nil)
524 | guard result == SQLITE_OK, let stmt = stmtPointer else {
525 | log("\(#function) line:(\(#line) sqlite stmt prepare error (\(result): (\(errorMessage))")
526 | return nil
527 | }
528 | dbBindJoinedKeys(keys: keys, stmt: stmt, fromIndex: 1)
529 | var items: [KVStorageItem]? = [KVStorageItem]()
530 | repeat {
531 | result = sqlite3_step(stmt)
532 | if (result == SQLITE_ROW) {
533 | items?.append(dbGetItemFromStmt(stmt: stmt, excludeInlineData: excludeInlineData))
534 | } else if (result == SQLITE_DONE) {
535 | break
536 | } else {
537 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
538 | items = nil
539 | break
540 | }
541 | } while(true)
542 | sqlite3_finalize(stmt)
543 | return items
544 | }
545 |
546 | fileprivate func dbGetValue(key: String) -> Data? {
547 | let sql = "select inline_data from manifest where key = ?1;"
548 | guard let stmt = dbPrepareStmt(sql) else { return nil }
549 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
550 | let result = sqlite3_step(stmt)
551 | if (result == SQLITE_ROW) {
552 | let inlineData: UnsafeRawPointer? = sqlite3_column_blob(stmt, 0)
553 | let inlineDataLength = sqlite3_column_bytes(stmt, 0)
554 | guard inlineDataLength > 0 && (inlineData != nil) else {
555 | return nil
556 | }
557 | return NSData(bytes: inlineData, length: Int(inlineDataLength)) as Data
558 | } else {
559 | if (result != SQLITE_DONE) {
560 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
561 | }
562 | return nil
563 | }
564 | }
565 |
566 | fileprivate func dbGetFilename(key: String) -> String? {
567 | let sql = "select filename from manifest where key = ?1;"
568 | guard let stmt = dbPrepareStmt(sql) else { return nil }
569 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
570 | let result = sqlite3_step(stmt)
571 | if (result == SQLITE_ROW) {
572 | return String(cString: UnsafePointer(sqlite3_column_text(stmt, 0)))
573 | } else {
574 | if (result != SQLITE_DONE) {
575 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
576 | }
577 | return nil
578 | }
579 | }
580 |
581 | fileprivate func dbGetFileNames(keys: [String]) -> [String]? {
582 | guard dbCheck() else { return nil }
583 | let sql = "select filename from manifest where key in (\(dbJoinedKeys(keys)));"
584 | var stmtPointer: OpaquePointer?
585 | var result = sqlite3_prepare_v2(database, sql, -1, &stmtPointer, nil)
586 | guard result == SQLITE_OK, let stmt = stmtPointer else {
587 | log("\(#function) line:(\(#line) sqlite stmt prepare error (\(result): (\(errorMessage))")
588 | return nil
589 | }
590 | dbBindJoinedKeys(keys: keys, stmt: stmt, fromIndex: 1)
591 | var fileNames: [String]? = [String]()
592 | repeat {
593 | result = sqlite3_step(stmt)
594 | if (result == SQLITE_ROW) {
595 | fileNames?.append(String(cString: UnsafePointer(sqlite3_column_text(stmt, 0))))
596 | } else if (result == SQLITE_DONE) {
597 | break
598 | } else {
599 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
600 | fileNames = nil
601 | break
602 | }
603 | } while(true)
604 | sqlite3_finalize(stmt)
605 | return fileNames
606 | }
607 |
608 | fileprivate func dbGetFilenames(sql: String, param: Int32) -> [String]? {
609 | guard let stmt = dbPrepareStmt(sql) else { return nil }
610 | sqlite3_bind_int(stmt, 1, Int32(param))
611 | var fileNames: [String]? = [String]()
612 | repeat {
613 | let result = sqlite3_step(stmt)
614 | if (result == SQLITE_ROW) {
615 | fileNames?.append(String(cString: UnsafePointer(sqlite3_column_text(stmt, 0))))
616 | } else if (result == SQLITE_DONE) {
617 | break
618 | } else {
619 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
620 | fileNames = nil
621 | break
622 | }
623 | } while(true)
624 | sqlite3_finalize(stmt)
625 | return fileNames
626 | }
627 |
628 | fileprivate func dbGetFilenamesWithSizeLargerThan(_ size: Int) -> [String]? {
629 | let sql = "select filename from manifest where size > ?1 and filename is not null;"
630 | return dbGetFilenames(sql: sql, param: Int32(size))
631 | }
632 |
633 | fileprivate func dbGetFilenamesWithTimeEarlierThan(_ time: Int) -> [String]? {
634 | let sql = "select filename from manifest where last_access_time < ?1 and filename is not null;"
635 | return dbGetFilenames(sql: sql, param: Int32(time))
636 | }
637 |
638 | fileprivate func dbGetItemSizeInfoOrderByTimeAscWithLimit(count: Int) -> [KVStorageItem]? {
639 | let sql = "select key, filename, size from manifest order by last_access_time asc limit ?1;"
640 | guard let stmt = dbPrepareStmt(sql) else { return nil }
641 | var items: [KVStorageItem]? = [KVStorageItem]()
642 | repeat {
643 | let result = sqlite3_step(stmt)
644 | if (result == SQLITE_ROW) {
645 | let item = KVStorageItem()
646 | item.key = String(cString: UnsafePointer(sqlite3_column_text(stmt, 0)))
647 | item.fileName = String(cString: UnsafePointer(sqlite3_column_text(stmt, 1)))
648 | item.size = Int(sqlite3_column_int(stmt, 2))
649 | items?.append(item)
650 | } else if (result == SQLITE_DONE) {
651 | break
652 | } else {
653 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
654 | items = nil
655 | break
656 | }
657 | } while(true)
658 | sqlite3_finalize(stmt)
659 | return items
660 | }
661 |
662 | fileprivate func dbGetItemCount(key: String) -> Int {
663 | let sql = "select count(key) from manifest where key = ?1;"
664 | guard let stmt = dbPrepareStmt(sql) else { return -1 }
665 | sqlite3_bind_text(stmt, 1, (key as NSString).utf8String, -1, nil)
666 | let result = sqlite3_step(stmt)
667 | if result != SQLITE_ROW {
668 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
669 | return -1
670 | }
671 | return Int(sqlite3_column_int(stmt, 0))
672 | }
673 |
674 | fileprivate func dbGetInt(_ sql: String) -> Int {
675 | guard let stmt = dbPrepareStmt(sql) else { return -1 }
676 | let result = sqlite3_step(stmt)
677 | if result != SQLITE_ROW {
678 | log("\(#function) line:(\(#line) sqlite query error (\(result): (\(errorMessage))")
679 | return -1
680 | }
681 | return Int(sqlite3_column_int(stmt, 0))
682 | }
683 |
684 | fileprivate func dbGetTotalItemSize() -> Int {
685 | return dbGetInt("select sum(size) from manifest;")
686 | }
687 |
688 | fileprivate func dbGetTotalItemCount() -> Int {
689 | return dbGetInt("select count(*) from manifest;")
690 | }
691 |
692 | // MARK: Public
693 | public func saveItem(key: String, value: Data, fileName: String?, extendedData: Data?) -> Bool {
694 | guard !key.isEmpty, !value.isEmpty else { return false }
695 | if type == .file && fileName?.isEmpty ?? true { return false }
696 | if let file = fileName, !file.isEmpty {
697 | if !fileWrite(fileName: file, data: value) {
698 | return false
699 | }
700 | if !dbSave(key: key, value: value, fileName: file, extendedData: extendedData) {
701 | fileDelete(fileName: file)
702 | return false
703 | }
704 | return true
705 | } else {
706 | if type != .sqlite {
707 | if let file = dbGetFilename(key: key) {
708 | fileDelete(fileName: file)
709 | }
710 | }
711 | return dbSave(key: key, value: value, fileName: nil, extendedData: extendedData)
712 | }
713 | }
714 |
715 | public func removeItem(key: String) -> Bool {
716 | guard !key.isEmpty else { return false }
717 | switch type {
718 | case .sqlite:
719 | return dbDeleteItem(key)
720 | case .file, .mixed:
721 | if let fileName = dbGetFilename(key: key) {
722 | fileDelete(fileName: fileName)
723 | }
724 | return dbDeleteItem(key)
725 | }
726 | }
727 |
728 | public func removeItems(keys: [String]) -> Bool {
729 | guard !keys.isEmpty else { return false }
730 | switch type {
731 | case .sqlite:
732 | return dbDeleteItems(keys)
733 | case .file, .mixed:
734 | if let fileNames = dbGetFileNames(keys: keys), !fileNames.isEmpty {
735 | for file in fileNames {
736 | fileDelete(fileName: file)
737 | }
738 | }
739 | return dbDeleteItems(keys)
740 | }
741 | }
742 |
743 | public func removeAllItems() -> Bool {
744 | guard dbClose() else { return false }
745 | reset()
746 | guard dbOpen() else { return false }
747 | guard dbInitialize() else { return false }
748 | return true
749 | }
750 |
751 | public func removeItemsLargerThanSize(_ size: Int) -> Bool {
752 | guard size != Int.max else { return true }
753 | guard size > 0 else { return removeAllItems() }
754 | switch type {
755 | case .sqlite:
756 | if dbDeleteItemsWithSizeLargerThan(size) {
757 | dbCheckpoint()
758 | return true
759 | }
760 | case .file, .mixed:
761 | if let fileNames = dbGetFilenamesWithSizeLargerThan(size) {
762 | for file in fileNames {
763 | fileDelete(fileName: file)
764 | }
765 | }
766 | if dbDeleteItemsWithSizeLargerThan(size) {
767 | dbCheckpoint()
768 | return true
769 | }
770 | }
771 | return false
772 | }
773 |
774 | public func removeItemsEarlierThanTime(_ time: Int) -> Bool {
775 | guard time > 0 else { return true }
776 | guard time != Int.max else { return removeAllItems() }
777 | switch type {
778 | case .sqlite:
779 | if dbDeleteItemsWithTimeEarlierThan(time) {
780 | dbCheckpoint()
781 | return true
782 | }
783 | case .file, .mixed:
784 | if let fileNames = dbGetFilenamesWithTimeEarlierThan(time) {
785 | for file in fileNames {
786 | fileDelete(fileName: file)
787 | }
788 | }
789 | if dbDeleteItemsWithTimeEarlierThan(time) {
790 | dbCheckpoint()
791 | return true
792 | }
793 | }
794 | return false
795 | }
796 |
797 | public func removeItemsToFitSize(_ maxSize: Int) -> Bool {
798 | guard maxSize != Int.max else { return true }
799 | guard maxSize > 0 else { return removeAllItems() }
800 | var total = dbGetTotalItemSize()
801 | guard total >= 0 else { return false }
802 | guard total > maxSize else { return true }
803 | var suc = false
804 | dbGetItemSizeInfoOrderByTimeAscWithLimit(count: 16)?.forEach({ (item) in
805 | if let fileName = item.fileName {
806 | fileDelete(fileName: fileName)
807 | }
808 | if let key = item.key {
809 | suc = dbDeleteItem(key)
810 | } else {
811 | suc = true
812 | }
813 | total -= item.size
814 | if total <= maxSize || !suc {
815 | return
816 | }
817 | })
818 | if suc {
819 | dbCheckpoint()
820 | }
821 | return suc
822 | }
823 |
824 | public func removeItemsToFitCount(_ maxCount: Int) -> Bool {
825 | guard maxCount != Int.max else { return true }
826 | guard maxCount > 0 else { return removeAllItems() }
827 | var total = dbGetTotalItemCount()
828 | guard total >= 0 else { return false }
829 | guard total > maxCount else { return true }
830 | var suc = false
831 | dbGetItemSizeInfoOrderByTimeAscWithLimit(count: 16)?.forEach({ (item) in
832 | if let fileName = item.fileName {
833 | fileDelete(fileName: fileName)
834 | }
835 | if let key = item.key {
836 | suc = dbDeleteItem(key)
837 | } else {
838 | suc = true
839 | }
840 | total -= 1
841 | if total <= maxCount || !suc {
842 | return
843 | }
844 | })
845 | if suc {
846 | dbCheckpoint()
847 | }
848 | return suc
849 | }
850 |
851 | public func removeAllItemsWithProgressBlock(progress: ((_ removedCount: Int, _ totalCount: Int) -> Void)?,
852 | end: ((_ error: Bool) -> Void)?) {
853 | let total = dbGetTotalItemCount()
854 | if total <= 0 {
855 | end?(total < 0)
856 | } else {
857 | var left = total
858 | var suc = false
859 | dbGetItemSizeInfoOrderByTimeAscWithLimit(count: 32)?.forEach({ (item) in
860 | if let fileName = item.fileName {
861 | fileDelete(fileName: fileName)
862 | }
863 | if let key = item.key {
864 | suc = dbDeleteItem(key)
865 | } else {
866 | suc = true
867 | }
868 | left -= 1
869 | if left <= 0 || !suc {
870 | return
871 | }
872 | progress?(total - left, total)
873 | })
874 | if suc {
875 | dbCheckpoint()
876 | }
877 | end?(!suc)
878 | }
879 | }
880 |
881 | public func getItemForKey(_ key: String) -> KVStorageItem? {
882 | guard !key.isEmpty else { return nil }
883 | guard let item = dbGetItem(key: key, excludeInlineData: false) else { return nil }
884 | dbUpdateAccessTime(key)
885 | if let fileName = item.fileName {
886 | if let value = fileRead(fileName: fileName) {
887 | item.value = value
888 | } else {
889 | dbDeleteItem(key)
890 | return nil
891 | }
892 | }
893 | return item
894 | }
895 |
896 | public func getItemInfoForKey(_ key: String) -> KVStorageItem? {
897 | guard !key.isEmpty else { return nil }
898 | return dbGetItem(key: key, excludeInlineData: true)
899 | }
900 |
901 | public func getItemValueForKey(_ key: String) -> Data? {
902 | guard !key.isEmpty else { return nil }
903 | var value: Data?
904 | switch type {
905 | case .file:
906 | if let fileName = dbGetFilename(key: key) {
907 | value = fileRead(fileName: fileName)
908 | if value == nil {
909 | dbDeleteItem(key)
910 | }
911 | }
912 | case .sqlite:
913 | value = dbGetValue(key: key)
914 | case .mixed:
915 | if let fileName = dbGetFilename(key: key) {
916 | value = fileRead(fileName: fileName)
917 | if value == nil {
918 | dbDeleteItem(key)
919 | }
920 | } else {
921 | value = dbGetValue(key: key)
922 | }
923 | }
924 | if value != nil {
925 | dbUpdateAccessTime(key)
926 | }
927 | return value
928 | }
929 |
930 | public func getItemForKeys(_ keys: [String]) -> [KVStorageItem]? {
931 | guard !keys.isEmpty else { return nil }
932 | if var items = dbGetItems(keys: keys, excludeInlineData: false), !items.isEmpty {
933 | if type == .sqlite {
934 | var index = 0
935 | var max = items.count
936 | repeat {
937 | let item = items[index]
938 | if let fileName = item.fileName {
939 | if let value = fileRead(fileName: fileName) {
940 | item.value = value
941 | } else {
942 | if let key = item.key {
943 | dbDeleteItem(key)
944 | }
945 | items.remove(at: index)
946 | index -= 1
947 | max -= 1
948 | }
949 | }
950 | index += 1
951 | } while(index < max)
952 | }
953 | return items.isEmpty ? nil : items
954 | } else {
955 | return nil
956 | }
957 | }
958 |
959 | public func getItemInfoForKeys(_ keys: [String]) -> [KVStorageItem]? {
960 | guard !keys.isEmpty else { return nil }
961 | return dbGetItems(keys: keys, excludeInlineData: true)
962 | }
963 |
964 | public func getItemValueForKeys(_ keys: [String]) -> [String: Any]? {
965 | guard let items = getItemForKeys(keys) else { return nil }
966 | var keyAndValue = [String: Any]()
967 | for item in items {
968 | if let key = item.key, let value = item.value {
969 | keyAndValue[key] = value
970 | }
971 | }
972 | return keyAndValue.isEmpty ? nil : keyAndValue
973 | }
974 |
975 | public func itemExistsForKey(_ key: String) -> Bool {
976 | guard !key.isEmpty else { return false }
977 | return dbGetItemCount(key: key) > 0
978 | }
979 |
980 | public func getItemsCount() -> Int {
981 | return dbGetTotalItemCount()
982 | }
983 |
984 | public func getItemsSize() -> Int {
985 | return dbGetTotalItemSize()
986 | }
987 | }
988 |
--------------------------------------------------------------------------------
/Sources/ClaretCache/MemoryCache.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MemoryCache.swift
3 | // ClaretCache
4 | //
5 | // Created by BirdMichael on 2019/7/23.
6 | // Copyright © 2019 com.ClaretCache. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | #if canImport(UIKit)
12 | import UIKit.UIApplication
13 | #endif
14 |
15 | #if canImport(QuartzCore)
16 | import QuartzCore.CABase
17 | #endif
18 |
19 | public final class MemoryCache where Key: Hashable, Value: Equatable {
20 |
21 | /// The name of the cache. **Default** is **"com.iteatimeteam.ClaretCache.memory.default"**
22 | let name: String
23 |
24 | /// The maximum number of objects the cache should hold.
25 | ///
26 | /// The default value is **UInt.max**, which means no limit.
27 | /// This is not a strict limit—if the cache goes over the limit, some objects in the
28 | /// cache could be evicted later in backgound thread.
29 | var countLimit: UInt = UInt.max
30 |
31 | /// The maximum total cost that the cache can hold before it starts evicting objects.
32 | ///
33 | /// The default value is **UInt.max**, which means no limit.
34 | /// This is not a strict limit—if the cache goes over the limit, some objects in the
35 | /// cache could be evicted later in backgound thread.
36 | var costLimit: UInt = UInt.max
37 |
38 | /// The maximum expiry time of objects in cache.
39 | ///
40 | /// The default value is **Double.greatestFiniteMagnitude**, which means no limit.
41 | /// This is not a strict limit—if an object goes over the limit, the object could
42 | /// be evicted later in backgound thread.
43 | var ageLimit: TimeInterval = TimeInterval(Double.greatestFiniteMagnitude)
44 |
45 | /// The auto trim check time interval in seconds. **Default is 5.0**.
46 | ///
47 | /// The cache holds an internal timer to check whether the cache reaches
48 | /// its limits, and if the limit is reached, it begins to evict objects.
49 | var autoTrimInterval: TimeInterval = 5.0
50 |
51 | #if canImport(UIKit)
52 | /// If **YES**, the cache will remove all objects when the app receives a memory warning.
53 | /// The **default** value is **YES**.
54 | var removeAllObjectsOnMemoryWarning: Bool = true
55 |
56 | /// If **YES**, The cache will remove all objects when the app enter background.
57 | /// The **default** value is **YES**.
58 | var removeAllObjectsWhenEnteringBackground: Bool = true
59 |
60 | /// A closure to be executed when the app receives a memory warning.
61 | /// **The default value is nil**.
62 | var didReceiveMemoryWarning: ((MemoryCache) -> Void)?
63 |
64 | /// A closure to be executed when the app enter background.
65 | /// **The default value is nil**.
66 | var didEnterBackground: ((MemoryCache) -> Void)?
67 |
68 | private var observers: [NSObjectProtocol] = []
69 | #endif
70 |
71 | /// If **YES**, the key-value pair will be released on main thread, otherwise on background thread.
72 | /// **Default** is **NO**.
73 | ///
74 | /// You may set this value to **YES** if the key-value object contains
75 | /// **the instance which should be released in main thread (such as UIView/CALayer)**.
76 | var releaseOnMainThread: Bool {
77 | set {
78 | lock { lru.releaseOnMainThread = newValue }
79 | }
80 | get {
81 | return lockRead { lru.releaseOnMainThread }
82 | }
83 | }
84 |
85 | /// If **YES**, the key-value pair will be released asynchronously to avoid blocking
86 | ///
87 | /// the access methods, otherwise it will be released in the access method
88 | /// (such as removeObjectForKey:). **Default is YES**.
89 | var releaseAsynchronously: Bool {
90 | set {
91 | lock { lru.releaseAsynchronously = newValue }
92 | }
93 | get {
94 | return self.lockRead { self.lru.releaseAsynchronously }
95 | }
96 | }
97 |
98 | /// The number of objects in the cache (read-only)
99 | var totalCount: UInt {
100 | return self.lockRead { self.lru.totalCount }
101 | }
102 |
103 | /// The total cost of objects in the cache (read-only).
104 | var totalCost: UInt {
105 | return self.lockRead { self.lru.totalCost }
106 | }
107 |
108 | private var mutex: pthread_mutex_t
109 | private let lru: LinkedMap = LinkedMap()
110 | private let queue: DispatchQueue
111 |
112 | /// The constructor
113 | /// - Parameter name: The name of the cache.
114 | /// The default value is **"com.iteatimeteam.ClaretCache.memory.default"**
115 | /// - Parameter costLimit: The maximum total cost that the cache can hold before it starts evicting objects.
116 | /// The default value is **UInt.max**, which means no limit.
117 | /// - Parameter countLimit: The maximum number of objects the cache should hold.
118 | /// The default value is **UInt.max**, which means no limit.
119 | /// - Parameter ageLimit: The maximum expiry time of objects in cache.
120 | /// The default value is **Double.greatestFiniteMagnitude**, which means no limit.
121 | /// - Parameter autoTrimInterval: The auto trim check time interval in seconds.
122 | /// The default value is **5.0**.
123 | init(name: String = "com.iteatimeteam.ClaretCache.memory.default",
124 | costLimit: UInt = UInt.max,
125 | countLimit: UInt = UInt.max,
126 | ageLimit: TimeInterval = TimeInterval(Double.greatestFiniteMagnitude),
127 | autoTrimInterval: TimeInterval = 5.0) {
128 |
129 | self.name = name
130 | self.costLimit = costLimit
131 | self.countLimit = countLimit
132 | self.ageLimit = ageLimit
133 | self.autoTrimInterval = autoTrimInterval
134 |
135 | mutex = .init()
136 | pthread_mutex_init(&mutex, nil)
137 | queue = DispatchQueue(label: "com.iteatimeteam.ClaretCache.memory", qos: DispatchQoS.default)
138 | #if canImport(UIKit)
139 | addNotification()
140 | #endif
141 | trimRecursively()
142 | }
143 |
144 | deinit {
145 | #if canImport(UIKit)
146 | removeNotification()
147 | #endif
148 | lru.removeAll()
149 | pthread_mutex_destroy(&mutex)
150 | }
151 | }
152 |
153 | // MARK: - Access Methods
154 | ///=============================================================================
155 | /// @name Access Methods
156 | ///=============================================================================
157 | public extension MemoryCache {
158 |
159 | /// Returns a Boolean value that indicates whether a given key is in cache.
160 | /// - Parameter atKey: atKey An object identifying the value. If nil, just return **NO**.
161 | /// - Returns: Whether the atKey is in cache.
162 | final func contains(_ atKey: Key) -> Bool {
163 | return lockRead { lru.dic[atKey] != nil }
164 | }
165 |
166 | /// Sets the value of the specified key in the cache, and associates the key-value
167 | /// pair with the specified cost.
168 | /// - Parameter value: The object to store in the cache. If nil, it calls **remove(_ atKey:)**.
169 | /// - Parameter atKey: The atKey with which to associate the value. If nil, this method has no effect.
170 | /// - Parameter cost: The cost with which to associate the key-value pair.
171 | final func set(_ value: Value?, _ atKey: Key, cost: UInt = 0) {
172 | // Delete new if value is nil
173 | // Cache if value is not nil
174 | guard let value = value else {
175 | remove(atKey)
176 | return
177 | }
178 |
179 | lock {
180 | let now = currentTime()
181 | if let node = lru.dic[atKey] {
182 | lru.totalCost -= node.cost
183 | lru.totalCount += cost
184 | node.cost = cost
185 | node.time = now
186 | node.value = value
187 | lru.bring(toHead: node)
188 | } else {
189 | let node = LinkedMap.Node(atKey: atKey, value: value, cost: cost)
190 | node.time = now
191 | lru.insert(atHead: node)
192 | }
193 |
194 | if lru.totalCost > costLimit {
195 | queue.async {
196 | self.trimTo(cost: self.costLimit)
197 | }
198 | }
199 |
200 | if lru.totalCount > countLimit {
201 | if let node = lru.removeTail() {
202 | release {
203 | //hold and release in queue
204 | _ = node.cost
205 | }
206 | }
207 | }
208 | }
209 | }
210 |
211 | /// Removes the value of the specified key in the cache.
212 | /// - Parameter atKey: atKey The atKey identifying the value to be removed.
213 | /// If nil, this method has no effect.
214 | final func remove(_ atKey: Key) {
215 | pthread_mutex_lock(&mutex)
216 | defer {
217 | pthread_mutex_unlock(&mutex)
218 | }
219 | guard let node = lru.dic[atKey] else { return }
220 | lru.remove(node)
221 | release {
222 | _ = node.cost //hold and release in queue
223 | }
224 | }
225 |
226 | /// Empties the cache immediately.
227 | final func removeAll() {
228 | lock {
229 | lru.removeAll()
230 | }
231 | }
232 |
233 | final subscript(_ atKey: Key) -> Value? {
234 | get {
235 | return lockRead { () -> Value? in
236 | guard let node = lru.dic[atKey] else {
237 | return nil
238 | }
239 | node.time = currentTime()
240 | lru.bring(toHead: node)
241 | return node.value
242 | }
243 | }
244 | set {
245 | set(newValue, atKey)
246 | }
247 | }
248 |
249 | /// Removes objects from the cache with LRU,
250 | /// until the **totalCount** is below or equal to the specified value.
251 | /// - Parameter count: The total count allowed to remain after the cache has been trimmed.
252 | final func trimTo(count: UInt) {
253 | guard count > .zero else {
254 | removeAll()
255 | return
256 | }
257 |
258 | trimCount(count)
259 | }
260 |
261 | /// Removes objects from the cache with LRU, until the **totalCost** is or equal to the specified value.
262 | /// - Parameter cost: cost The total cost allowed to remain after the cache has been trimmed.
263 | final func trimTo(cost: UInt) {
264 | trimCost(cost)
265 | }
266 |
267 | /// Removes objects from the cache with LRU, until all expiry objects removed by the specified value.
268 | /// - Parameter age: The maximum age (in seconds) of objects.
269 | final func trimTo(age: TimeInterval) {
270 | trimAge(age)
271 | }
272 | }
273 |
274 | extension MemoryCache: CustomDebugStringConvertible {
275 | public var debugDescription: String {
276 | return "<\(type(of: self)): \(Unmanaged.passUnretained(self).toOpaque())> (\(name))"
277 | }
278 | }
279 |
280 | private extension MemoryCache {
281 |
282 | #if canImport(UIKit)
283 | func addNotification() {
284 |
285 | let memoryWarningObserver = NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: nil) { [weak self] _ in
286 | guard let self = self else { return }
287 | self.didReceiveMemoryWarning?(self)
288 | if self.removeAllObjectsOnMemoryWarning {
289 | self.removeAll()
290 | }
291 | }
292 |
293 | let enterBackgroundObserver = NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
294 | guard let self = self else { return }
295 | self.didEnterBackground?(self)
296 | if self.removeAllObjectsWhenEnteringBackground {
297 | self.removeAll()
298 | }
299 | }
300 | observers.append(contentsOf: [memoryWarningObserver, enterBackgroundObserver])
301 | }
302 |
303 | func removeNotification() {
304 | observers.forEach {
305 | NotificationCenter.default.removeObserver($0)
306 | }
307 | observers.removeAll()
308 | }
309 | #endif
310 |
311 | final func trimLRU(path: KeyPath, UInt>, limit: UInt) {
312 |
313 | var finish = lockRead { () -> Bool in
314 | if limit == .zero {
315 | lru.removeAll()
316 | return true
317 | } else if lru[keyPath: path] <= limit {
318 | return true
319 | }
320 | return false
321 | }
322 | guard !finish else { return }
323 |
324 | var holder: [LinkedMap.Node] = []
325 |
326 | while !finish {
327 | if pthread_mutex_trylock(&mutex) == .zero {
328 | if lru[keyPath: path] > limit {
329 | if let node = lru.removeTail() {
330 | holder.append(node)
331 | }
332 | } else {
333 | finish = true
334 | }
335 | pthread_mutex_unlock(&mutex)
336 | } else {
337 | usleep(10 * 1000) //10 ms
338 | }
339 | }
340 |
341 | guard !holder.isEmpty else { return }
342 |
343 | release(trimed: true) {
344 | _ = holder.isEmpty // release in queue
345 | }
346 | }
347 |
348 | final func trimCost(_ costLimit: UInt) {
349 | trimLRU(path: \.totalCost, limit: costLimit)
350 | }
351 |
352 | final func trimCount(_ countLimit: UInt) {
353 | trimLRU(path: \.totalCount, limit: countLimit)
354 | }
355 |
356 | final func trimAge(_ ageLimit: TimeInterval) {
357 | let now = currentTime()
358 | var finish = lockRead { () -> Bool in
359 | if ageLimit <= .zero {
360 | lru.removeAll()
361 | return true
362 | } else if lru.tail == nil || (now - (lru.tail?.time ?? 0)) <= ageLimit {
363 | return true
364 | }
365 | return false
366 | }
367 | guard !finish else { return }
368 |
369 | var holder: [LinkedMap.Node] = []
370 |
371 | while !finish {
372 | if pthread_mutex_trylock(&mutex) == .zero {
373 | if let tail = lru.tail, (now - tail.time) > ageLimit {
374 | if let node = lru.removeTail() {
375 | holder.append(node)
376 | }
377 | } else {
378 | finish = true
379 | }
380 | pthread_mutex_unlock(&mutex)
381 | } else {
382 | usleep(10 * 1000) //10 ms
383 | }
384 | }
385 |
386 | guard !holder.isEmpty else { return }
387 |
388 | release(trimed: true) {
389 | _ = holder.isEmpty // release in queue
390 | }
391 | }
392 |
393 | final func trimRecursively() {
394 | memoryCacheReleaseQueue().asyncAfter(deadline: .now() + autoTrimInterval) { [weak self] in
395 | guard let self = self else { return }
396 | self.trimInBackground()
397 | self.trimRecursively()
398 | }
399 | }
400 |
401 | final func trimInBackground() {
402 | queue.async { [weak self] in
403 | guard let self = self else { return }
404 | self.trimTo(cost: self.costLimit)
405 | self.trimTo(count: self.countLimit)
406 | self.trimTo(age: self.ageLimit)
407 | }
408 | }
409 |
410 | final func lock(execute: (() -> Void)) {
411 | pthread_mutex_lock(&mutex)
412 | defer {
413 | pthread_mutex_unlock(&mutex)
414 | }
415 | execute()
416 | }
417 |
418 | final func lockRead(execute: (() -> V)) -> V {
419 | pthread_mutex_lock(&mutex)
420 | defer {
421 | pthread_mutex_unlock(&mutex)
422 | }
423 | return execute()
424 | }
425 |
426 | final func release(trimed: Bool = false, _ execute: @escaping (() -> Void)) {
427 | if trimed {
428 | let queue = lru.releaseOnMainThread ? DispatchQueue.main : memoryCacheReleaseQueue()
429 | queue.async(execute: execute)
430 | } else {
431 | if lru.releaseAsynchronously {
432 | let queue = lru.releaseOnMainThread ? DispatchQueue.main : memoryCacheReleaseQueue()
433 | queue.async(execute: execute)
434 | } else if lru.releaseOnMainThread && pthread_main_np() != .zero {
435 | DispatchQueue.main.async(execute: execute)
436 | }
437 | }
438 | }
439 |
440 | final func currentTime() -> TimeInterval {
441 | #if canImport(QuartzCore)
442 | return CACurrentMediaTime()
443 | #else
444 | return Date().timeIntervalSince1970
445 | #endif
446 | }
447 | }
448 |
449 | private func memoryCacheReleaseQueue() -> DispatchQueue {
450 | return DispatchQueue.global(qos: .utility)
451 | }
452 |
453 | /**
454 | A linked map used by MemoryCache.
455 | It's not thread-safe and does not validate the parameters.
456 |
457 | Typically, you should not use this class directly.
458 | */
459 | fileprivate final class LinkedMap where Key: Hashable, Value: Equatable {
460 | var dic: [Key: Node] = [:]
461 | var totalCost: UInt = 0
462 | var totalCount: UInt = 0
463 | var head: Node? // MRU, do not change it directly
464 | var tail: Node? // LRU, do not change it directly
465 | var releaseOnMainThread: Bool = false
466 | var releaseAsynchronously: Bool = true
467 |
468 | final class Node: Equatable where Key: Hashable, Value: Equatable {
469 | var prev: Node?
470 | var next: Node?
471 | let key: Key
472 | var value: Value?
473 | var cost: UInt = 0
474 | var time: TimeInterval = 0.0
475 |
476 | init(atKey: Key, value: Value?, cost: UInt = 0) {
477 | self.key = atKey
478 | self.value = value
479 | self.cost = cost
480 | }
481 |
482 | static func == (lhs: Node, rhs: Node) -> Bool {
483 | return lhs.key == rhs.key && lhs.value == rhs.value
484 | }
485 |
486 | #if ClaretCacheLOG
487 | deinit {
488 | print("[ClaretCache LOG]: cache for key: \(key) release in \(Thread.current)")
489 | }
490 | #endif
491 | }
492 | }
493 |
494 | extension LinkedMap {
495 |
496 | /// Insert a node at head and update the total cost.
497 | /// Node and node.key should not be nil.
498 | final func insert(atHead node: Node) {
499 | dic[node.key] = node
500 | totalCost += node.cost
501 | totalCount += 1
502 | if head != nil {
503 | node.next = head
504 | head?.prev = node
505 | head = node
506 | } else {
507 | tail = node
508 | head = tail
509 | }
510 | }
511 |
512 | /// Bring a inner node to header.
513 | /// Node should already inside the dic.
514 | final func bring(toHead node: Node) {
515 | guard head != node else { return }
516 |
517 | if tail == node {
518 | tail = node.prev
519 | tail?.next = nil
520 | } else {
521 | node.next?.prev = node.prev
522 | node.prev?.next = node.next
523 | }
524 |
525 | node.next = head
526 | node.prev = nil
527 | head?.prev = node
528 | head = node
529 | }
530 |
531 | /// Remove a inner node and update the total cost.
532 | /// Node should already inside the dic.
533 | final func remove(_ node: Node) {
534 | dic.removeValue(forKey: node.key)
535 | totalCost -= node.cost
536 | totalCount -= 1
537 |
538 | if node.next != nil {
539 | node.next?.prev = node.prev
540 | }
541 |
542 | if node.prev != nil {
543 | node.prev?.next = node.next
544 | }
545 |
546 | if head == node {
547 | head = node.next
548 | }
549 |
550 | if tail == node {
551 | tail = node.prev
552 | }
553 | }
554 |
555 | /// Remove tail node if exist.
556 | final func removeTail() -> Node? {
557 | guard let tmpTail = tail else { return nil }
558 | dic.removeValue(forKey: tmpTail.key)
559 | totalCost -= tmpTail.cost
560 | totalCount -= 1
561 | if head == tail {
562 | head = nil
563 | tail = nil
564 | } else {
565 | tail = tail?.prev
566 | tail?.next = nil
567 | }
568 | return tmpTail
569 | }
570 |
571 | /// Remove all node
572 | final func removeAll() {
573 | totalCost = 0
574 | totalCount = 0
575 | head = nil
576 | tail = nil
577 | guard !dic.isEmpty else { return }
578 |
579 | let holder: [Key: Node] = dic
580 | dic = [:]
581 | if releaseAsynchronously {
582 | let queue = releaseOnMainThread ? DispatchQueue.main : memoryCacheReleaseQueue()
583 | queue.async {
584 | // hold and release in specified queue
585 | _ = holder.count
586 | }
587 | } else if releaseOnMainThread && pthread_main_np() != .zero {
588 | DispatchQueue.main.async {
589 | // hold and release in specified queue
590 | _ = holder.count
591 | }
592 | } else {
593 | // nothing
594 | }
595 | }
596 | }
597 |
--------------------------------------------------------------------------------
/Swift_Style_Guide.md:
--------------------------------------------------------------------------------
1 | # ClaretCache Swift代码选型规范
2 |
3 | ## 目标
4 |
5 | 本规约旨在:
6 |
7 | * 使代码易读,易理解, 易维护.
8 | * 减少编写代码时的认知负担.
9 | * 使项目成员能更专注讨论代码逻辑而不是代码写法.
10 |
11 |
12 | ## 指导原则
13 |
14 | * 本规约是 [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) 的扩展,规约内容不应该和官方文档相抵触.
15 | * 如果规约修改了代码格式, 则需要重新自动格式化(使用SwiftLint)
16 |
17 |
18 | ## 目录
19 |
20 | 1. [Xcode 配置](#xcode-配置)
21 | 1. [命名](#命名)
22 | 1. [风格](#风格)
23 | 1. [函数](#函数)
24 | 1. [闭包](#)
25 | 1. [操作符](#操作符)
26 | 1. [最佳实践](#最佳实践)
27 | 1. [与Objc的交互](#与Objc的交互)
28 |
29 | ## Xcode 配置
30 |
31 | * **每行的最大列宽应为100个字符.** (考虑到外接大屏显示器, 我们选择最大列宽超过80个字符)
32 |
33 | * **每行使用2个空格缩进.**
34 |
35 | * **删除所有行尾的空白字符.**
36 |
37 | **[⬆ 返回顶部](#目录)**
38 |
39 | ## 命名
40 |
41 | * **对于类型(值类型或引用类型)和协议, 使用大写驼峰命名法; 其余则使用小写驼峰命名法.**
42 |
43 |
44 | 示例
45 |
46 | ```swift
47 | protocol Person {
48 | // ...
49 | }
50 |
51 | class Teacher: Person {
52 |
53 | enum Gender {
54 | // ...
55 | }
56 |
57 | class Course {
58 | // ...
59 | }
60 |
61 | var courses: [Course] = []
62 | static let schoolName: String = "Stanford"
63 |
64 | func addCourse(_ course: Course) {
65 | // ...
66 | }
67 | }
68 |
69 | let teacher = Teacher()
70 | ```
71 |
72 |
73 |
74 | _特例: 给自定义私有属性添加下划线前缀. 当自定义的属性和系统属性冲突时, 并且需要赋予自定义属性更高的权限时.
75 |
76 |
77 | 示例
78 |
79 | ```swift
80 | class DemoViewController: UIViewController {
81 | private lazy var _view = CustomView()
82 |
83 | loadView() {
84 | self.view = _view
85 | }
86 | }
87 | ```
88 |
89 |
90 |
91 | * **给布尔值命名时请参考 `isTeacher`, `hasCourse`这样的命名方式.** 该方式能更好地体现其是一个布尔类型, 而非其他类型.
92 |
93 | * **名称中的缩略语(例如URL),除非该缩略语是名称的开头,否则一律使用大写.** *(名称: 变量名,函数名)*
94 |
95 |
96 | 示例
97 |
98 | ```swift
99 | //错误示范
100 | class UrlValidator {
101 |
102 | func isValidUrl(_ URL: URL) -> Bool {
103 | // ...
104 | }
105 |
106 | func isUrlReachable(_ URL: URL) -> Bool {
107 | // ...
108 | }
109 | }
110 |
111 | let URLValidator = UrlValidator().isValidUrl(/* some URL */)
112 |
113 | // 正确示范
114 | class URLValidator {
115 |
116 | func isValidURL(_ url: URL) -> Bool {
117 | // ...
118 | }
119 |
120 | func isURLReachable(_ url: URL) -> Bool {
121 | // ...
122 | }
123 | }
124 |
125 | let urlValidator = URLValidator().isValidURL(/* some URL */)
126 | ```
127 |
128 |
129 |
130 | * **名称应该明确体现其功能. 从左至右顺序应为: 常见部分到具体部分.** 常见部分是指: 最能帮助我们锁定目标的类型名词, 一般粒度较大; 具体部分是指: 粒度最小的部分。
131 |
132 |
133 | 示例
134 |
135 | ```swift
136 | // 错误示范
137 | let rightTitleMargin: CGFloat
138 | let leftTitleMargin: CGFloat
139 | let bodyRightMargin: CGFloat
140 | let bodyLeftMargin: CGFloat
141 |
142 | // 正确示范
143 | let titleMarginRight: CGFloat
144 | let titleMarginLeft: CGFloat
145 | let bodyMarginRight: CGFloat
146 | let bodyMarginLeft: CGFloat
147 | ```
148 |
149 |
150 |
151 | * **如果名称不明确,请在名称中包含有关类型的提示.**
152 |
153 |
154 | 示例
155 |
156 | ```swift
157 | // 错误示范
158 | let title: String
159 | let cancel: UIButton
160 |
161 | // 正确示范
162 | let titleText: String
163 | let cancelButton: UIButton
164 | ```
165 |
166 |
167 |
168 | * **事件处理函数命名使用过去时.**
169 |
170 |
171 | 示例
172 |
173 | ```swift
174 | // 错误示例
175 | class ExperiencesViewController {
176 |
177 | private func handleBookButtonTap() {
178 | // ...
179 | }
180 |
181 | private func modelChanged() {
182 | // ...
183 | }
184 | }
185 |
186 | // 正确示例
187 | class ExperiencesViewController {
188 |
189 | private func didTapBookButton() {
190 | // ...
191 | }
192 |
193 | private func modelDidChange() {
194 | // ...
195 | }
196 | }
197 | ```
198 |
199 |
200 |
201 | * **避免Objective-C的命名前缀.**
202 |
203 |
204 | 示例
205 |
206 | ```swift
207 | // 错误示例
208 | class AIRAccount {
209 | // ...
210 | }
211 |
212 | // 正确示例
213 | class Account {
214 | // ...
215 | }
216 | ```
217 |
218 |
219 |
220 | **[⬆ 返回顶部](#目录)**
221 |
222 | ## 风格
223 |
224 | * **当编译器可推断变量类型时, 不需要显式为变量添加类型**
225 |
226 |
227 | 示例
228 |
229 | ```swift
230 | // 错误示例
231 | let teacher: Teacher = Teacher()
232 |
233 | // 正确示例
234 | let teacher = Teacher()
235 | ```
236 |
237 | ```swift
238 | enum Weather {
239 | case sunny
240 | case cloudy
241 | }
242 |
243 | func someWeather() -> Weather {
244 | // WRONG
245 | return Weather.sunny
246 |
247 | // RIGHT
248 | return .sunny
249 | }
250 | ```
251 |
252 |
253 |
254 | * **不要使用 `self` 关键字,除非产生二义性.**
255 |
256 |
257 | 示例
258 |
259 | ```swift
260 | final class Listing {
261 |
262 | init(capacity: Int, allowsPets: Bool) {
263 | // 错误示例
264 | self.capacity = capacity
265 | self.isFamilyFriendly = !allowsPets // `self.` not required here
266 |
267 | // 正确示例
268 | self.capacity = capacity
269 | isFamilyFriendly = !allowsPets
270 | }
271 | }
272 | ```
273 |
274 |
275 |
276 | * **当元组作为返回值时, 给每一个成员添加名称,使其含义更为清晰.** 如果元祖包含超过3个成员的话, 则建议使用结构体.
277 |
278 |
279 | 示例
280 |
281 | ```swift
282 | // 错误示例
283 | func numbers() -> (Int, Int) {
284 | return (6, 6)
285 | }
286 | let numbers = numbers()
287 | print(numbers.0)
288 |
289 | // 正确示例
290 | func numbers() -> (x: Int, y: Int) {
291 | return (x: 6, y: 6)
292 | }
293 |
294 | // 替代方案
295 | func numbers2() -> (x: Int, y: Int) {
296 | let x = is 6
297 | let y = 6
298 | return (x, y)
299 | }
300 |
301 | let numbers = numbers()
302 | numbers
303 | numbers
304 | ```
305 |
306 |
307 |
308 | * **当声明类型或者变量时, 变量名后紧跟冒号,之后添加空格符, 空格符后紧跟类型.**
309 |
310 |
311 | 示例
312 |
313 | ```swift
314 |
315 | // 错误示例
316 | var something : Double = 0
317 |
318 | // 正确示例
319 | var something: Double = 0
320 | ```
321 |
322 | ```swift
323 | // 错误示例
324 | class MyClass : SuperClass {
325 | // ...
326 | }
327 |
328 | // 正确示例
329 | class MyClass: SuperClass {
330 | // ...
331 | }
332 | ```
333 |
334 | ```swift
335 | // 错误示例
336 | var dict = [KeyType:ValueType]()
337 | var dict = [KeyType : ValueType]()
338 |
339 | // 正确示例
340 | var dict = [KeyType: ValueType]()
341 | ```
342 |
343 |
344 |
345 | * **返回箭头两侧添加空格以增加可读性.**
346 |
347 |
348 | 示例
349 |
350 | ```swift
351 | // 错误示例
352 | func doSomething()->String {
353 | // ...
354 | }
355 |
356 | // 正确示例
357 | func doSomething() -> String {
358 | // ...
359 | }
360 | ```
361 |
362 |
363 |
364 | * **去除不必要的括号.**
365 |
366 |
367 | 示例
368 |
369 | ```swift
370 | // 错误示例
371 | if (userCount > 0) { ... }
372 | switch (someValue) { ... }
373 | let evens = userCounts.filter { (number) in number % 2 == 0 }
374 | let squares = userCounts.map() { $0 * $0 }
375 |
376 | // 正确示例
377 | if userCount > 0 { ... }
378 | switch someValue { ... }
379 | let evens = userCounts.filter { number in number % 2 == 0 }
380 | let squares = userCounts.map { $0 * $0 }
381 | ```
382 |
383 | ```swift
384 | // 错误示例
385 | if case .done(_) = result { ... }
386 |
387 | switch animal {
388 | case .dog(_, _, _):
389 | ...
390 | }
391 |
392 | // 正确示例
393 | if case .done = result { ... }
394 |
395 | switch animal {
396 | case .dog:
397 | ...
398 | }
399 | ```
400 |
401 |
402 |
403 | * **当使用`switch`时,默认情况使用 @unknown 来修饰 `default`关键字.**
404 |
405 |
406 | 示例
407 |
408 | ```swift
409 | // 错误示例
410 | let someFruit = .apple
411 | switch someFruit {
412 | case "apple":
413 | print("apple")
414 | default:
415 | print("Some other fruits.")
416 | }
417 |
418 | // 正确示例
419 | switch someFruit {
420 | case .apple:
421 | ...
422 | @unknown default:
423 | print("We don't sell that kind of fruit here.")
424 | }
425 | ```
426 |
427 |
428 |
429 | ### 函数
430 |
431 | * **函数体积不应超过百行; 并且需要对函数入参进行判断; 其内部应尽量避免使用全局变量来传递数据.**
432 |
433 |
434 | 示例
435 |
436 | ```swift
437 | // 正确示例
438 | func saveRSS(rss: RSS?, store: Store?) {
439 | guard let rss = rss else { return }
440 |
441 | guard let store = store else { return }
442 |
443 | return
444 | }
445 | ```
446 |
447 |
448 | * **当函数没有返回值时, 不需指定 `Void` 关键字.**
449 |
450 |
451 | 示例
452 |
453 | ```swift
454 | // 错误示例
455 | func doSomething() -> Void {
456 | ...
457 | }
458 |
459 | // 正确示例
460 | func doSomething() {
461 | ...
462 | }
463 | ```
464 |
465 |
466 |
467 | ### 闭包
468 |
469 | * **使用 `Void` 作为闭包返回值类型(当返回为空时).**
470 |
471 | 示例
472 |
473 | ```swift
474 | // 错误示例
475 | func doSomething(completion: () -> ()) {
476 | ...
477 | }
478 |
479 | // 正确示例
480 | func doSomething(completion: () -> Void) {
481 | ...
482 | }
483 | ```
484 |
485 |
486 |
487 | * **使用 (`_`) 代替闭包中未被使用的参数.**
488 |
489 |
490 | 示例
491 |
492 | ```swift
493 | // 错误示例
494 | someAsyncThing() { argument1, argument2, argument3 in
495 | print(argument3)
496 | }
497 |
498 | // 正确示例
499 | someAsyncThing() { _, _, argument3 in
500 | print(argument3)
501 | }
502 | ```
503 |
504 |
505 |
506 | ### 操作符
507 |
508 | * **二元操作符两侧应添加空格.** 该规则不适用于以下操作符 (e.g. `1...6` 或者 `1..<6`)
509 |
510 |
511 | ```swift
512 | 示例
513 |
514 | // 错误示例
515 | let capacity = 1+2
516 | let capacity = currentCapacity ?? 0
517 | let mask = (UIAccessibilityTraitButton|UIAccessibilityTraitSelected)
518 | let capacity=newCapacity
519 | let latitude = region.center.latitude - region.span.latitudeDelta/2.0
520 |
521 | // 正确示例
522 | let capacity = 1 + 2
523 | let capacity = currentCapacity ?? 0
524 | let mask = (UIAccessibilityTraitButton | UIAccessibilityTraitSelected)
525 | let capacity = newCapacity
526 | let latitude = region.center.latitude - (region.span.latitudeDelta / 2.0)
527 | ```
528 |
529 |
530 |
531 | **[⬆ 返回顶部](#目录)**
532 |
533 | ## 最佳实践
534 |
535 | * **尽可能在初始化函数 `init` 中完成对变量的初始化工作; 避免直接声明强制解包的变量.** 但是UIViewController的 `view `变量不在此考虑范围内.
536 |
537 |
538 | 示例
539 |
540 | ```swift
541 | // 错误示例
542 | class MyClass: NSObject {
543 |
544 | init() {
545 | super.init()
546 | someValue = 5
547 | }
548 |
549 | var someValue: Int!
550 | }
551 |
552 | // 正确示例
553 | class MyClass: NSObject {
554 |
555 | init() {
556 | someValue = 0
557 | super.init()
558 | }
559 |
560 | var someValue: Int
561 | }
562 | ```
563 |
564 |
565 |
566 | * **避免在 `init()` 中声明一切耗时或产生副作用的操作.** 例如建立数据库连接,读取数据等等.
567 |
568 | * **将属性观察器中的复杂逻辑提取到函数中.**
569 |
570 |
571 | 示例
572 |
573 | ```swift
574 | // 错误示例
575 | class TextField {
576 | var text: String? {
577 | didSet {
578 | guard oldValue != text else {
579 | return
580 | }
581 |
582 | // Do a bunch of text-related side-effects.
583 | }
584 | }
585 | }
586 |
587 | // 正确示例
588 | class TextField {
589 | var text: String? {
590 | didSet { textDidUpdate(from: oldValue) }
591 | }
592 |
593 | private func textDidUpdate(from oldValue: String?) {
594 | guard oldValue != text else {
595 | return
596 | }
597 |
598 | // Do a bunch of text-related side-effects.
599 | }
600 | }
601 | ```
602 |
603 |
604 |
605 | * **将复杂的回调逻辑代码放入函数**. 这样可以有效减少嵌套和 `weak self` 的使用。如果需要使用 `self` 关键字,则使用 `guard` 将其解包后使用.
606 |
607 |
608 | 示例
609 |
610 | ```swift
611 | //错误示例
612 | class MyClass {
613 |
614 | func request(completion: () -> Void) {
615 | API.request() { [weak self] response in
616 | if let strongSelf = self {
617 | // Processing and side effects
618 | }
619 | completion()
620 | }
621 | }
622 | }
623 |
624 | // 正确示例
625 | class MyClass {
626 |
627 | func request(completion: () -> Void) {
628 | API.request() { [weak self] response in
629 | guard let strongSelf = self else { return }
630 | strongSelf.doSomething(strongSelf.property)
631 | completion()
632 | }
633 | }
634 |
635 | func doSomething(nonOptionalParameter: SomeClass) {
636 | // Processing and side effects
637 | }
638 | }
639 | ```
640 |
641 |
642 |
643 | * **在一个范围 `Scope` 起始部分使用guard来做逻辑或者是参数检查.**
644 |
645 | * **访问控制符需要有清晰地设定.** 首选 `public, open, private` 而不是 `fileprivate`.
646 |
647 | * **访避免使用全局函数.**
648 |
649 |
650 | 示例
651 |
652 | ```swift
653 | // 错误示例
654 | func age(of person, bornAt timeInterval) -> Int {
655 | // ...
656 | }
657 |
658 | func jump(person: Person) {
659 | // ...
660 | }
661 |
662 | // 正确示例
663 | class Person {
664 | var bornAt: TimeInterval
665 |
666 | var age: Int {
667 | // ...
668 | }
669 |
670 | func jump() {
671 | // ...
672 | }
673 | }
674 | ```
675 |
676 |
677 |
678 | * **将私有常量放于文件顶部.** 若常量是外部或模块内可见,则将其定义为静态属性.
679 |
680 |
681 | 示例
682 |
683 | ```swift
684 | // 标准示例
685 | private let privateValue = "secret"
686 |
687 | public class MyClass {
688 |
689 | public static let publicValue = "something"
690 |
691 | func doSomething() {
692 | print(privateValue)
693 | print(MyClass.publicValue)
694 | }
695 | }
696 | ```
697 |
698 |
699 |
700 | * **使用无具体 `case` 的枚举类型来管理 `public, internal`的常量和函数.** 这样做可有效避免命名空间产生的冲突.
701 |
702 |
703 | 示例
704 |
705 | ```swift
706 | // 标准示例
707 | enum Environment {
708 |
709 | enum Earth {
710 | static let gravity = 9.8
711 | }
712 |
713 | enum Moon {
714 | static let gravity = 1.6
715 | }
716 | }
717 | ```
718 |
719 |
720 |
721 | * **使用Swift枚举自产生值,除非特定业务需要映射到外部资源.**
722 |
723 |
724 | 示例
725 |
726 | ```swift
727 | // 错误示例
728 | enum ErrorType: String {
729 | case error = "error"
730 | case warning = "warning"
731 | }
732 |
733 | enum UserType: String {
734 | case owner
735 | case manager
736 | case member
737 | }
738 |
739 | enum Planet: Int {
740 | case mercury = 0
741 | case venus = 1
742 | case earth = 2
743 | case mars = 3
744 | case jupiter = 4
745 | case saturn = 5
746 | case uranus = 6
747 | case neptune = 7
748 | }
749 |
750 | enum ErrorCode: Int {
751 | case notEnoughMemory
752 | case invalidResource
753 | case timeOut
754 | }
755 |
756 | // 正确实例
757 | enum ErrorType: String {
758 | case error
759 | case warning
760 | }
761 |
762 | /// 特定需要
763 | // swiftlint:disable redundant_string_enum_value
764 | enum UserType: String {
765 | case owner = "owner"
766 | case manager = "manager"
767 | case member = "member"
768 | }
769 | // swiftlint:enable redundant_string_enum_value
770 |
771 | enum Planet: Int {
772 | case mercury
773 | case venus
774 | case earth
775 | case mars
776 | case jupiter
777 | case saturn
778 | case uranus
779 | case neptune
780 | }
781 | ```
782 |
783 |
784 |
785 | * **默认使用 `static` 作为类型函数** 若支持重写,则要使用 `class` 关键字.
786 |
787 |
788 | 示例
789 |
790 | ```swift
791 |
792 | // 错误示例
793 | class Fruit {
794 | class func eatFruits(_ fruits: [Fruit]) { ... }
795 | }
796 |
797 | // 错误示例
798 | class Fruit {
799 | static func eatFruits(_ fruits: [Fruit]) { ... }
800 | }
801 | ```
802 |
803 |
804 |
805 | * **默认使用 `final` 修饰 `class`类型.** 该规则会明确告诉编译器取消对该class类型的动态派发优化, 其函数会使用直接派发方式.
806 |
807 |
808 | 示例
809 |
810 | ```swift
811 | // 错误示例
812 | class SettingsRepository {
813 | // ...
814 | }
815 |
816 | // 正确示例
817 | final class SettingsRepository {
818 | // ...
819 | }
820 | ```
821 |
822 |
823 |
824 | * **在不使用 `optinal binding`值时,则检查其是否为空.**
825 |
826 |
827 | 示例
828 |
829 | ```swift
830 | var thing: Thing?
831 |
832 | // 错误示例
833 | if let _ = thing {
834 | doThing()
835 | }
836 |
837 | // 正确示例
838 | if thing != nil {
839 | doThing()
840 | }
841 | ```
842 |
843 |
844 |
845 | **[⬆ 返回顶部](#目录)**
846 |
847 | ## 与Objc的交互
848 |
849 | * **在非必要情况下class无需继承 `NSObject`.** 如果需要使用Objc特性, 则按照需添加 `@objc` 修饰.
850 |
851 |
852 | 示例
853 |
854 | ```swift
855 | // 标准示例
856 | class PriceBreakdownViewController {
857 |
858 | private let acceptButton = UIButton()
859 |
860 | private func setUpAcceptButton() {
861 | acceptButton.addTarget(
862 | self,
863 | action: #selector(didTapAcceptButton),
864 | forControlEvents: .TouchUpInside)
865 | }
866 |
867 | @objc
868 | private func didTapAcceptButton() {
869 | // ...
870 | }
871 | }
872 | ```
873 |
874 |
875 |
876 | **[⬆ 返回顶部](#目录)**
--------------------------------------------------------------------------------
/Tests/ClaretCacheTests/ClaretCacheTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import ClaretCache
3 |
4 | final class ClaretCacheTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(ClaretCache().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample)
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/ClaretCacheTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(ClaretCacheTests.allTests)
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import ClaretCacheTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += ClaretCacheTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/configs/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules: # rule identifiers to exclude from running
2 | - colon
3 | - comma
4 | - control_statement
5 | - file_length
6 | - type_body_length
7 | opt_in_rules: # some rules are only opt-in
8 | - empty_count
9 | # Find all the available rules by running:
10 | # swiftlint rules
11 | included: # paths to include during linting. `--path` is ignored if present.
12 | - Sources
13 | - ClaretCacheDemoTests
14 | excluded: # paths to ignore during linting. Takes precedence over `included`.
15 | - Carthage
16 | - Pods
17 | - Source/ExcludedFolder
18 | - Source/ExcludedFile.swift
19 | - Source/*/ExcludedFile.swift # Exclude files with a wildcard
20 | analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
21 | - explicit_self
22 |
23 | # configurable rules can be customized from this configuration file
24 | # binary rules can set their severity level
25 | force_cast: warning # implicitly
26 | force_try:
27 | severity: warning # explicitly
28 | # rules that have both warning and error levels, can set just the warning level
29 | # implicitly
30 | line_length: 200
31 | # they can set both implicitly with an array
32 | type_body_length:
33 | - 300 # warning
34 | - 400 # error
35 | # or they can set both explicitly
36 | file_length:
37 | warning: 600
38 | error: 1200
39 | # naming rules can set warnings/errors for min_length and max_length
40 | # additionally they can set excluded names
41 | type_name:
42 | min_length: 4 # only warning
43 | max_length: # warning and error
44 | warning: 40
45 | error: 50
46 | excluded: iPhone # excluded via string
47 | identifier_name:
48 | min_length: # only min_length
49 | error: 2 # only error
50 | excluded: # excluded via string array
51 | - id
52 | - URL
53 | - GlobalAPIKey
54 | reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)
55 |
--------------------------------------------------------------------------------