Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File cache #306

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
55d82ad
EntityCache now allows specific ContentType instead of Any
pcantrell Oct 24, 2021
fcdb017
Corrections to the EntityCache API docs
pcantrell Nov 27, 2017
2760c11
New SiestaTools subproject
pcantrell Oct 24, 2021
2c6687b
Initial FileCache implementation
pcantrell Oct 24, 2021
33a8bda
Clarified log message
pcantrell Jun 18, 2018
1ea6ed0
EntityCache methods can now throw
pcantrell Oct 24, 2021
f1aa923
FileCache data isolation
pcantrell Oct 24, 2021
2f26b6f
Including FileCache.ContentType in keys
pcantrell Mar 18, 2018
48bb9eb
More legible cache logging
pcantrell Oct 24, 2021
87906bd
Make Entity conditionally codable
pcantrell Mar 31, 2019
06d7f7e
Removed deprecated isCompleted from comments/spec names
pcantrell Apr 18, 2019
99fc172
Lint warning for disabled specs
pcantrell Apr 22, 2019
8219e44
Mopping up cache API changes
pcantrell Apr 11, 2019
d4f06d8
Experimenting with FileCache in GHBrowser
pcantrell Oct 24, 2021
8aae16e
Tuck cache actions inside ResponseInfo, let Resource control whether …
pcantrell Oct 24, 2021
16188d8
Caching finally limited to GETs on same resource
pcantrell Oct 24, 2021
95594a7
Made cache requests behave correctly if passed to load(using:)
pcantrell Apr 4, 2020
17c7436
Removed some unnecessary self. refs
pcantrell Oct 29, 2020
c07b725
Fixed cache timestamp handling
pcantrell Oct 24, 2021
066baac
Rerunning a cache request needs to clear earlier cache stages
pcantrell Apr 5, 2020
1ada1be
Show stale cached data during load in example project
pcantrell Apr 6, 2020
ff47fac
Tidied up cache-related logging
pcantrell Oct 24, 2021
8e2d8ba
Oops, where did example project's scheme go?
pcantrell Apr 8, 2020
e4f8ab4
Added SiestaTools to SwiftPM manifest & set up for testing
pcantrell Apr 8, 2020
63baa8e
Consistent rules for which branches CI runs on
pcantrell Oct 24, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/swiftpm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: SwiftPM regression tests

on:
push:
branches: [ master ]
branches: [ main, ci-experiments ]
pull_request:
branches: [ master ]
branches: [ main ]

jobs:
test:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/xcode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Xcode regression tests

on:
push:
branches: [ $default-branch, main, master, ci-experiments ]
branches: [ main, ci-experiments ]
pull_request:
branches: [ $default-branch, main, master ]
branches: [ main ]

jobs:
test:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA7462381B4C768B00406D67"
BuildableName = "GithubBrowser.app"
BlueprintName = "GithubBrowser"
ReferencedContainer = "container:GithubBrowser.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA7462381B4C768B00406D67"
BuildableName = "GithubBrowser.app"
BlueprintName = "GithubBrowser"
ReferencedContainer = "container:GithubBrowser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA7462381B4C768B00406D67"
BuildableName = "GithubBrowser.app"
BlueprintName = "GithubBrowser"
ReferencedContainer = "container:GithubBrowser.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
43 changes: 41 additions & 2 deletions Examples/GithubBrowser/Source/API/GithubAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class _GitHubAPI {
fileprivate init() {
#if DEBUG
// Bare-bones logging of which network calls Siesta makes:
SiestaLog.Category.enabled = [.network]
SiestaLog.Category.enabled = [.network, .cache]

// For more info about how Siesta decides whether to make a network call,
// and which state updates it broadcasts to the app:
Expand All @@ -44,13 +44,48 @@ class _GitHubAPI {

$0.pipeline[.cleanup].add(
GitHubErrorMessageExtractor(jsonDecoder: jsonDecoder))

// Cache API results for fast launch & offline access:

$0.pipeline[.rawData].cacheUsing {
try FileCache<Data>(
poolName: "api.github.com",
dataIsolation: .perUser(identifiedBy: self.username)) // Show each user their own data
}

// Using the closure form of cacheUsing above signals that if we encounter an error trying create a cache
// directory or generate a cache isolation key from the username, we should simply proceed silently without
// having a persistent cache.

// Note that the dataIsolation uses only username. This means that users will not _see_ other users’ data;
// however, it does not _secure_ one user’s data from another. A user with permission to see the cache
// directory could in principle see all the cached data.
//
// To fully secure one user’s data from another, the application would need to generate some long-lived
// secret that is unique to each user. A password can work, though it will essentially empty the user’s
// cache if the password changes. The server could also send some kind of high-entropy per-user token in
// the authentication response.
}

RemoteImageView.defaultImageService.configure {
// We can cache images offline too:

$0.pipeline[.rawData].cacheUsing {
try FileCache<Data>(
poolName: "images",
dataIsolation: .sharedByAllUsers) // images aren't secret, so no need to isolate them
}
}


// –––––– Resource-specific configuration ––––––

service.configure("/search/**") {
// Refresh search results after 10 seconds (Siesta default is 30)
$0.expirationTime = 10

// Don't cache search results between runs, so we don't see stale results on launch
$0.pipeline.removeAllCaches()
}

// –––––– Auth configuration ––––––
Expand Down Expand Up @@ -116,19 +151,23 @@ class _GitHubAPI {
// MARK: - Authentication

func logIn(username: String, password: String) {
if let auth = "\(username):\(password)".data(using: String.Encoding.utf8) {
self.username = username
if let auth = "\(username):\(password)".data(using: .utf8) {
basicAuthHeader = "Basic \(auth.base64EncodedString())"
}
}

func logOut() {
username = nil
basicAuthHeader = nil
}

var isAuthenticated: Bool {
return basicAuthHeader != nil
}

private var username: String?

private var basicAuthHeader: String? {
didSet {
// These two calls are almost always necessary when you have changing auth for your API:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ class RepositoryListViewController: UITableViewController, ResourceObserver {
super.viewDidLoad()

view.backgroundColor = SiestaTheme.darkColor

statusOverlay.embed(in: self)
statusOverlay.displayPriority = [.anyData, .loading, .error]
}

override func viewDidLayoutSubviews() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class RepositoryViewController: UIViewController, ResourceObserver {
super.viewDidLoad()

view.backgroundColor = SiestaTheme.darkColor

statusOverlay.embed(in: self)
statusOverlay.displayPriority = [.anyData, .loading, .error] // Prioritize partial data over loading indicator

Expand Down
2 changes: 2 additions & 0 deletions Examples/GithubBrowser/Source/UI/UserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class UserViewController: UIViewController, UISearchBarDelegate, ResourceObserve
view.backgroundColor = SiestaTheme.darkColor

statusOverlay.embed(in: self)
statusOverlay.displayPriority = [.anyData, .loading, .error]

showUser(nil)

searchBar.becomeFirstResponder()
Expand Down
6 changes: 5 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ let package = Package(
.target(
name: "Siesta"
),
.target(
name: "SiestaTools",
dependencies: ["Siesta"]
),
.target(
name: "SiestaUI",
dependencies: ["Siesta"],
Expand All @@ -42,7 +46,7 @@ let package = Package(
),
.testTarget(
name: "SiestaTests",
dependencies: ["SiestaUI", "Siesta_Alamofire", "Quick", "Nimble"],
dependencies: ["SiestaUI", "SiestaTools", "Siesta_Alamofire", "Quick", "Nimble"],
path: "Tests/Functional",
exclude: ["ObjcCompatibilitySpec.m"] // SwiftPM currently only supports Swift
),
Expand Down
6 changes: 6 additions & 0 deletions Siesta.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ Pod::Spec.new do |s|
s.exclude_files = "**/Info*.plist"
end

s.subspec "Tools" do |s|
s.source_files = "Source/SiestaTools/**/*"
s.exclude_files = "**/Info*.plist"
s.dependency "Siesta/Core"
end

s.subspec "UI" do |s|
s.ios.source_files = "Source/SiestaUI/**/*.{swift,m,h}"
s.dependency "Siesta/Core"
Expand Down
Loading