Skip to content

Commit a8e01d6

Browse files
committed
Init
0 parents  commit a8e01d6

14 files changed

+696
-0
lines changed

.gitignore

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
2+
# Created by https://www.toptal.com/developers/gitignore/api/swift
3+
# Edit at https://www.toptal.com/developers/gitignore?templates=swift
4+
5+
### Swift ###
6+
# Xcode
7+
#
8+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
9+
10+
## User settings
11+
xcuserdata/
12+
13+
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
14+
*.xcscmblueprint
15+
*.xccheckout
16+
17+
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
18+
build/
19+
DerivedData/
20+
*.moved-aside
21+
*.pbxuser
22+
!default.pbxuser
23+
*.mode1v3
24+
!default.mode1v3
25+
*.mode2v3
26+
!default.mode2v3
27+
*.perspectivev3
28+
!default.perspectivev3
29+
30+
## Obj-C/Swift specific
31+
*.hmap
32+
33+
## App packaging
34+
*.ipa
35+
*.dSYM.zip
36+
*.dSYM
37+
38+
## Playgrounds
39+
timeline.xctimeline
40+
playground.xcworkspace
41+
42+
# Swift Package Manager
43+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
44+
# Packages/
45+
# Package.pins
46+
# Package.resolved
47+
# *.xcodeproj
48+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
49+
# hence it is not needed unless you have added a package configuration file to your project
50+
# .swiftpm
51+
52+
.build/
53+
54+
# CocoaPods
55+
# We recommend against adding the Pods directory to your .gitignore. However
56+
# you should judge for yourself, the pros and cons are mentioned at:
57+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
58+
# Pods/
59+
# Add this line if you want to avoid checking in source code from the Xcode workspace
60+
# *.xcworkspace
61+
62+
# Carthage
63+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
64+
# Carthage/Checkouts
65+
66+
Carthage/Build/
67+
68+
# Accio dependency management
69+
Dependencies/
70+
.accio/
71+
72+
# fastlane
73+
# It is recommended to not store the screenshots in the git repo.
74+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
75+
# For more information about the recommended setup visit:
76+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
77+
78+
fastlane/report.xml
79+
fastlane/Preview.html
80+
fastlane/screenshots/**/*.png
81+
fastlane/test_output
82+
83+
# Code Injection
84+
# After new code Injection tools there's a generated folder /iOSInjectionProject
85+
# https://github.com/johnno1962/injectionforxcode
86+
87+
iOSInjectionProject/
88+
89+
# End of https://www.toptal.com/developers/gitignore/api/swift
90+
91+
*.xcodeproj/
92+
.DS_Store
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1210"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "UserDefaultsBacking"
18+
BuildableName = "UserDefaultsBacking"
19+
BlueprintName = "UserDefaultsBacking"
20+
ReferencedContainer = "container:">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
<BuildActionEntry
24+
buildForTesting = "YES"
25+
buildForRunning = "YES"
26+
buildForProfiling = "YES"
27+
buildForArchiving = "YES"
28+
buildForAnalyzing = "YES">
29+
<BuildableReference
30+
BuildableIdentifier = "primary"
31+
BlueprintIdentifier = "UserDefaultsBackingTests"
32+
BuildableName = "UserDefaultsBackingTests"
33+
BlueprintName = "UserDefaultsBackingTests"
34+
ReferencedContainer = "container:">
35+
</BuildableReference>
36+
</BuildActionEntry>
37+
</BuildActionEntries>
38+
</BuildAction>
39+
<TestAction
40+
buildConfiguration = "Debug"
41+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
42+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
43+
shouldUseLaunchSchemeArgsEnv = "YES">
44+
<Testables>
45+
<TestableReference
46+
skipped = "NO">
47+
<BuildableReference
48+
BuildableIdentifier = "primary"
49+
BlueprintIdentifier = "UserDefaultsBackingTests"
50+
BuildableName = "UserDefaultsBackingTests"
51+
BlueprintName = "UserDefaultsBackingTests"
52+
ReferencedContainer = "container:">
53+
</BuildableReference>
54+
</TestableReference>
55+
</Testables>
56+
</TestAction>
57+
<LaunchAction
58+
buildConfiguration = "Debug"
59+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
60+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
61+
launchStyle = "0"
62+
useCustomWorkingDirectory = "NO"
63+
ignoresPersistentStateOnLaunch = "NO"
64+
debugDocumentVersioning = "YES"
65+
debugServiceExtension = "internal"
66+
allowLocationSimulation = "YES">
67+
</LaunchAction>
68+
<ProfileAction
69+
buildConfiguration = "Release"
70+
shouldUseLaunchSchemeArgsEnv = "YES"
71+
savedToolIdentifier = ""
72+
useCustomWorkingDirectory = "NO"
73+
debugDocumentVersioning = "YES">
74+
<MacroExpansion>
75+
<BuildableReference
76+
BuildableIdentifier = "primary"
77+
BlueprintIdentifier = "UserDefaultsBacking"
78+
BuildableName = "UserDefaultsBacking"
79+
BlueprintName = "UserDefaultsBacking"
80+
ReferencedContainer = "container:">
81+
</BuildableReference>
82+
</MacroExpansion>
83+
</ProfileAction>
84+
<AnalyzeAction
85+
buildConfiguration = "Debug">
86+
</AnalyzeAction>
87+
<ArchiveAction
88+
buildConfiguration = "Release"
89+
revealArchiveInOrganizer = "YES">
90+
</ArchiveAction>
91+
</Scheme>

Package.swift

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "UserDefaultsSnapshotLib",
8+
platforms: [.iOS(.v12)],
9+
products: [
10+
// Products define the executables and libraries a package produces, and make them visible to other packages.
11+
.library(
12+
name: "UserDefaultsSnapshotLib",
13+
targets: ["UserDefaultsSnapshotLib"]),
14+
],
15+
dependencies: [
16+
// Dependencies declare other packages that this package depends on.
17+
// .package(url: /* package url */, from: "1.0.0"),
18+
],
19+
targets: [
20+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
21+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
22+
.target(
23+
name: "UserDefaultsSnapshotLib",
24+
dependencies: []),
25+
.testTarget(
26+
name: "UserDefaultsSnapshotLibTests",
27+
dependencies: ["UserDefaultsSnapshotLib"]),
28+
]
29+
)

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# UserDefaultsBacking
2+
3+
A description of this package.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
Copyright 2020 Hiroshi Kimura
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5+
6+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9+
*/
10+
11+
import Foundation
12+
13+
open class UserDefaultsObject: Hashable {
14+
15+
public static func == (lhs: UserDefaultsObject, rhs: UserDefaultsObject) -> Bool {
16+
lhs === rhs
17+
}
18+
19+
public func hash(into hasher: inout Hasher) {
20+
ObjectIdentifier(self).hash(into: &hasher)
21+
}
22+
23+
public let storage: [String : Any]
24+
private(set) var modified: [String : Any] = [:]
25+
26+
public required init(
27+
snapshot: [String : Any]
28+
) {
29+
self.storage = snapshot
30+
}
31+
32+
public func read<T: _UserDefaultsPrimitiveValueType>(type: T.Type? = nil, from key: String) -> T? {
33+
return (modified[key] as? T) ?? (storage[key] as? T)
34+
}
35+
36+
public func write<T: _UserDefaultsPrimitiveValueType>(value: T?, for key: String) {
37+
guard let value = value else {
38+
modified[key] = NSNull()
39+
return
40+
}
41+
modified[key] = value
42+
}
43+
44+
}
45+
46+
extension UserDefaultsObject {
47+
48+
@propertyWrapper
49+
public struct OptionalProperty<WrappedValue: _UserDefaultsPrimitiveValueType> {
50+
51+
@available(*, unavailable)
52+
public var wrappedValue: WrappedValue? {
53+
get { fatalError() }
54+
set { fatalError() }
55+
}
56+
57+
public let key: String
58+
59+
public init(key: String) {
60+
self.key = key
61+
}
62+
63+
public static subscript<Instance: UserDefaultsObject>(
64+
_enclosingInstance instance: Instance,
65+
wrapped wrappedKeyPath: KeyPath<Instance, WrappedValue?>,
66+
storage storageKeyPath: KeyPath<Instance, Self>
67+
) -> WrappedValue? {
68+
get {
69+
let storage = instance[keyPath: storageKeyPath]
70+
return instance.read(type: WrappedValue.self, from: storage.key)
71+
}
72+
set {
73+
let storage = instance[keyPath: storageKeyPath]
74+
instance.write(value: newValue, for: storage.key)
75+
}
76+
}
77+
78+
}
79+
80+
@propertyWrapper
81+
public struct Property<WrappedValue: _UserDefaultsPrimitiveValueType> {
82+
83+
@available(*, unavailable)
84+
public var wrappedValue: WrappedValue {
85+
get { fatalError() }
86+
set { fatalError() }
87+
}
88+
89+
public let key: String
90+
public let defaultValue: WrappedValue
91+
92+
public init(wrappedValue: WrappedValue, key: String) {
93+
self.key = key
94+
self.defaultValue = wrappedValue
95+
}
96+
97+
public static subscript<Instance: UserDefaultsObject>(
98+
_enclosingInstance instance: Instance,
99+
wrapped wrappedKeyPath: KeyPath<Instance, WrappedValue>,
100+
storage storageKeyPath: KeyPath<Instance, Self>
101+
) -> WrappedValue {
102+
get {
103+
let storage = instance[keyPath: storageKeyPath]
104+
return instance.read(type: WrappedValue.self, from: storage.key) ?? storage.defaultValue
105+
}
106+
set {
107+
let storage = instance[keyPath: storageKeyPath]
108+
instance.write(value: newValue, for: storage.key)
109+
}
110+
}
111+
112+
}
113+
114+
}

0 commit comments

Comments
 (0)