Merge pull request #189 from hyperoslo/refactor/generic_storage

Refactor to generic Storage
This commit is contained in:
Khoa
2018-06-13 13:55:18 +02:00
committed by GitHub
27 changed files with 767 additions and 627 deletions
+138 -100
View File
@@ -9,31 +9,17 @@
/* Begin PBXBuildFile section */
BDEDD37D1DBCEB8A007416A6 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDEDD3561DBCE5B1007416A6 /* Cache.framework */; };
D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; };
D21B667D1F6A723C00125DE1 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; };
D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; };
D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; };
D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; };
D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; };
D21B66831F6A723C00125DE1 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; };
D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; };
D21B66861F6A723C00125DE1 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; };
D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; };
D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; };
D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; };
D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; };
D21B668C1F6A723C00125DE1 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; };
D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; };
D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; };
D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; };
D21B66901F6A723F00125DE1 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; };
D21B66911F6A723F00125DE1 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; };
D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; };
D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; };
D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; };
D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; };
D21B66961F6A724000125DE1 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; };
D21B66971F6A724000125DE1 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; };
D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; };
D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; };
D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; };
D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984D1F694FFA00CE8F68 /* DiskConfig.swift */; };
@@ -41,7 +27,60 @@
D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984D1F694FFA00CE8F68 /* DiskConfig.swift */; };
D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */; };
D21B66A11F6A747000125DE1 /* ImageWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98761F69513800CE8F68 /* ImageWrapperTests.swift */; };
D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236F3191F6BEF73004EE01F /* StorageTests.swift */; };
D221E5BC20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; };
D221E5BD20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; };
D221E5BE20D00D9300BC940E /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BB20D00D9300BC940E /* MemoryStorage.swift */; };
D221E5C020D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; };
D221E5C120D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; };
D221E5C220D00DCC00BC940E /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5BF20D00DCC00BC940E /* Entry.swift */; };
D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; };
D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; };
D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D221E5C320D00DDB00BC940E /* DiskStorage.swift */; };
D270147020D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; };
D270147120D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; };
D270147220D1018A003B45C7 /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270146F20D1018A003B45C7 /* Transformer.swift */; };
D270147420D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; };
D270147520D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; };
D270147620D101F3003B45C7 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147320D101F3003B45C7 /* StorageAware.swift */; };
D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; };
D270147920D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; };
D270147A20D1046A003B45C7 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147720D1046A003B45C7 /* HybridStorage.swift */; };
D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; };
D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; };
D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147B20D107DA003B45C7 /* SyncStorage.swift */; };
D270148020D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; };
D270148120D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; };
D270148220D10982003B45C7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270147F20D10982003B45C7 /* Storage.swift */; };
D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; };
D270148520D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; };
D270148620D10E76003B45C7 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148320D10E76003B45C7 /* AsyncStorage.swift */; };
D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; };
D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; };
D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148720D11040003B45C7 /* Storage+Transform.swift */; };
D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; };
D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; };
D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148B20D113EA003B45C7 /* Optional+Extension.swift */; };
D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; };
D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; };
D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270148F20D1251E003B45C7 /* TypeWrapper.swift */; };
D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; };
D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; };
D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270149320D125AC003B45C7 /* MemoryCapsule.swift */; };
D270149E20D1286F003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; };
D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; };
D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; };
D27014A320D129A3003B45C7 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; };
D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; };
D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; };
D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27014A420D129EB003B45C7 /* TransformerFactory.swift */; };
D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; };
D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; };
D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; };
D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; };
D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; };
D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; };
D27014B020D12E37003B45C7 /* StorageSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */; };
D27014B120D12E38003B45C7 /* StorageSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */; };
D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; };
D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; };
D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */; };
@@ -50,22 +89,9 @@
D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */; };
D28C9BAC1F67ECD400C180C1 /* TestHelper+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291CDF1C28374800B702C9 /* TestHelper+iOS.swift */; };
D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291DA01C28405900B702C9 /* UIImage+ExtensionsTests.swift */; };
D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; };
D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; };
D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF01F6A85F30060F614 /* SyncStorage.swift */; };
D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; };
D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; };
D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */; };
D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; };
D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; };
D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAF81F6A962D0060F614 /* AsyncStorage.swift */; };
D292DAFD1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; };
D292DAFE1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; };
D292DAFF1F6A970B0060F614 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DAFC1F6A970B0060F614 /* Result.swift */; };
D292DB011F6AA06B0060F614 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; };
D292DB021F6AA06B0060F614 /* SyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */; };
D292DB041F6AA0730060F614 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; };
D292DB051F6AA0730060F614 /* AsyncStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */; };
D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; };
D2CF981F1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; };
D2CF98201F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF981B1F69427C00CE8F68 /* TestCase+Extensions.swift */; };
@@ -79,27 +105,16 @@
D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */; };
D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */; };
D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */; };
D2CF98651F694FFA00CE8F68 /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98541F694FFA00CE8F68 /* Entry.swift */; };
D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */; };
D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98561F694FFA00CE8F68 /* Expiry.swift */; };
D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */; };
D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98591F694FFA00CE8F68 /* StorageError.swift */; };
D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */; };
D2CF986C1F694FFA00CE8F68 /* HybridStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */; };
D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */; };
D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985E1F694FFA00CE8F68 /* Storage.swift */; };
D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */; };
D2CF987C1F69513800CE8F68 /* Date+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98721F69513800CE8F68 /* Date+ExtensionsTests.swift */; };
D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98741F69513800CE8F68 /* MemoryCapsuleTests.swift */; };
D2CF987E1F69513800CE8F68 /* ExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98751F69513800CE8F68 /* ExpiryTests.swift */; };
D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98761F69513800CE8F68 /* ImageWrapperTests.swift */; };
D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98771F69513800CE8F68 /* TypeWrapperTests.swift */; };
D2CF98811F69513800CE8F68 /* DiskStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */; };
D2CF98821F69513800CE8F68 /* HybridStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */; };
D2CF98831F69513800CE8F68 /* MemoryStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */; };
D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */; };
D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98861F695B8F00CE8F68 /* Types.swift */; };
D2CF98891F695F9400CE8F68 /* TypeWrapperStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */; };
D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; };
D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; };
D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; };
@@ -147,12 +162,23 @@
/* Begin PBXFileReference section */
BDEDD3561DBCE5B1007416A6 /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BDEDD3781DBCEB8A007416A6 /* Cache-tvOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-tvOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
D221E5BB20D00D9300BC940E /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = "<group>"; };
D221E5BF20D00DCC00BC940E /* Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = "<group>"; };
D221E5C320D00DDB00BC940E /* DiskStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = "<group>"; };
D236F3191F6BEF73004EE01F /* StorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageTests.swift; sourceTree = "<group>"; };
D270146F20D1018A003B45C7 /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = "<group>"; };
D270147320D101F3003B45C7 /* StorageAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAware.swift; sourceTree = "<group>"; };
D270147720D1046A003B45C7 /* HybridStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridStorage.swift; sourceTree = "<group>"; };
D270147B20D107DA003B45C7 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = "<group>"; };
D270147F20D10982003B45C7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
D270148320D10E76003B45C7 /* AsyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage.swift; sourceTree = "<group>"; };
D270148720D11040003B45C7 /* Storage+Transform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Transform.swift"; sourceTree = "<group>"; };
D270148B20D113EA003B45C7 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
D270148F20D1251E003B45C7 /* TypeWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapper.swift; sourceTree = "<group>"; };
D270149320D125AC003B45C7 /* MemoryCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCapsule.swift; sourceTree = "<group>"; };
D27014A420D129EB003B45C7 /* TransformerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransformerFactory.swift; sourceTree = "<group>"; };
D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectConverterTests.swift; sourceTree = "<group>"; };
D28897041F8B79B300C61DEE /* JSONDecoder+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Extensions.swift"; sourceTree = "<group>"; };
D292DAF01F6A85F30060F614 /* SyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorage.swift; sourceTree = "<group>"; };
D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorageAware.swift; sourceTree = "<group>"; };
D292DAF81F6A962D0060F614 /* AsyncStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorage.swift; sourceTree = "<group>"; };
D292DAFC1F6A970B0060F614 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStorageTests.swift; sourceTree = "<group>"; };
D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStorageTests.swift; sourceTree = "<group>"; };
@@ -163,16 +189,10 @@
D2CF984E1F694FFA00CE8F68 /* MemoryConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryConfig.swift; sourceTree = "<group>"; };
D2CF98501F694FFA00CE8F68 /* Date+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSerializer.swift; sourceTree = "<group>"; };
D2CF98541F694FFA00CE8F68 /* Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = "<group>"; };
D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpirationMode.swift; sourceTree = "<group>"; };
D2CF98561F694FFA00CE8F68 /* Expiry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Expiry.swift; sourceTree = "<group>"; };
D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageWrapper.swift; sourceTree = "<group>"; };
D2CF98591F694FFA00CE8F68 /* StorageError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageError.swift; sourceTree = "<group>"; };
D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = "<group>"; };
D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HybridStorage.swift; sourceTree = "<group>"; };
D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = "<group>"; };
D2CF985E1F694FFA00CE8F68 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageAware.swift; sourceTree = "<group>"; };
D2CF98721F69513800CE8F68 /* Date+ExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+ExtensionsTests.swift"; sourceTree = "<group>"; };
D2CF98741F69513800CE8F68 /* MemoryCapsuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryCapsuleTests.swift; sourceTree = "<group>"; };
D2CF98751F69513800CE8F68 /* ExpiryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpiryTests.swift; sourceTree = "<group>"; };
@@ -181,9 +201,8 @@
D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskStorageTests.swift; sourceTree = "<group>"; };
D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HybridStorageTests.swift; sourceTree = "<group>"; };
D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryStorageTests.swift; sourceTree = "<group>"; };
D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorage.swift; sourceTree = "<group>"; };
D2CF98861F695B8F00CE8F68 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = "<group>"; };
D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorageTests.swift; sourceTree = "<group>"; };
D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageSupportTests.swift; sourceTree = "<group>"; };
D2D4CC151FA3145000E4A2D5 /* MD5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = "<group>"; };
D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5Tests.swift; sourceTree = "<group>"; };
D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryWrapper.swift; sourceTree = "<group>"; };
@@ -297,7 +316,6 @@
isa = PBXGroup;
children = (
D2CF98531F694FFA00CE8F68 /* DataSerializer.swift */,
D2CF98541F694FFA00CE8F68 /* Entry.swift */,
D2CF98551F694FFA00CE8F68 /* ExpirationMode.swift */,
D2CF98561F694FFA00CE8F68 /* Expiry.swift */,
D2CF98571F694FFA00CE8F68 /* ImageWrapper.swift */,
@@ -307,6 +325,12 @@
D2D4CC151FA3145000E4A2D5 /* MD5.swift */,
D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */,
D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */,
D221E5BF20D00DCC00BC940E /* Entry.swift */,
D270146F20D1018A003B45C7 /* Transformer.swift */,
D270148B20D113EA003B45C7 /* Optional+Extension.swift */,
D270148F20D1251E003B45C7 /* TypeWrapper.swift */,
D270149320D125AC003B45C7 /* MemoryCapsule.swift */,
D27014A420D129EB003B45C7 /* TransformerFactory.swift */,
);
path = Library;
sourceTree = "<group>";
@@ -314,15 +338,14 @@
D2CF985A1F694FFA00CE8F68 /* Storage */ = {
isa = PBXGroup;
children = (
D2CF985B1F694FFA00CE8F68 /* DiskStorage.swift */,
D2CF985C1F694FFA00CE8F68 /* HybridStorage.swift */,
D2CF985D1F694FFA00CE8F68 /* MemoryStorage.swift */,
D2CF985E1F694FFA00CE8F68 /* Storage.swift */,
D2CF985F1F694FFA00CE8F68 /* StorageAware.swift */,
D2CF98841F69598900CE8F68 /* TypeWrapperStorage.swift */,
D292DAF01F6A85F30060F614 /* SyncStorage.swift */,
D292DAF41F6A94000060F614 /* AsyncStorageAware.swift */,
D292DAF81F6A962D0060F614 /* AsyncStorage.swift */,
D221E5BB20D00D9300BC940E /* MemoryStorage.swift */,
D221E5C320D00DDB00BC940E /* DiskStorage.swift */,
D270147320D101F3003B45C7 /* StorageAware.swift */,
D270147720D1046A003B45C7 /* HybridStorage.swift */,
D270147B20D107DA003B45C7 /* SyncStorage.swift */,
D270147F20D10982003B45C7 /* Storage.swift */,
D270148320D10E76003B45C7 /* AsyncStorage.swift */,
D270148720D11040003B45C7 /* Storage+Transform.swift */,
);
path = Storage;
sourceTree = "<group>";
@@ -365,7 +388,7 @@
D2CF98791F69513800CE8F68 /* DiskStorageTests.swift */,
D2CF987A1F69513800CE8F68 /* HybridStorageTests.swift */,
D2CF987B1F69513800CE8F68 /* MemoryStorageTests.swift */,
D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */,
D2CF98881F695F9400CE8F68 /* StorageSupportTests.swift */,
D292DB001F6AA06B0060F614 /* SyncStorageTests.swift */,
D292DB031F6AA0730060F614 /* AsyncStorageTests.swift */,
D236F3191F6BEF73004EE01F /* StorageTests.swift */,
@@ -767,31 +790,35 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D21B66861F6A723C00125DE1 /* Entry.swift in Sources */,
D21B66971F6A724000125DE1 /* StorageAware.swift in Sources */,
D221E5C620D00DDB00BC940E /* DiskStorage.swift in Sources */,
D21B669D1F6A724600125DE1 /* DiskConfig.swift in Sources */,
D21B66961F6A724000125DE1 /* Storage.swift in Sources */,
D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */,
D270147620D101F3003B45C7 /* StorageAware.swift in Sources */,
D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */,
D270149220D1251E003B45C7 /* TypeWrapper.swift in Sources */,
D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */,
D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */,
D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */,
D221E5BE20D00D9300BC940E /* MemoryStorage.swift in Sources */,
D270148E20D113EA003B45C7 /* Optional+Extension.swift in Sources */,
D270147220D1018A003B45C7 /* Transformer.swift in Sources */,
D21B668C1F6A723C00125DE1 /* Types.swift in Sources */,
D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
D27014A720D129EB003B45C7 /* TransformerFactory.swift in Sources */,
D270147E20D107DA003B45C7 /* SyncStorage.swift in Sources */,
D292DAFF1F6A970B0060F614 /* Result.swift in Sources */,
D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */,
D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
D21B668B1F6A723C00125DE1 /* StorageError.swift in Sources */,
D292DAFB1F6A962D0060F614 /* AsyncStorage.swift in Sources */,
D270148A20D11040003B45C7 /* Storage+Transform.swift in Sources */,
D5A138C21EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
D21B66851F6A723C00125DE1 /* DataSerializer.swift in Sources */,
D21B66941F6A724000125DE1 /* HybridStorage.swift in Sources */,
D270147A20D1046A003B45C7 /* HybridStorage.swift in Sources */,
D270148620D10E76003B45C7 /* AsyncStorage.swift in Sources */,
D270149620D125AC003B45C7 /* MemoryCapsule.swift in Sources */,
D28897071F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
D292DAF71F6A94000060F614 /* AsyncStorageAware.swift in Sources */,
D21B66931F6A724000125DE1 /* DiskStorage.swift in Sources */,
D21B66981F6A724000125DE1 /* TypeWrapperStorage.swift in Sources */,
D270148220D10982003B45C7 /* Storage.swift in Sources */,
D221E5C220D00DCC00BC940E /* Entry.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -801,8 +828,14 @@
files = (
D2CF98201F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */,
D2CF98231F69427C00CE8F68 /* TestHelper.swift in Sources */,
D27014A320D129A3003B45C7 /* DiskStorageTests.swift in Sources */,
D27014A020D12870003B45C7 /* MemoryStorageTests.swift in Sources */,
D2CF98261F69427C00CE8F68 /* User.swift in Sources */,
D27014AE20D12D83003B45C7 /* AsyncStorageTests.swift in Sources */,
D28A1D241F6FFEF60030DF81 /* ObjectConverterTests.swift in Sources */,
D27014B120D12E38003B45C7 /* StorageSupportTests.swift in Sources */,
D27014AD20D12CC3003B45C7 /* SyncStorageTests.swift in Sources */,
D27014AA20D12BA4003B45C7 /* HybridStorageTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -812,24 +845,23 @@
files = (
D2CF987E1F69513800CE8F68 /* ExpiryTests.swift in Sources */,
D28A1D231F6FFEF50030DF81 /* ObjectConverterTests.swift in Sources */,
D2CF98891F695F9400CE8F68 /* TypeWrapperStorageTests.swift in Sources */,
D2CF98821F69513800CE8F68 /* HybridStorageTests.swift in Sources */,
D2CF98831F69513800CE8F68 /* MemoryStorageTests.swift in Sources */,
D270149E20D1286F003B45C7 /* MemoryStorageTests.swift in Sources */,
D2CF987C1F69513800CE8F68 /* Date+ExtensionsTests.swift in Sources */,
D28C9BAC1F67ECD400C180C1 /* TestHelper+iOS.swift in Sources */,
D2CF98211F69427C00CE8F68 /* TestHelper.swift in Sources */,
D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */,
D2D4CC1A1FA3166900E4A2D5 /* MD5Tests.swift in Sources */,
D292DB041F6AA0730060F614 /* AsyncStorageTests.swift in Sources */,
D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */,
D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */,
D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */,
D27014A120D129A2003B45C7 /* DiskStorageTests.swift in Sources */,
D27014B020D12E37003B45C7 /* StorageSupportTests.swift in Sources */,
D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */,
D27014A820D12BA3003B45C7 /* HybridStorageTests.swift in Sources */,
D27014AF20D12D84003B45C7 /* AsyncStorageTests.swift in Sources */,
D2CF98241F69427C00CE8F68 /* User.swift in Sources */,
D27014AC20D12CC1003B45C7 /* SyncStorageTests.swift in Sources */,
D2CF981E1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */,
D236F31A1F6BEF73004EE01F /* StorageTests.swift in Sources */,
D292DB011F6AA06B0060F614 /* SyncStorageTests.swift in Sources */,
D2CF98811F69513800CE8F68 /* DiskStorageTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -837,31 +869,35 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D21B667D1F6A723C00125DE1 /* Entry.swift in Sources */,
D21B66911F6A723F00125DE1 /* StorageAware.swift in Sources */,
D221E5C520D00DDB00BC940E /* DiskStorage.swift in Sources */,
D21B669B1F6A724600125DE1 /* DiskConfig.swift in Sources */,
D21B66901F6A723F00125DE1 /* Storage.swift in Sources */,
D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */,
D270147520D101F3003B45C7 /* StorageAware.swift in Sources */,
D21B669C1F6A724600125DE1 /* MemoryConfig.swift in Sources */,
D270149120D1251E003B45C7 /* TypeWrapper.swift in Sources */,
D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */,
D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */,
D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */,
D221E5BD20D00D9300BC940E /* MemoryStorage.swift in Sources */,
D270148D20D113EA003B45C7 /* Optional+Extension.swift in Sources */,
D270147120D1018A003B45C7 /* Transformer.swift in Sources */,
D21B66831F6A723C00125DE1 /* Types.swift in Sources */,
D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
D27014A620D129EB003B45C7 /* TransformerFactory.swift in Sources */,
D270147D20D107DA003B45C7 /* SyncStorage.swift in Sources */,
D292DAFE1F6A970B0060F614 /* Result.swift in Sources */,
D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */,
D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
D21B66821F6A723C00125DE1 /* StorageError.swift in Sources */,
D292DAFA1F6A962D0060F614 /* AsyncStorage.swift in Sources */,
D270148920D11040003B45C7 /* Storage+Transform.swift in Sources */,
D5A138C41EB29C2100881A20 /* NSImage+Extensions.swift in Sources */,
D21B667C1F6A723C00125DE1 /* DataSerializer.swift in Sources */,
D21B668E1F6A723F00125DE1 /* HybridStorage.swift in Sources */,
D270147920D1046A003B45C7 /* HybridStorage.swift in Sources */,
D270148520D10E76003B45C7 /* AsyncStorage.swift in Sources */,
D270149520D125AC003B45C7 /* MemoryCapsule.swift in Sources */,
D28897061F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
D292DAF61F6A94000060F614 /* AsyncStorageAware.swift in Sources */,
D21B668D1F6A723F00125DE1 /* DiskStorage.swift in Sources */,
D21B66921F6A723F00125DE1 /* TypeWrapperStorage.swift in Sources */,
D270148120D10982003B45C7 /* Storage.swift in Sources */,
D221E5C120D00DCC00BC940E /* Entry.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -869,7 +905,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D292DB051F6AA0730060F614 /* AsyncStorageTests.swift in Sources */,
D28A1D221F6FFEF50030DF81 /* ObjectConverterTests.swift in Sources */,
D2CF98221F69427C00CE8F68 /* TestHelper.swift in Sources */,
D21B66A11F6A747000125DE1 /* ImageWrapperTests.swift in Sources */,
@@ -877,7 +912,6 @@
D5291D851C283C7C00B702C9 /* TestHelper+OSX.swift in Sources */,
D5291DA31C2841D200B702C9 /* NSImage+ExtensionsTests.swift in Sources */,
D2CF981F1F69427C00CE8F68 /* TestCase+Extensions.swift in Sources */,
D292DB021F6AA06B0060F614 /* SyncStorageTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -885,31 +919,35 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D2CF98651F694FFA00CE8F68 /* Entry.swift in Sources */,
D221E5C420D00DDB00BC940E /* DiskStorage.swift in Sources */,
D2CF98681F694FFA00CE8F68 /* ImageWrapper.swift in Sources */,
D2CF986C1F694FFA00CE8F68 /* HybridStorage.swift in Sources */,
D2CF98871F695B8F00CE8F68 /* Types.swift in Sources */,
D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */,
D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */,
D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */,
D270147420D101F3003B45C7 /* StorageAware.swift in Sources */,
D270149020D1251E003B45C7 /* TypeWrapper.swift in Sources */,
D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */,
D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */,
D221E5BC20D00D9300BC940E /* MemoryStorage.swift in Sources */,
D270148C20D113EA003B45C7 /* Optional+Extension.swift in Sources */,
D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */,
D270147020D1018A003B45C7 /* Transformer.swift in Sources */,
D292DAFD1F6A970B0060F614 /* Result.swift in Sources */,
D27014A520D129EB003B45C7 /* TransformerFactory.swift in Sources */,
D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */,
D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */,
D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */,
D270147C20D107DA003B45C7 /* SyncStorage.swift in Sources */,
D2CF98671F694FFA00CE8F68 /* Expiry.swift in Sources */,
D292DAF91F6A962D0060F614 /* AsyncStorage.swift in Sources */,
D270148820D11040003B45C7 /* Storage+Transform.swift in Sources */,
D2CF986A1F694FFA00CE8F68 /* StorageError.swift in Sources */,
D5A138C11EB29BFA00881A20 /* UIImage+Extensions.swift in Sources */,
D2CF986E1F694FFA00CE8F68 /* Storage.swift in Sources */,
D270147820D1046A003B45C7 /* HybridStorage.swift in Sources */,
D270148420D10E76003B45C7 /* AsyncStorage.swift in Sources */,
D28897051F8B79B300C61DEE /* JSONDecoder+Extensions.swift in Sources */,
D292DAF51F6A94000060F614 /* AsyncStorageAware.swift in Sources */,
D270148020D10982003B45C7 /* Storage.swift in Sources */,
D270149420D125AC003B45C7 /* MemoryCapsule.swift in Sources */,
D2CF98611F694FFA00CE8F68 /* MemoryConfig.swift in Sources */,
D2CF98661F694FFA00CE8F68 /* ExpirationMode.swift in Sources */,
D221E5C020D00DCC00BC940E /* Entry.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+8 -10
View File
@@ -4,19 +4,17 @@ public struct MemoryConfig {
/// Expiry date that will be applied by default for every added object
/// if it's not overridden in the add(key: object: expiry: completion:) method
public let expiry: Expiry
/// The maximum number of objects in memory the cache should hold. 0 means no limit.
/// The maximum number of objects in memory the cache should hold.
/// If 0, there is no count limit. The default value is 0.
public let countLimit: UInt
public init(expiry: Expiry = .never, countLimit: UInt = 0) {
/// The maximum total cost that the cache can hold before it starts evicting objects.
/// If 0, there is no total cost limit. The default value is 0
public let totalCostLimit: UInt
public init(expiry: Expiry = .never, countLimit: UInt = 0, totalCostLimit: UInt = 0) {
self.expiry = expiry
self.countLimit = countLimit
}
// MARK: - Deprecated
@available(*, deprecated,
message: "Use init(expiry:countLimit:) instead.",
renamed: "init(expiry:countLimit:)")
public init(expiry: Expiry = .never, countLimit: UInt = 0, totalCostLimit: UInt = 0) {
self.init(expiry: expiry, countLimit: countLimit)
self.totalCostLimit = totalCostLimit
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Foundation
/// A wrapper around cached object and its expiry date.
public struct Entry<T: Codable> {
public struct Entry<T> {
/// Cached object
public let object: T
/// Expiry date
+20
View File
@@ -0,0 +1,20 @@
import Foundation
/// Helper class to hold cached instance and expiry date.
/// Used in memory storage to work with NSCache.
class MemoryCapsule: NSObject {
/// Object to be cached
let object: Any
/// Expiration date
let expiry: Expiry
/**
Creates a new instance of Capsule.
- Parameter value: Object to be cached
- Parameter expiry: Expiration date
*/
init(value: Any, expiry: Expiry) {
self.object = value
self.expiry = expiry
}
}
@@ -0,0 +1,11 @@
import Foundation
public extension Optional {
func unwrapOrThrow(error: Error) throws -> Wrapped {
if let value = self {
return value
} else {
throw error
}
}
}
+2
View File
@@ -13,4 +13,6 @@ public enum StorageError: Error {
case encodingFailed
/// The storage has been deallocated
case deallocated
/// Fail to perform transformation to or from Data
case transformerFail
}
+11
View File
@@ -0,0 +1,11 @@
import Foundation
public class Transformer<T> {
let toData: (T) throws -> Data
let fromData: (Data) throws -> T
public init(toData: @escaping (T) throws -> Data, fromData: @escaping (Data) throws -> T) {
self.toData = toData
self.fromData = fromData
}
}
@@ -0,0 +1,38 @@
import Foundation
public class TransformerFactory {
public static func forData() -> Transformer<Data> {
let toData: (Data) throws -> Data = { $0 }
let fromData: (Data) throws -> Data = { $0 }
return Transformer<Data>(toData: toData, fromData: fromData)
}
public static func forImage() -> Transformer<Image> {
let toData: (Image) throws -> Data = { image in
return try image.cache_toData().unwrapOrThrow(error: StorageError.transformerFail)
}
let fromData: (Data) throws -> Image = { data in
return try Image(data: data).unwrapOrThrow(error: StorageError.transformerFail)
}
return Transformer<Image>(toData: toData, fromData: fromData)
}
public static func forCodable<U: Codable>(ofType: U.Type) -> Transformer<U> {
let toData: (U) throws -> Data = { object in
let wrapper = TypeWrapper<U>(object: object)
let encoder = JSONEncoder()
return try encoder.encode(wrapper)
}
let fromData: (Data) throws -> U = { data in
let decoder = JSONDecoder()
return try decoder.decode(TypeWrapper<U>.self, from: data).object
}
return Transformer<U>(toData: toData, fromData: fromData)
}
}
+14
View File
@@ -0,0 +1,14 @@
import Foundation
/// Used to wrap Codable object
public struct TypeWrapper<T: Codable>: Codable {
enum CodingKeys: String, CodingKey {
case object
}
public let object: T
public init(object: T) {
self.object = object
}
}
+45 -15
View File
@@ -3,18 +3,18 @@ import Dispatch
/// Manipulate storage in a "all async" manner.
/// The completion closure will be called when operation completes.
public class AsyncStorage {
fileprivate let internalStorage: StorageAware
public class AsyncStorage<T> {
public let innerStorage: HybridStorage<T>
public let serialQueue: DispatchQueue
init(storage: StorageAware, serialQueue: DispatchQueue) {
self.internalStorage = storage
public init(storage: HybridStorage<T>, serialQueue: DispatchQueue) {
self.innerStorage = storage
self.serialQueue = serialQueue
}
}
extension AsyncStorage: AsyncStorageAware {
public func entry<T>(ofType type: T.Type, forKey key: String, completion: @escaping (Result<Entry<T>>) -> Void) {
extension AsyncStorage {
public func entry(forKey key: String, completion: @escaping (Result<Entry<T>>) -> Void) {
serialQueue.async { [weak self] in
guard let `self` = self else {
completion(Result.error(StorageError.deallocated))
@@ -22,7 +22,7 @@ extension AsyncStorage: AsyncStorageAware {
}
do {
let anEntry = try self.internalStorage.entry(ofType: type, forKey: key)
let anEntry = try self.innerStorage.entry(forKey: key)
completion(Result.value(anEntry))
} catch {
completion(Result.error(error))
@@ -38,7 +38,7 @@ extension AsyncStorage: AsyncStorageAware {
}
do {
try self.internalStorage.removeObject(forKey: key)
try self.innerStorage.removeObject(forKey: key)
completion(Result.value(()))
} catch {
completion(Result.error(error))
@@ -46,10 +46,11 @@ extension AsyncStorage: AsyncStorageAware {
}
}
public func setObject<T: Codable>(_ object: T,
forKey key: String,
expiry: Expiry? = nil,
completion: @escaping (Result<()>) -> Void) {
public func setObject(
_ object: T,
forKey key: String,
expiry: Expiry? = nil,
completion: @escaping (Result<()>) -> Void) {
serialQueue.async { [weak self] in
guard let `self` = self else {
completion(Result.error(StorageError.deallocated))
@@ -57,7 +58,7 @@ extension AsyncStorage: AsyncStorageAware {
}
do {
try self.internalStorage.setObject(object, forKey: key, expiry: expiry)
try self.innerStorage.setObject(object, forKey: key, expiry: expiry)
completion(Result.value(()))
} catch {
completion(Result.error(error))
@@ -73,7 +74,7 @@ extension AsyncStorage: AsyncStorageAware {
}
do {
try self.internalStorage.removeAll()
try self.innerStorage.removeAll()
completion(Result.value(()))
} catch {
completion(Result.error(error))
@@ -89,11 +90,40 @@ extension AsyncStorage: AsyncStorageAware {
}
do {
try self.internalStorage.removeExpiredObjects()
try self.innerStorage.removeExpiredObjects()
completion(Result.value(()))
} catch {
completion(Result.error(error))
}
}
}
public func object(forKey key: String, completion: @escaping (Result<T>) -> Void) {
entry(forKey: key, completion: { (result: Result<Entry<T>>) in
completion(result.map({ entry in
return entry.object
}))
})
}
public func existsObject(
forKey key: String,
completion: @escaping (Result<Bool>) -> Void) {
object(forKey: key, completion: { (result: Result<T>) in
completion(result.map({ _ in
return true
}))
})
}
}
public extension AsyncStorage {
func transform<U>(transformer: Transformer<U>) -> AsyncStorage<U> {
let storage = AsyncStorage<U>(
storage: innerStorage.transform(transformer: transformer),
serialQueue: serialQueue
)
return storage
}
}
@@ -1,82 +0,0 @@
import Foundation
import Dispatch
/// A protocol used for saving and loading from storage in async manner.
public protocol AsyncStorageAware: class {
/// All async operation must act on a serial queue.
var serialQueue: DispatchQueue { get }
/**
Tries to retrieve the object from the storage.
- Parameter key: Unique key to identify the object in the cache.
- Parameter completion: Triggered until the operation completes.
*/
func object<T: Codable>(ofType type: T.Type, forKey key: String, completion: @escaping (Result<T>) -> Void)
/**
Get cache entry which includes object with metadata.
- Parameter key: Unique key to identify the object in the cache
- Parameter completion: Triggered until the operation completes.
*/
func entry<T>(ofType type: T.Type, forKey key: String, completion: @escaping (Result<Entry<T>>) -> Void)
/**
Removes the object by the given key.
- Parameter key: Unique key to identify the object.
- Parameter completion: Triggered until the operation completes.
*/
func removeObject(forKey key: String, completion: @escaping (Result<()>) -> Void)
/**
Saves passed object.
- Parameter key: Unique key to identify the object in the cache.
- Parameter object: Object that needs to be cached.
- Parameter expiry: Overwrite expiry for this object only.
- Parameter completion: Triggered until the operation completes.
*/
func setObject<T: Codable>(_ object: T,
forKey key: String,
expiry: Expiry?,
completion: @escaping (Result<()>) -> Void)
/**
Check if an object exist by the given key.
- Parameter key: Unique key to identify the object.
- Parameter completion: Triggered until the operation completes.
*/
func existsObject<T: Codable>(ofType type: T.Type,
forKey key: String,
completion: @escaping (Result<Bool>) -> Void)
/**
Removes all objects from the cache storage.
- Parameter completion: Triggered until the operation completes.
*/
func removeAll(completion: @escaping (Result<()>) -> Void)
/**
Clears all expired objects.
- Parameter completion: Triggered until the operation completes.
*/
func removeExpiredObjects(completion: @escaping (Result<()>) -> Void)
}
public extension AsyncStorageAware {
func object<T: Codable>(ofType type: T.Type, forKey key: String, completion: @escaping (Result<T>) -> Void) {
entry(ofType: type, forKey: key, completion: { (result: Result<Entry<T>>) in
completion(result.map({ entry in
return entry.object
}))
})
}
func existsObject<T: Codable>(ofType type: T.Type,
forKey key: String,
completion: @escaping (Result<Bool>) -> Void) {
object(ofType: type, forKey: key, completion: { (result: Result<T>) in
completion(result.map({ _ in
return true
}))
})
}
}
+38 -18
View File
@@ -1,54 +1,61 @@
import Foundation
/// Save objects to file on disk
final class DiskStorage {
final public class DiskStorage<T> {
enum Error: Swift.Error {
case fileEnumeratorFailed
}
/// File manager to read/write to the disk
fileprivate let fileManager: FileManager
public let fileManager: FileManager
/// Configuration
fileprivate let config: DiskConfig
/// The computed path `directory+name`
let path: String
public let path: String
private let transformer: Transformer<T>
// MARK: - Initialization
required init(config: DiskConfig, fileManager: FileManager = FileManager.default) throws {
public required init(config: DiskConfig, fileManager: FileManager = FileManager.default, transformer: Transformer<T>) throws {
self.config = config
self.fileManager = fileManager
self.transformer = transformer
let url: URL
if let directory = config.directory {
url = directory
} else {
url = try fileManager.url(
for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
}
// path
path = url.appendingPathComponent(config.name, isDirectory: true).path
self.path = url.appendingPathComponent(config.name, isDirectory: true).path
try createDirectory()
// protection
#if os(iOS) || os(tvOS)
if let protectionType = config.protectionType {
try setDirectoryAttributes([
FileAttributeKey.protectionKey: protectionType
])
}
if let protectionType = config.protectionType {
try setDirectoryAttributes([
FileAttributeKey.protectionKey: protectionType
])
}
#endif
}
}
extension DiskStorage: StorageAware {
func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
public func entry(forKey key: String) throws -> Entry<T> {
let filePath = makeFilePath(for: key)
let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
let attributes = try fileManager.attributesOfItem(atPath: filePath)
let object: T = try DataSerializer.deserialize(data: data)
let object = try transformer.fromData(data)
guard let date = attributes[.modificationDate] as? Date else {
throw StorageError.malformedFileAttributes
@@ -65,24 +72,24 @@ extension DiskStorage: StorageAware {
)
}
func setObject<T: Codable>(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
let expiry = expiry ?? config.expiry
let data = try DataSerializer.serialize(object: object)
let data = try transformer.toData(object)
let filePath = makeFilePath(for: key)
_ = fileManager.createFile(atPath: filePath, contents: data, attributes: nil)
try fileManager.setAttributes([.modificationDate: expiry.date], ofItemAtPath: filePath)
}
func removeObject(forKey key: String) throws {
public func removeObject(forKey key: String) throws {
try fileManager.removeItem(atPath: makeFilePath(for: key))
}
func removeAll() throws {
public func removeAll() throws {
try fileManager.removeItem(atPath: path)
try createDirectory()
}
func removeExpiredObjects() throws {
public func removeExpiredObjects() throws {
let storageURL = URL(fileURLWithPath: path)
let resourceKeys: [URLResourceKey] = [
.isDirectoryKey,
@@ -229,3 +236,16 @@ extension DiskStorage {
}
}
}
public extension DiskStorage {
func transform<U>(transformer: Transformer<U>) -> DiskStorage<U> {
// swiftlint:disable force_try
let storage = try! DiskStorage<U>(
config: config,
fileManager: fileManager,
transformer: transformer
)
return storage
}
}
+22 -11
View File
@@ -1,45 +1,56 @@
import Foundation
/// Use both memory and disk storage. Try on memory first.
class HybridStorage {
let memoryStorage: MemoryStorage
let diskStorage: DiskStorage
public class HybridStorage<T> {
public let memoryStorage: MemoryStorage<T>
public let diskStorage: DiskStorage<T>
init(memoryStorage: MemoryStorage, diskStorage: DiskStorage) {
public init(memoryStorage: MemoryStorage<T>, diskStorage: DiskStorage<T>) {
self.memoryStorage = memoryStorage
self.diskStorage = diskStorage
}
}
extension HybridStorage: StorageAware {
func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
public func entry(forKey key: String) throws -> Entry<T> {
do {
return try memoryStorage.entry(ofType: type, forKey: key)
return try memoryStorage.entry(forKey: key)
} catch {
let entry = try diskStorage.entry(ofType: type, forKey: key)
let entry = try diskStorage.entry(forKey: key)
// set back to memoryStorage
memoryStorage.setObject(entry.object, forKey: key, expiry: entry.expiry)
return entry
}
}
func removeObject(forKey key: String) throws {
public func removeObject(forKey key: String) throws {
memoryStorage.removeObject(forKey: key)
try diskStorage.removeObject(forKey: key)
}
func setObject<T: Codable>(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
memoryStorage.setObject(object, forKey: key, expiry: expiry)
try diskStorage.setObject(object, forKey: key, expiry: expiry)
}
func removeAll() throws {
public func removeAll() throws {
memoryStorage.removeAll()
try diskStorage.removeAll()
}
func removeExpiredObjects() throws {
public func removeExpiredObjects() throws {
memoryStorage.removeExpiredObjects()
try diskStorage.removeExpiredObjects()
}
}
public extension HybridStorage {
func transform<U>(transformer: Transformer<U>) -> HybridStorage<U> {
let storage = HybridStorage<U>(
memoryStorage: memoryStorage.transform(),
diskStorage: diskStorage.transform(transformer: transformer)
)
return storage
}
}
+38 -59
View File
@@ -1,24 +1,50 @@
import Foundation
/// Save objects to memory based on NSCache
final class MemoryStorage {
/// Memory cache instance
public class MemoryStorage<T>: StorageAware {
fileprivate let cache = NSCache<NSString, MemoryCapsule>()
// Memory cache keys
fileprivate var keys = Set<String>()
/// Configuration
fileprivate let config: MemoryConfig
// MARK: - Initialization
init(config: MemoryConfig) {
public init(config: MemoryConfig) {
self.config = config
self.cache.countLimit = Int(config.countLimit)
self.cache.totalCostLimit = Int(config.totalCostLimit)
}
}
extension MemoryStorage: StorageAware {
func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
extension MemoryStorage {
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) {
let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date))
cache.setObject(capsule, forKey: NSString(string: key))
keys.insert(key)
}
public func removeAll() {
cache.removeAllObjects()
keys.removeAll()
}
public func removeExpiredObjects() {
let allKeys = keys
for key in allKeys {
removeObjectIfExpired(forKey: key)
}
}
public func removeObjectIfExpired(forKey key: String) {
if let capsule = cache.object(forKey: NSString(string: key)), capsule.expiry.isExpired {
removeObject(forKey: key)
}
}
public func removeObject(forKey key: String) {
cache.removeObject(forKey: NSString(string: key))
keys.remove(key)
}
public func entry(forKey key: String) throws -> Entry<T> {
guard let capsule = cache.object(forKey: NSString(string: key)) else {
throw StorageError.notFound
}
@@ -29,58 +55,11 @@ extension MemoryStorage: StorageAware {
return Entry(object: object, expiry: capsule.expiry)
}
func removeObject(forKey key: String) {
cache.removeObject(forKey: NSString(string: key))
keys.remove(key)
}
func setObject<T: Codable>(_ object: T, forKey key: String, expiry: Expiry? = nil) {
let capsule = MemoryCapsule(value: object, expiry: .date(expiry?.date ?? config.expiry.date))
cache.setObject(capsule, forKey: NSString(string: key))
keys.insert(key)
}
func removeAll() {
cache.removeAllObjects()
keys.removeAll()
}
func removeExpiredObjects() {
let allKeys = keys
for key in allKeys {
removeObjectIfExpired(forKey: key)
}
}
}
extension MemoryStorage {
/**
Removes the object from the cache if it's expired.
- Parameter key: Unique key to identify the object in the cache
*/
func removeObjectIfExpired(forKey key: String) {
if let capsule = cache.object(forKey: NSString(string: key)), capsule.expiry.isExpired {
removeObject(forKey: key)
}
}
}
/// Helper class to hold cached instance and expiry date.
/// Used in memory storage to work with NSCache.
class MemoryCapsule: NSObject {
/// Object to be cached
let object: Any
/// Expiration date
let expiry: Expiry
/**
Creates a new instance of Capsule.
- Parameter value: Object to be cached
- Parameter expiry: Expiration date
*/
init(value: Any, expiry: Expiry) {
self.object = value
self.expiry = expiry
public extension MemoryStorage {
func transform<U>() -> MemoryStorage<U> {
let storage = MemoryStorage<U>(config: config)
return storage
}
}
@@ -0,0 +1,18 @@
import Foundation
public extension Storage {
func transformData() -> Storage<Data> {
let storage = transform(transformer: TransformerFactory.forData())
return storage
}
func transformImage() -> Storage<Image> {
let storage = transform(transformer: TransformerFactory.forImage())
return storage
}
func transformCodable<U: Codable>(ofType: U.Type) -> Storage<U> {
let storage = transform(transformer: TransformerFactory.forCodable(ofType: U.self))
return storage
}
}
+44 -31
View File
@@ -3,63 +3,76 @@ import Dispatch
/// Manage storage. Use memory storage if specified.
/// Synchronous by default. Use `async` for asynchronous operations.
public class Storage {
public class Storage<T> {
/// Used for sync operations
fileprivate let sync: StorageAware
/// Storage used internally by both sync and async storages
private let interalStorage: StorageAware
let syncStorage: SyncStorage<T>
let asyncStorage: AsyncStorage<T>
/// Initialize storage with configuration options.
///
/// - Parameters:
/// - diskConfig: Configuration for disk storage
/// - memoryConfig: Optional. Pass confi if you want memory cache
/// - memoryConfig: Optional. Pass config if you want memory cache
/// - Throws: Throw StorageError if any.
public required init(diskConfig: DiskConfig, memoryConfig: MemoryConfig? = nil) throws {
// Disk or Hybrid
let storage: StorageAware
let disk = try DiskStorage(config: diskConfig)
public convenience init(diskConfig: DiskConfig, memoryConfig: MemoryConfig, transformer: Transformer<T>) throws {
let disk = try DiskStorage(config: diskConfig, transformer: transformer)
let memory = MemoryStorage<T>(config: memoryConfig)
if let memoryConfig = memoryConfig {
let memory = MemoryStorage(config: memoryConfig)
storage = HybridStorage(memoryStorage: memory, diskStorage: disk)
} else {
storage = disk
}
let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk)
let syncStorage = SyncStorage(
storage: hybridStorage,
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue")
)
// Wrapper
self.interalStorage = TypeWrapperStorage(storage: storage)
let asyncStorage = AsyncStorage(
storage: hybridStorage,
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue")
)
// Sync
self.sync = SyncStorage(storage: interalStorage,
serialQueue: DispatchQueue(label: "Cache.SyncStorage.SerialQueue"))
self.init(syncStorage: syncStorage, asyncStorage: asyncStorage)
}
/// Initialise with sync and async storages
///
/// - Parameter syncStorage: Synchronous storage
/// - Paraeter: asyncStorage: Asynchronous storage
public required init(syncStorage: SyncStorage<T>, asyncStorage: AsyncStorage<T>) {
self.syncStorage = syncStorage
self.asyncStorage = asyncStorage
}
/// Used for async operations
public lazy var async: AsyncStorage = AsyncStorage(storage: self.interalStorage,
serialQueue: DispatchQueue(label: "Cache.AsyncStorage.SerialQueue"))
public lazy var async = self.asyncStorage
}
extension Storage: StorageAware {
public func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
return try self.sync.entry(ofType: type, forKey: key)
public func entry(forKey key: String) throws -> Entry<T> {
return try self.syncStorage.entry(forKey: key)
}
public func removeObject(forKey key: String) throws {
try self.sync.removeObject(forKey: key)
try self.syncStorage.removeObject(forKey: key)
}
public func setObject<T: Codable>(_ object: T, forKey key: String,
expiry: Expiry? = nil) throws {
try self.sync.setObject(object, forKey: key, expiry: expiry)
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
try self.syncStorage.setObject(object, forKey: key, expiry: expiry)
}
public func removeAll() throws {
try self.sync.removeAll()
try self.syncStorage.removeAll()
}
public func removeExpiredObjects() throws {
try self.sync.removeExpiredObjects()
try self.syncStorage.removeExpiredObjects()
}
}
public extension Storage {
func transform<U>(transformer: Transformer<U>) -> Storage<U> {
let storage = Storage<U>(
syncStorage: syncStorage.transform(transformer: transformer),
asyncStorage: asyncStorage.transform(transformer: transformer)
)
return storage
}
}
+13 -12
View File
@@ -2,19 +2,20 @@ import Foundation
/// A protocol used for saving and loading from storage
public protocol StorageAware {
associatedtype T
/**
Tries to retrieve the object from the storage.
- Parameter key: Unique key to identify the object in the cache
- Returns: Cached object or nil if not found
*/
func object<T: Codable>(ofType type: T.Type, forKey key: String) throws -> T
func object(forKey key: String) throws -> T
/**
Get cache entry which includes object with metadata.
- Parameter key: Unique key to identify the object in the cache
- Returns: Object wrapper with metadata or nil if not found
*/
func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T>
func entry(forKey key: String) throws -> Entry<T>
/**
Removes the object by the given key.
@@ -28,13 +29,13 @@ public protocol StorageAware {
- Parameter object: Object that needs to be cached.
- Parameter expiry: Overwrite expiry for this object only.
*/
func setObject<T: Codable>(_ object: T, forKey key: String, expiry: Expiry?) throws
func setObject(_ object: T, forKey key: String, expiry: Expiry?) throws
/**
Check if an object exist by the given key.
- Parameter key: Unique key to identify the object.
*/
func existsObject<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Bool
func existsObject(forKey key: String) throws -> Bool
/**
Removes all objects from the cache storage.
@@ -49,27 +50,27 @@ public protocol StorageAware {
/**
Check if an expired object by the given key.
- Parameter key: Unique key to identify the object.
*/
func isExpiredObject<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Bool
*/
func isExpiredObject(forKey key: String) throws -> Bool
}
public extension StorageAware {
func object<T: Codable>(ofType type: T.Type, forKey key: String) throws -> T {
return try entry(ofType: type, forKey: key).object
func object(forKey key: String) throws -> T {
return try entry(forKey: key).object
}
func existsObject<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Bool {
func existsObject(forKey key: String) throws -> Bool {
do {
let _: T = try object(ofType: type, forKey: key)
let _: T = try object(forKey: key)
return true
} catch {
return false
}
}
func isExpiredObject<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Bool {
func isExpiredObject(forKey key: String) throws -> Bool {
do {
let entry = try self.entry(ofType: type, forKey: key)
let entry = try self.entry(forKey: key)
return entry.expiry.isExpired
} catch {
return true
+23 -13
View File
@@ -3,21 +3,21 @@ import Dispatch
/// Manipulate storage in a "all sync" manner.
/// Block the current queue until the operation completes.
public class SyncStorage {
fileprivate let internalStorage: StorageAware
fileprivate let serialQueue: DispatchQueue
public class SyncStorage<T> {
public let innerStorage: HybridStorage<T>
public let serialQueue: DispatchQueue
init(storage: StorageAware, serialQueue: DispatchQueue) {
self.internalStorage = storage
public init(storage: HybridStorage<T>, serialQueue: DispatchQueue) {
self.innerStorage = storage
self.serialQueue = serialQueue
}
}
extension SyncStorage: StorageAware {
public func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
public func entry(forKey key: String) throws -> Entry<T> {
var entry: Entry<T>!
try serialQueue.sync {
entry = try internalStorage.entry(ofType: type, forKey: key)
entry = try innerStorage.entry(forKey: key)
}
return entry
@@ -25,26 +25,36 @@ extension SyncStorage: StorageAware {
public func removeObject(forKey key: String) throws {
try serialQueue.sync {
try self.internalStorage.removeObject(forKey: key)
try self.innerStorage.removeObject(forKey: key)
}
}
public func setObject<T: Codable>(_ object: T, forKey key: String,
expiry: Expiry? = nil) throws {
public func setObject(_ object: T, forKey key: String, expiry: Expiry? = nil) throws {
try serialQueue.sync {
try internalStorage.setObject(object, forKey: key, expiry: expiry)
try innerStorage.setObject(object, forKey: key, expiry: expiry)
}
}
public func removeAll() throws {
try serialQueue.sync {
try internalStorage.removeAll()
try innerStorage.removeAll()
}
}
public func removeExpiredObjects() throws {
try serialQueue.sync {
try internalStorage.removeExpiredObjects()
try innerStorage.removeExpiredObjects()
}
}
}
public extension SyncStorage {
func transform<U>(transformer: Transformer<U>) -> SyncStorage<U> {
let storage = SyncStorage<U>(
storage: innerStorage.transform(transformer: transformer),
serialQueue: serialQueue
)
return storage
}
}
@@ -1,50 +0,0 @@
import Foundation
/// Deal with top level primitive. Use TypeWrapper as wrapper
/// Because we use `JSONEncoder` and `JSONDecoder`.
/// Avoid issue like "Top-level T encoded as number JSON fragment"
final class TypeWrapperStorage {
let internalStorage: StorageAware
init(storage: StorageAware) {
self.internalStorage = storage
}
}
extension TypeWrapperStorage: StorageAware {
public func entry<T: Codable>(ofType type: T.Type, forKey key: String) throws -> Entry<T> {
let wrapperEntry = try internalStorage.entry(ofType: TypeWrapper<T>.self, forKey: key)
return Entry(object: wrapperEntry.object.object, expiry: wrapperEntry.expiry, meta: wrapperEntry.meta)
}
public func removeObject(forKey key: String) throws {
try internalStorage.removeObject(forKey: key)
}
public func setObject<T: Codable>(_ object: T, forKey key: String,
expiry: Expiry? = nil) throws {
let wrapper = TypeWrapper<T>(object: object)
try internalStorage.setObject(wrapper, forKey: key, expiry: expiry)
}
public func removeAll() throws {
try internalStorage.removeAll()
}
public func removeExpiredObjects() throws {
try internalStorage.removeExpiredObjects()
}
}
/// Used to wrap Codable object
struct TypeWrapper<T: Codable>: Codable {
enum CodingKeys: String, CodingKey {
case object
}
let object: T
init(object: T) {
self.object = object
}
}
@@ -2,12 +2,15 @@ import XCTest
@testable import Cache
final class JSONDecoderExtensionsTests: XCTestCase {
private var storage: HybridStorage!
private var storage: HybridStorage<User>!
override func setUp() {
super.setUp()
let memory = MemoryStorage(config: MemoryConfig())
let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk"))
let memory = MemoryStorage<User>(config: MemoryConfig())
let disk = try! DiskStorage<User>(
config: DiskConfig(name: "HybridDisk"),
transformer: TransformerFactory.forCodable(ofType: User.self)
)
storage = HybridStorage(memoryStorage: memory, diskStorage: disk)
}
@@ -26,7 +29,7 @@ final class JSONDecoderExtensionsTests: XCTestCase {
let user = try JSONDecoder.decode(json, to: User.self)
try storage.setObject(user, forKey: "user")
let cachedObject = try storage.object(ofType: User.self, forKey: "user")
let cachedObject = try storage.object(forKey: "user")
XCTAssertEqual(user, cachedObject)
}
@@ -36,7 +39,7 @@ final class JSONDecoderExtensionsTests: XCTestCase {
let user = try JSONDecoder.decode(string, to: User.self)
try storage.setObject(user, forKey: "user")
let cachedObject = try storage.object(ofType: User.self, forKey: "user")
let cachedObject = try storage.object(forKey: "user")
XCTAssertEqual(cachedObject.firstName, "John")
}
}
+10 -10
View File
@@ -3,16 +3,15 @@ import Dispatch
@testable import Cache
final class AsyncStorageTests: XCTestCase {
private var storage: AsyncStorage!
private var storage: AsyncStorage<User>!
let user = User(firstName: "John", lastName: "Snow")
override func setUp() {
super.setUp()
let memory = MemoryStorage(config: MemoryConfig())
let disk = try! DiskStorage(config: DiskConfig(name: "Async Disk"))
let hybrid = HybridStorage(memoryStorage: memory, diskStorage: disk)
let primitive = TypeWrapperStorage(storage: hybrid)
storage = AsyncStorage(storage: primitive, serialQueue: DispatchQueue(label: "Async"))
let memory = MemoryStorage<User>(config: MemoryConfig())
let disk = try! DiskStorage<User>(config: DiskConfig(name: "Async Disk"), transformer: TransformerFactory.forCodable(ofType: User.self))
let hybrid = HybridStorage<User>(memoryStorage: memory, diskStorage: disk)
storage = AsyncStorage(storage: hybrid, serialQueue: DispatchQueue(label: "Async"))
}
override func tearDown() {
@@ -24,7 +23,7 @@ final class AsyncStorageTests: XCTestCase {
let expectation = self.expectation(description: #function)
storage.setObject(user, forKey: "user", completion: { _ in })
storage.object(ofType: User.self, forKey: "user", completion: { result in
storage.object(forKey: "user", completion: { result in
switch result {
case .value(let cachedUser):
XCTAssertEqual(cachedUser, self.user)
@@ -38,19 +37,20 @@ final class AsyncStorageTests: XCTestCase {
}
func testRemoveAll() {
let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
let expectation = self.expectation(description: #function)
given("add a lot of objects") {
Array(0..<100).forEach {
storage.setObject($0, forKey: "key-\($0)", completion: { _ in })
intStorage.setObject($0, forKey: "key-\($0)", completion: { _ in })
}
}
when("remove all") {
storage.removeAll(completion: { _ in })
intStorage.removeAll(completion: { _ in })
}
then("all are removed") {
storage.existsObject(ofType: Int.self, forKey: "key-99", completion: { result in
intStorage.existsObject(forKey: "key-99", completion: { result in
switch result {
case .value:
XCTFail()
+13 -13
View File
@@ -5,12 +5,12 @@ final class DiskStorageTests: XCTestCase {
private let key = "youknownothing"
private let testObject = User(firstName: "John", lastName: "Snow")
private let fileManager = FileManager()
private var storage: DiskStorage!
private var storage: DiskStorage<User>!
private let config = DiskConfig(name: "Floppy")
override func setUp() {
super.setUp()
storage = try! DiskStorage(config: config)
storage = try! DiskStorage<User>(config: config, transformer: TransformerFactory.forCodable(ofType: User.self))
}
override func tearDown() {
@@ -30,7 +30,7 @@ final class DiskStorageTests: XCTestCase {
/// Test that it returns the correct path
func testDefaultPath() {
let paths = NSSearchPathForDirectoriesInDomains(
.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true
.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true
)
let path = "\(paths.first!)/\(config.name.capitalized)"
XCTAssertEqual(storage.path, path)
@@ -47,7 +47,7 @@ final class DiskStorageTests: XCTestCase {
let customConfig = DiskConfig(name: "SSD", directory: url)
storage = try DiskStorage(config: customConfig)
storage = try DiskStorage<User>(config: customConfig, transformer: TransformerFactory.forCodable(ofType: User.self))
XCTAssertEqual(
storage.path,
@@ -77,13 +77,13 @@ final class DiskStorageTests: XCTestCase {
// Returns nil if entry doesn't exist
var entry: Entry<User>?
do {
entry = try storage.entry(ofType: User.self, forKey: key)
entry = try storage.entry(forKey: key)
} catch {}
XCTAssertNil(entry)
// Returns entry if object exists
try storage.setObject(testObject, forKey: key)
entry = try storage.entry(ofType: User.self, forKey: key)
entry = try storage.entry(forKey: key)
let attributes = try fileManager.attributesOfItem(atPath: storage.makeFilePath(for: key))
let expiry = Expiry.date(attributes[FileAttributeKey.modificationDate] as! Date)
@@ -95,7 +95,7 @@ final class DiskStorageTests: XCTestCase {
/// Test that it resolves cached object
func testSetObject() throws {
try storage.setObject(testObject, forKey: key)
let cachedObject: User? = try storage.object(ofType: User.self, forKey: key)
let cachedObject: User? = try storage.object(forKey: key)
XCTAssertEqual(cachedObject?.firstName, testObject.firstName)
XCTAssertEqual(cachedObject?.lastName, testObject.lastName)
@@ -116,7 +116,7 @@ final class DiskStorageTests: XCTestCase {
try storage.removeObjectIfExpired(forKey: key)
var cachedObject: User?
do {
cachedObject = try storage.object(ofType: User.self, forKey: key)
cachedObject = try storage.object(forKey: key)
} catch {}
XCTAssertNil(cachedObject)
@@ -126,16 +126,16 @@ final class DiskStorageTests: XCTestCase {
func testRemoveObjectIfExpiredWhenNotExpired() throws {
try storage.setObject(testObject, forKey: key)
try storage.removeObjectIfExpired(forKey: key)
let cachedObject: User? = try storage.object(ofType: User.self, forKey: key)
let cachedObject: User? = try storage.object(forKey: key)
XCTAssertNotNil(cachedObject)
}
/// Test expired object
func testExpiredObject() throws {
try storage.setObject(testObject, forKey: key, expiry: .seconds(0.9))
XCTAssertFalse(try! storage.isExpiredObject(ofType: User.self, forKey: key))
XCTAssertFalse(try! storage.isExpiredObject(forKey: key))
sleep(1)
XCTAssertTrue(try! storage.isExpiredObject(ofType: User.self, forKey: key))
XCTAssertTrue(try! storage.isExpiredObject(forKey: key))
}
/// Test that it clears cache directory
@@ -185,10 +185,10 @@ final class DiskStorageTests: XCTestCase {
try storage.setObject(testObject, forKey: key2, expiry: expiry2)
try storage.removeExpiredObjects()
var object1: User?
let object2 = try storage.object(ofType: User.self, forKey: key2)
let object2 = try storage.object(forKey: key2)
do {
object1 = try storage.object(ofType: User.self, forKey: key1)
object1 = try storage.object(forKey: key1)
} catch {}
XCTAssertNil(object1)
@@ -5,13 +5,13 @@ final class HybridStorageTests: XCTestCase {
private let cacheName = "WeirdoCache"
private let key = "alongweirdkey"
private let testObject = User(firstName: "John", lastName: "Targaryen")
private var storage: HybridStorage!
private var storage: HybridStorage<User>!
private let fileManager = FileManager()
override func setUp() {
super.setUp()
let memory = MemoryStorage(config: MemoryConfig())
let disk = try! DiskStorage(config: DiskConfig(name: "HybridDisk"))
let memory = MemoryStorage<User>(config: MemoryConfig())
let disk = try! DiskStorage<User>(config: DiskConfig(name: "HybridDisk"), transformer: TransformerFactory.forCodable(ofType: User.self))
storage = HybridStorage(memoryStorage: memory, diskStorage: disk)
}
@@ -24,17 +24,17 @@ final class HybridStorageTests: XCTestCase {
func testSetObject() throws {
try when("set to storage") {
try storage.setObject(testObject, forKey: key)
let cachedObject = try storage.object(ofType: User.self, forKey: key)
let cachedObject = try storage.object(forKey: key)
XCTAssertEqual(cachedObject, testObject)
}
try then("it is set to memory too") {
let memoryObject = try storage.memoryStorage.object(ofType: User.self, forKey: key)
let memoryObject = try storage.memoryStorage.object(forKey: key)
XCTAssertNotNil(memoryObject)
}
try then("it is set to disk too") {
let diskObject = try storage.diskStorage.object(ofType: User.self, forKey: key)
let diskObject = try storage.diskStorage.object(forKey: key)
XCTAssertNotNil(diskObject)
}
}
@@ -42,7 +42,7 @@ final class HybridStorageTests: XCTestCase {
func testEntry() throws {
let expiryDate = Date()
try storage.setObject(testObject, forKey: key, expiry: .date(expiryDate))
let entry = try storage.entry(ofType: User.self, forKey: key)
let entry = try storage.entry(forKey: key)
XCTAssertEqual(entry.object, testObject)
XCTAssertEqual(entry.expiry.date, expiryDate)
@@ -52,12 +52,12 @@ final class HybridStorageTests: XCTestCase {
func testObjectCopyToMemory() throws {
try when("set to disk only") {
try storage.diskStorage.setObject(testObject, forKey: key)
let cachedObject: User = try storage.object(ofType: User.self, forKey: key)
let cachedObject: User = try storage.object(forKey: key)
XCTAssertEqual(cachedObject, testObject)
}
try then("there is no object in memory") {
let inMemoryCachedObject = try storage.memoryStorage.object(ofType: User.self, forKey: key)
let inMemoryCachedObject = try storage.memoryStorage.object(forKey: key)
XCTAssertEqual(inMemoryCachedObject, testObject)
}
}
@@ -66,7 +66,7 @@ final class HybridStorageTests: XCTestCase {
let date = Date().addingTimeInterval(3)
try when("set to disk only") {
try storage.diskStorage.setObject(testObject, forKey: key, expiry: .seconds(3))
let entry = try storage.entry(ofType: User.self, forKey: key)
let entry = try storage.entry(forKey: key)
//accuracy for slow disk processes
XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate,
date.timeIntervalSinceReferenceDate,
@@ -74,7 +74,7 @@ final class HybridStorageTests: XCTestCase {
}
try then("there is no object in memory") {
let entry = try storage.memoryStorage.entry(ofType: User.self, forKey: key)
let entry = try storage.memoryStorage.entry(forKey: key)
//accuracy for slow disk processes
XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate,
date.timeIntervalSinceReferenceDate,
@@ -86,22 +86,22 @@ final class HybridStorageTests: XCTestCase {
func testRemoveObject() throws {
try given("set to storage") {
try storage.setObject(testObject, forKey: key)
XCTAssertNotNil(try storage.object(ofType: User.self, forKey: key))
XCTAssertNotNil(try storage.object(forKey: key))
}
try when("remove object from storage") {
try storage.removeObject(forKey: key)
let cachedObject = try? storage.object(ofType: User.self, forKey: key)
let cachedObject = try? storage.object(forKey: key)
XCTAssertNil(cachedObject)
}
then("there is no object in memory") {
let memoryObject = try? storage.memoryStorage.object(ofType: User.self, forKey: key)
let memoryObject = try? storage.memoryStorage.object(forKey: key)
XCTAssertNil(memoryObject)
}
then("there is no object on disk") {
let diskObject = try? storage.diskStorage.object(ofType: User.self, forKey: key)
let diskObject = try? storage.diskStorage.object(forKey: key)
XCTAssertNil(diskObject)
}
}
@@ -111,16 +111,16 @@ final class HybridStorageTests: XCTestCase {
try when("set and remove all") {
try storage.setObject(testObject, forKey: key)
try storage.removeAll()
XCTAssertNil(try? storage.object(ofType: User.self, forKey: key))
XCTAssertNil(try? storage.object(forKey: key))
}
then("there is no object in memory") {
let memoryObject = try? storage.memoryStorage.object(ofType: User.self, forKey: key)
let memoryObject = try? storage.memoryStorage.object(forKey: key)
XCTAssertNil(memoryObject)
}
then("there is no object on disk") {
let diskObject = try? storage.diskStorage.object(ofType: User.self, forKey: key)
let diskObject = try? storage.diskStorage.object(forKey: key)
XCTAssertNil(diskObject)
}
}
@@ -152,8 +152,8 @@ final class HybridStorageTests: XCTestCase {
}
then("object with key2 survived") {
XCTAssertNil(try? storage.object(ofType: User.self, forKey: key1))
XCTAssertNotNil(try? storage.object(ofType: User.self, forKey: key2))
XCTAssertNil(try? storage.object(forKey: key1))
XCTAssertNotNil(try? storage.object(forKey: key2))
}
}
}
@@ -4,12 +4,12 @@ import XCTest
final class MemoryStorageTests: XCTestCase {
private let key = "youknownothing"
private let testObject = User(firstName: "John", lastName: "Snow")
private var storage: MemoryStorage!
private var storage: MemoryStorage<User>!
private let config = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
override func setUp() {
super.setUp()
storage = MemoryStorage(config: config)
storage = MemoryStorage<User>(config: config)
}
override func tearDown() {
@@ -20,7 +20,7 @@ final class MemoryStorageTests: XCTestCase {
/// Test that it saves an object
func testSetObject() {
storage.setObject(testObject, forKey: key)
let cachedObject = try! storage.object(ofType: User.self, forKey: key)
let cachedObject = try! storage.object(forKey: key)
XCTAssertNotNil(cachedObject)
XCTAssertEqual(cachedObject.firstName, testObject.firstName)
XCTAssertEqual(cachedObject.lastName, testObject.lastName)
@@ -28,12 +28,12 @@ final class MemoryStorageTests: XCTestCase {
func testCacheEntry() {
// Returns nil if entry doesn't exist
var entry = try? storage.entry(ofType: User.self, forKey: key)
var entry = try? storage.entry(forKey: key)
XCTAssertNil(entry)
// Returns entry if object exists
storage.setObject(testObject, forKey: key)
entry = try! storage.entry(ofType: User.self, forKey: key)
entry = try! storage.entry(forKey: key)
XCTAssertEqual(entry?.object.firstName, testObject.firstName)
XCTAssertEqual(entry?.object.lastName, testObject.lastName)
@@ -43,13 +43,13 @@ final class MemoryStorageTests: XCTestCase {
func testSetObjectWithExpiry() {
let date = Date().addingTimeInterval(1)
storage.setObject(testObject, forKey: key, expiry: .seconds(1))
var entry = try! storage.entry(ofType: User.self, forKey: key)
var entry = try! storage.entry(forKey: key)
XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate,
date.timeIntervalSinceReferenceDate,
accuracy: 0.1)
//Timer vs sleep: do not complicate
sleep(1)
entry = try! storage.entry(ofType: User.self, forKey: key)
entry = try! storage.entry(forKey: key)
XCTAssertEqual(entry.expiry.date.timeIntervalSinceReferenceDate,
date.timeIntervalSinceReferenceDate,
accuracy: 0.1)
@@ -59,7 +59,7 @@ final class MemoryStorageTests: XCTestCase {
func testRemoveObject() {
storage.setObject(testObject, forKey: key)
storage.removeObject(forKey: key)
let cachedObject = try? storage.object(ofType: User.self, forKey: key)
let cachedObject = try? storage.object(forKey: key)
XCTAssertNil(cachedObject)
}
@@ -68,7 +68,7 @@ final class MemoryStorageTests: XCTestCase {
let expiry: Expiry = .date(Date().addingTimeInterval(-10))
storage.setObject(testObject, forKey: key, expiry: expiry)
storage.removeObjectIfExpired(forKey: key)
let cachedObject = try? storage.object(ofType: User.self, forKey: key)
let cachedObject = try? storage.object(forKey: key)
XCTAssertNil(cachedObject)
}
@@ -77,7 +77,7 @@ final class MemoryStorageTests: XCTestCase {
func testRemoveObjectIfExpiredWhenNotExpired() {
storage.setObject(testObject, forKey: key)
storage.removeObjectIfExpired(forKey: key)
let cachedObject = try! storage.object(ofType: User.self, forKey: key)
let cachedObject = try! storage.object(forKey: key)
XCTAssertNotNil(cachedObject)
}
@@ -85,16 +85,16 @@ final class MemoryStorageTests: XCTestCase {
/// Test expired object
func testExpiredObject() throws {
storage.setObject(testObject, forKey: key, expiry: .seconds(0.9))
XCTAssertFalse(try! storage.isExpiredObject(ofType: User.self, forKey: key))
XCTAssertFalse(try! storage.isExpiredObject(forKey: key))
sleep(1)
XCTAssertTrue(try! storage.isExpiredObject(ofType: User.self, forKey: key))
XCTAssertTrue(try! storage.isExpiredObject(forKey: key))
}
/// Test that it clears cache directory
func testRemoveAll() {
storage.setObject(testObject, forKey: key)
storage.removeAll()
let cachedObject = try? storage.object(ofType: User.self, forKey: key)
let cachedObject = try? storage.object(forKey: key)
XCTAssertNil(cachedObject)
}
@@ -107,8 +107,8 @@ final class MemoryStorageTests: XCTestCase {
storage.setObject(testObject, forKey: key1, expiry: expiry1)
storage.setObject(testObject, forKey: key2, expiry: expiry2)
storage.removeExpiredObjects()
let object1 = try? storage.object(ofType: User.self, forKey: key1)
let object2 = try! storage.object(ofType: User.self, forKey: key2)
let object1 = try? storage.object(forKey: key1)
let object2 = try! storage.object(forKey: key2)
XCTAssertNil(object1)
XCTAssertNotNil(object2)
@@ -0,0 +1,205 @@
import XCTest
@testable import Cache
final class StorageSupportTests: XCTestCase {
private var storage: HybridStorage<Bool>!
override func setUp() {
super.setUp()
let memory = MemoryStorage<Bool>(config: MemoryConfig())
let disk = try! DiskStorage<Bool>(config: DiskConfig(name: "PrimitiveDisk"), transformer: TransformerFactory.forCodable(ofType: Bool.self))
storage = HybridStorage<Bool>(memoryStorage: memory, diskStorage: disk)
}
override func tearDown() {
try? storage.removeAll()
super.tearDown()
}
func testSetPrimitive() throws {
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Bool.self))
try s.setObject(true, forKey: "bool")
XCTAssertEqual(try s.object(forKey: "bool"), true)
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Bool].self))
try s.setObject([true, false, true], forKey: "array of bools")
XCTAssertEqual(try s.object(forKey: "array of bools"), [true, false, true])
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self))
try s.setObject("one", forKey: "string")
XCTAssertEqual(try s.object(forKey: "string"), "one")
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [String].self))
try s.setObject(["one", "two", "three"], forKey: "array of strings")
XCTAssertEqual(try s.object(forKey: "array of strings"), ["one", "two", "three"])
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
try s.setObject(10, forKey: "int")
XCTAssertEqual(try s.object(forKey: "int"), 10)
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Int].self))
try s.setObject([1, 2, 3], forKey: "array of ints")
XCTAssertEqual(try s.object(forKey: "array of ints"), [1, 2, 3])
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self))
let float: Float = 1.1
try s.setObject(float, forKey: "float")
XCTAssertEqual(try s.object(forKey: "float"), float)
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Float].self))
let floats: [Float] = [1.1, 1.2, 1.3]
try s.setObject(floats, forKey: "array of floats")
XCTAssertEqual(try s.object(forKey: "array of floats"), floats)
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Double.self))
let double: Double = 1.1
try s.setObject(double, forKey: "double")
XCTAssertEqual(try s.object(forKey: "double"), double)
}
do {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [Double].self))
let doubles: [Double] = [1.1, 1.2, 1.3]
try s.setObject(doubles, forKey: "array of doubles")
XCTAssertEqual(try s.object(forKey: "array of doubles"), doubles)
}
}
func testSetData() {
let s = storage.transform(transformer: TransformerFactory.forData())
do {
let string = "Hello"
let data = string.data(using: .utf8)!
try s.setObject(data, forKey: "data")
let cachedObject = try s.object(forKey: "data")
let cachedString = String(data: cachedObject, encoding: .utf8)
XCTAssertEqual(cachedString, string)
} catch {
XCTFail(error.localizedDescription)
}
}
func testSetDate() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Date.self))
let date = Date(timeIntervalSince1970: 100)
try s.setObject(date, forKey: "date")
let cachedObject = try s.object(forKey: "date")
XCTAssertEqual(date, cachedObject)
}
func testSetURL() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: URL.self))
let url = URL(string: "https://hyper.no")!
try s.setObject(url, forKey: "url")
let cachedObject = try s.object(forKey: "url")
XCTAssertEqual(url, cachedObject)
}
func testWithSet() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Set<Int>.self))
let set = Set<Int>(arrayLiteral: 1, 2, 3)
try s.setObject(set, forKey: "set")
XCTAssertEqual(try s.object(forKey: "set") as Set<Int>, set)
}
func testWithSimpleDictionary() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: [String: Int].self))
let dict: [String: Int] = [
"key1": 1,
"key2": 2
]
try s.setObject(dict, forKey: "dict")
let cachedObject = try s.object(forKey: "dict") as [String: Int]
XCTAssertEqual(cachedObject, dict)
}
func testWithComplexDictionary() {
let _: [String: Any] = [
"key1": 1,
"key2": 2
]
// fatal error: Dictionary<String, Any> does not conform to Encodable because Any does not conform to Encodable
// try storage.setObject(dict, forKey: "dict")
}
func testIntFloat() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self))
let key = "key"
try s.setObject(10, forKey: key)
try then("Casting to int or float is the same") {
XCTAssertEqual(try s.object(forKey: key), 10)
let intStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
XCTAssertEqual(try intStorage.object(forKey: key), 10)
}
}
func testFloatDouble() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: Float.self))
let key = "key"
try s.setObject(10.5, forKey: key)
try then("Casting to float or double is the same") {
XCTAssertEqual(try s.object(forKey: key), 10.5)
let doubleStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Double.self))
XCTAssertEqual(try doubleStorage.object(forKey: key), 10.5)
}
}
func testCastingToAnotherType() throws {
let s = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self))
try s.setObject("Hello", forKey: "string")
do {
let intStorage = s.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
let _ = try intStorage.object(forKey: "string")
XCTFail()
} catch {
XCTAssertTrue(error is DecodingError)
}
}
func testOverridenOnDisk() throws {
let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
let stringStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: String.self))
let key = "sameKey"
try intStorage.setObject(1, forKey: key)
try stringStorage.setObject("hello world", forKey: key)
let intValue = try? intStorage.diskStorage.object(forKey: key)
let stringValue = try? stringStorage.diskStorage.object(forKey: key)
XCTAssertNil(intValue)
XCTAssertNotNil(stringValue)
}
}
+12 -8
View File
@@ -3,14 +3,17 @@ import Dispatch
@testable import Cache
final class SyncStorageTests: XCTestCase {
private var storage: SyncStorage!
private var storage: SyncStorage<User>!
let user = User(firstName: "John", lastName: "Snow")
override func setUp() {
super.setUp()
let memory = MemoryStorage(config: MemoryConfig())
let primitive = TypeWrapperStorage(storage: memory)
storage = SyncStorage(storage: primitive, serialQueue: DispatchQueue(label: "Sync"))
let memory = MemoryStorage<User>(config: MemoryConfig())
let disk = try! DiskStorage<User>(config: DiskConfig(name: "HybridDisk"), transformer: TransformerFactory.forCodable(ofType: User.self))
let hybridStorage = HybridStorage(memoryStorage: memory, diskStorage: disk)
storage = SyncStorage(storage: hybridStorage, serialQueue: DispatchQueue(label: "Sync"))
}
override func tearDown() {
@@ -20,24 +23,25 @@ final class SyncStorageTests: XCTestCase {
func testSetObject() throws {
try storage.setObject(user, forKey: "user")
let cachedObject = try storage.object(ofType: User.self, forKey: "user")
let cachedObject = try storage.object(forKey: "user")
XCTAssertEqual(cachedObject, user)
}
func testRemoveAll() throws {
let intStorage = storage.transform(transformer: TransformerFactory.forCodable(ofType: Int.self))
try given("add a lot of objects") {
try Array(0..<100).forEach {
try storage.setObject($0, forKey: "key-\($0)")
try intStorage.setObject($0, forKey: "key-\($0)")
}
}
try when("remove all") {
try storage.removeAll()
try intStorage.removeAll()
}
try then("all are removed") {
XCTAssertFalse(try storage.existsObject(ofType: Int.self, forKey: "key-99"))
XCTAssertFalse(try intStorage.existsObject(forKey: "key-99"))
}
}
}
@@ -1,154 +0,0 @@
import XCTest
@testable import Cache
final class TypeWrapperStorageTests: XCTestCase {
private var storage: TypeWrapperStorage!
override func setUp() {
super.setUp()
let memory = MemoryStorage(config: MemoryConfig())
let disk = try! DiskStorage(config: DiskConfig(name: "PrimitiveDisk"))
let hybrid = HybridStorage(memoryStorage: memory, diskStorage: disk)
storage = TypeWrapperStorage(storage: hybrid)
}
override func tearDown() {
try? storage.removeAll()
super.tearDown()
}
func testSetPrimitive() throws {
try storage.setObject(true, forKey: "bool")
XCTAssertEqual(try storage.object(ofType: Bool.self, forKey: "bool"), true)
try storage.setObject([true, false, true], forKey: "array of bools")
XCTAssertEqual(try storage.object(ofType: [Bool].self, forKey: "array of bools"), [true, false, true])
try storage.setObject("one", forKey: "string")
XCTAssertEqual(try storage.object(ofType: String.self, forKey: "string"), "one")
try storage.setObject(["one", "two", "three"], forKey: "array of strings")
XCTAssertEqual(try storage.object(ofType: [String].self, forKey: "array of strings"), ["one", "two", "three"])
try storage.setObject(10, forKey: "int")
XCTAssertEqual(try storage.object(ofType: Int.self, forKey: "int"), 10)
try storage.setObject([1, 2, 3], forKey: "array of ints")
XCTAssertEqual(try storage.object(ofType: [Int].self, forKey: "array of ints"), [1, 2, 3])
let float: Float = 1.1
try storage.setObject(float, forKey: "float")
XCTAssertEqual(try storage.object(ofType: Float.self, forKey: "float"), float)
let floats: [Float] = [1.1, 1.2, 1.3]
try storage.setObject(floats, forKey: "array of floats")
XCTAssertEqual(try storage.object(ofType: [Float].self, forKey: "array of floats"), floats)
let double: Double = 1.1
try storage.setObject(double, forKey: "double")
XCTAssertEqual(try storage.object(ofType: Double.self, forKey: "double"), double)
let doubles: [Double] = [1.1, 1.2, 1.3]
try storage.setObject(doubles, forKey: "array of doubles")
XCTAssertEqual(try storage.object(ofType: [Double].self, forKey: "array of doubles"), doubles)
}
func testSetData() {
do {
let string = "Hello"
let data = string.data(using: .utf8)
try storage.setObject(data, forKey: "data")
let cachedObject = try storage.object(ofType: Data.self, forKey: "data")
let cachedString = String(data: cachedObject, encoding: .utf8)
XCTAssertEqual(cachedString, string)
} catch {
XCTFail(error.localizedDescription)
}
}
func testSetDate() throws {
let date = Date(timeIntervalSince1970: 100)
try storage.setObject(date, forKey: "date")
let cachedObject = try storage.object(ofType: Date.self, forKey: "date")
XCTAssertEqual(date, cachedObject)
}
func testSetURL() throws {
let url = URL(string: "https://hyper.no")
try storage.setObject(url, forKey: "url")
let cachedObject = try storage.object(ofType: URL.self, forKey: "url")
XCTAssertEqual(url, cachedObject)
}
func testWithSet() throws {
let set = Set<Int>(arrayLiteral: 1, 2, 3)
try storage.setObject(set, forKey: "set")
XCTAssertEqual(try storage.object(ofType: Set<Int>.self, forKey: "set") as Set<Int>, set)
}
func testWithSimpleDictionary() throws {
let dict: [String: Int] = [
"key1": 1,
"key2": 2
]
try storage.setObject(dict, forKey: "dict")
let cachedObject = try storage.object(ofType: [String: Int].self, forKey: "dict") as [String: Int]
XCTAssertEqual(cachedObject, dict)
}
func testWithComplexDictionary() {
let _: [String: Any] = [
"key1": 1,
"key2": 2
]
// fatal error: Dictionary<String, Any> does not conform to Encodable because Any does not conform to Encodable
// try storage.setObject(dict, forKey: "dict")
}
func testSameKey() throws {
let user = User(firstName: "John", lastName: "Snow")
let key = "keyMadeOfDragonGlass"
try storage.setObject(user, forKey: key)
try storage.setObject("Dragonstone", forKey: key)
XCTAssertNil(try? storage.object(ofType: User.self, forKey: key))
XCTAssertNotNil(try storage.object(ofType: String.self, forKey: key))
}
func testIntFloat() throws {
let key = "key"
try storage.setObject(10, forKey: key)
try then("Casting to int or float is the same") {
XCTAssertEqual(try storage.object(ofType: Int.self, forKey: key), 10)
XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10)
}
}
func testFloatDouble() throws {
let key = "key"
try storage.setObject(10.5, forKey: key)
try then("Casting to float or double is the same") {
XCTAssertEqual(try storage.object(ofType: Float.self, forKey: key), 10.5)
XCTAssertEqual(try storage.object(ofType: Double.self, forKey: key), 10.5)
}
}
func testCastingToAnotherType() throws {
try storage.setObject("Hello", forKey: "string")
do {
let cachedObject = try storage.object(ofType: Int.self, forKey: "string")
XCTAssertEqual(cachedObject, 10)
} catch {
XCTAssertTrue(error is DecodingError)
}
}
}