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

Differentiate caches also by data provided (e.g. domain filter for entitity states) #70

Merged
merged 4 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.3] - 2024-11-12
## [0.4.4] - 2024-12-05
- Changed: Differentiate cache also by data provided (e.g. domain filter for states)

## [0.4.3] - 2024-12-03
- Changed: Allow passing data to entities state subcription, so you can filter what you want to receive

## [0.4.2] - 2024-04-22
Expand Down
2 changes: 1 addition & 1 deletion HAKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'HAKit'
s.version = '0.4.3'
s.version = '0.4.4'
s.summary = 'Communicate with a Home Assistant instance.'
s.author = 'Home Assistant'

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ To add it to an Xcode project, you can do this by adding the URL to File > Swift
Add the following line to your Podfile:

```ruby
pod "HAKit", "~> 0.4.3"
pod "HAKit", "~> 0.4.4"
# We are working from a fork of Starscream due to a necessary fix, please specify in your podfile
pod 'Starscream', git: 'https://github.com/bgoncal/starscream', branch: 'ha-URLSession-fix'
# pod "HAKit/PromiseKit" # optional, for PromiseKit support
Expand Down
33 changes: 27 additions & 6 deletions Source/Caches/HACachesContainer.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Foundation

/// A cache key for `HACachesContainer`
public protocol HACacheKey {
/// The value type in the cache, e.g. `T` in `HACache<T>`
Expand Down Expand Up @@ -40,9 +42,14 @@ public protocol HACacheKey {
///
/// Then, access it from a connection like `connection.caches.yourValueType`.
public class HACachesContainer {
struct CacheEntry {
let data: [String: Any]
let cache: Any
}

/// Our current initialized caches. We key by the ObjectIdentifier of the meta type, which guarantees a unique
/// cache entry per key since the identifier is globally unique per type.
private var values: [ObjectIdentifier: Any] = [:]
private(set) var values: [ObjectIdentifier: [CacheEntry]] = [:]
/// The connection we're chained off. This is unowned to avoid a cyclic reference. We expect to crash in this case.
internal unowned let connection: HAConnection

Expand All @@ -65,12 +72,26 @@ public class HACachesContainer {
// ObjectIdentifier is globally unique per class _or_ meta type, and we're using meta type here
let key = ObjectIdentifier(KeyType.self)

if let value = values[key] as? HACache<KeyType.Value> {
return value
if let cacheEntries = values[key], let cacheEntry = cacheEntries.first(where: { entry in
// Avoid unecessary json serialization to compare dictionaries
if entry.data.isEmpty, data.isEmpty {
return true
}

let currentData = try? JSONSerialization.data(withJSONObject: entry.data, options: .prettyPrinted)
let requestedData = try? JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)

return currentData == requestedData
}), let cache = cacheEntry.cache as? HACache<KeyType.Value> {
return cache
}

let value = KeyType.create(connection: connection, data: data)
values[key] = value
return value
let cache = KeyType.create(connection: connection, data: data)
if values[key] == nil {
values[key] = [.init(data: data, cache: cache)]
} else {
values[key]?.append(CacheEntry(data: data, cache: cache))
}
return cache
}
}
14 changes: 14 additions & 0 deletions Tests/HACachesContainer.test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ internal class HACachesContainerTests: XCTestCase {
let cache2 = container[Key2.self]
XCTAssertNotEqual(ObjectIdentifier(cache1), ObjectIdentifier(cache2))
}

func testSameKeyWithDifferentDataReturnsDifferentCache() {
_ = container.states(["abc": "def"])
_ = container.states(["123": "456"])

XCTAssertEqual(container.values.first?.value.count, 2)
}

func testSameKeyWithSameDataReturnsSameCache() {
_ = container.states(["abc": "def"])
_ = container.states(["abc": "def"])

XCTAssertEqual(container.values.first?.value.count, 1)
}
}

private struct Key1: HACacheKey {
Expand Down
Loading