Skip to content

Commit 393ded4

Browse files
committed
Implement RedactionReasons
1 parent bdf70b2 commit 393ded4

File tree

2 files changed

+155
-4
lines changed

2 files changed

+155
-4
lines changed

Sources/OpenSwiftUICore/Data/EnvironmentKeys/RedactionReasons.swift

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,111 @@
33
// OpenSwiftUICore
44
//
55
// Audited for 6.5.4
6-
// Status: WIP
6+
// Status: Blocked by Image
77
// ID: 18671928047E57F039DC339288B6FAFB (SwiftUICore)
88

9+
// MARK: - RedactionReasons
10+
11+
/// The reasons to apply a redaction to data displayed on screen.
12+
@available(OpenSwiftUI_v2_0, *)
13+
public struct RedactionReasons: OptionSet, Sendable {
14+
public let rawValue: Int
15+
16+
public init(rawValue: Int) {
17+
self.rawValue = rawValue
18+
}
19+
20+
/// Displayed data should appear as generic placeholders.
21+
///
22+
/// Text and images will be automatically masked to appear as
23+
/// generic placeholders, though maintaining their original size and shape.
24+
/// Use this to create a placeholder UI without directly exposing
25+
/// placeholder data to users.
26+
public static let placeholder: RedactionReasons = .init(rawValue: 1 << 0)
27+
28+
/// Displayed data should be obscured to protect private information.
29+
///
30+
/// Views marked with `privacySensitive` will be automatically redacted
31+
/// using a standard styling. To apply a custom treatment the redaction
32+
/// reason can be read out of the environment.
33+
///
34+
/// struct BankingContentView: View {
35+
/// @Environment(\.redactionReasons) var redactionReasons
36+
///
37+
/// var body: some View {
38+
/// if redactionReasons.contains(.privacy) {
39+
/// FullAppCover()
40+
/// } else {
41+
/// AppContent()
42+
/// }
43+
/// }
44+
/// }
45+
@available(OpenSwiftUI_v3_0, *)
46+
public static let privacy: RedactionReasons = .init(rawValue: 1 << 1)
47+
48+
/// Displayed data should appear as invalidated and pending a new update.
49+
///
50+
/// Views marked with `invalidatableContent` will be automatically
51+
/// redacted with a standard styling indicating the content is invalidated
52+
/// and new content will be available soon.
53+
@available(OpenSwiftUI_v5_0, *)
54+
public static let invalidated: RedactionReasons = .init(rawValue: 1 << 2)
55+
56+
@_spi(Private)
57+
@available(OpenSwiftUI_v6_0, *)
58+
public static let screencaptureProhibited: RedactionReasons = .init(rawValue: 1 << 3)
59+
}
60+
61+
// MARK: - View + redacted
62+
63+
@available(OpenSwiftUI_v2_0, *)
64+
extension View {
65+
66+
/// Adds a reason to apply a redaction to this view hierarchy.
67+
///
68+
/// Adding a redaction is an additive process: any redaction
69+
/// provided will be added to the reasons provided by the parent.
70+
nonisolated public func redacted(reason: RedactionReasons) -> some View {
71+
transformEnvironment(\.redactionReasons) {
72+
$0.insert(reason)
73+
}
74+
}
75+
76+
/// Removes any reason to apply a redaction to this view hierarchy.
77+
nonisolated public func unredacted() -> some View {
78+
environment(\.redactionReasons, [])
79+
}
80+
}
81+
982
// MARK: - EnvironmentValues + RedactionReasons
1083

11-
// TODO
84+
private struct RedactionReasonsKey: EnvironmentKey {
85+
static let defaultValue: RedactionReasons = []
86+
}
87+
1288
private struct ShouldRedactContentKey: DerivedEnvironmentKey {
1389
static func value(in environment: EnvironmentValues) -> Bool {
14-
// redaction
15-
false
90+
let redactionReasons = environment.redactionReasons
91+
if redactionReasons.contains(.placeholder) {
92+
return true
93+
} else if redactionReasons.contains(.privacy) {
94+
return environment.sensitiveContent
95+
} else {
96+
return false
97+
}
98+
}
99+
}
100+
101+
private struct UnredactSymbolImage: EnvironmentKey {
102+
static let defaultValue: Bool = false
103+
}
104+
105+
private struct ShouldRedactSymbolImagesKey: DerivedEnvironmentKey {
106+
static func value(in environment: EnvironmentValues) -> Bool {
107+
guard environment.shouldRedactContent else {
108+
return false
109+
}
110+
return !environment.unredactSymbolImage
16111
}
17112
}
18113

@@ -21,3 +116,41 @@ extension EnvironmentValues {
21116
self[ShouldRedactContentKey.self]
22117
}
23118
}
119+
120+
@_spi(Private)
121+
@available(OpenSwiftUI_v6_0, *)
122+
extension EnvironmentValues {
123+
124+
public var unredactSymbolImage: Bool {
125+
get { self[UnredactSymbolImage.self] }
126+
set { self[UnredactSymbolImage.self] = newValue }
127+
}
128+
129+
package var shouldRedactSymbolImages: Bool {
130+
self[ShouldRedactSymbolImagesKey.self]
131+
}
132+
}
133+
134+
@available(OpenSwiftUI_v2_0, *)
135+
extension EnvironmentValues {
136+
137+
/// The current redaction reasons applied to the view hierarchy.
138+
public var redactionReasons: RedactionReasons {
139+
get { self[RedactionReasonsKey.self] }
140+
set { self[RedactionReasonsKey.self] = newValue }
141+
}
142+
}
143+
144+
// MARK: - Image + redacted [TODO]
145+
146+
extension GraphicsImage {
147+
package mutating func redact(in environment: EnvironmentValues) {
148+
_openSwiftUIUnimplementedFailure()
149+
}
150+
}
151+
152+
extension Image {
153+
package static let redacted: Image = {
154+
_openSwiftUIUnimplementedFailure()
155+
}()
156+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// SensitiveContent.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for 6.5.4
6+
// Status: WIP
7+
// ID: 7799685610985DBA9248562F2E4D5E6E (SwiftUICore?)
8+
9+
private struct SensitiveContentKey: EnvironmentKey {
10+
static let defaultValue: Bool = false
11+
}
12+
13+
extension EnvironmentValues {
14+
package var sensitiveContent: Bool {
15+
get { self[SensitiveContentKey.self] }
16+
set { self[SensitiveContentKey.self] = newValue }
17+
}
18+
}

0 commit comments

Comments
 (0)