Skip to content

Commit dc9b326

Browse files
committed
Update Image.redacted using SymbolLocator
1 parent 31aa0d5 commit dc9b326

File tree

6 files changed

+121
-16
lines changed

6 files changed

+121
-16
lines changed

Package.resolved

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,32 @@ let package = Package(
99
.library(name: "OpenAsyncImageKit", targets: ["OpenAsyncImageKit"]),
1010
.library(name: "OpenAsyncImagePlayground", targets: ["OpenAsyncImagePlayground"]),
1111
],
12+
dependencies: [
13+
.package(url: "https://github.com/OpenSwiftUIProject/SymbolLocator.git", from: "0.2.0")
14+
],
1215
targets: [
13-
.target(name: "OpenAsyncImage", swiftSettings: [.swiftLanguageMode(.v5)]),
14-
.target(name: "OpenAsyncImageKit", dependencies: ["OpenAsyncImage"], swiftSettings: [.swiftLanguageMode(.v5)]),
15-
.target(name: "OpenAsyncImagePlayground", dependencies: ["OpenAsyncImageKit"], swiftSettings: [.swiftLanguageMode(.v5)]),
16+
.target(
17+
name: "COpenAsyncImage",
18+
dependencies: [.product(
19+
name: "SymbolLocator",
20+
package: "SymbolLocator"
21+
)],
22+
publicHeadersPath: "."
23+
),
24+
.target(
25+
name: "OpenAsyncImage",
26+
dependencies: ["COpenAsyncImage"],
27+
swiftSettings: [.swiftLanguageMode(.v5)]
28+
),
29+
.target(
30+
name: "OpenAsyncImageKit",
31+
dependencies: ["OpenAsyncImage"],
32+
swiftSettings: [.swiftLanguageMode(.v5)]
33+
),
34+
.target(
35+
name: "OpenAsyncImagePlayground",
36+
dependencies: ["OpenAsyncImageKit"],
37+
swiftSettings: [.swiftLanguageMode(.v5)]
38+
),
1639
]
1740
)

Sources/COpenAsyncImage/ImageStub.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//
2+
// ImageTestsStub.c
3+
// OpenSwiftUISymbolDualTestsSupport
4+
5+
#import <SymbolLocator.h>
6+
7+
DEFINE_SL_STUB_SLF(OpenAsyncImage_Image_Redacted, SwiftUI, $s7SwiftUI5ImageVAAE8redactedACvgZ);

Sources/OpenAsyncImage/AsyncImage.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,26 +148,30 @@ public struct AsyncImage<Content>: View where Content : View {
148148
/// to the loaded image, use the ``init(url:scale:content:placeholder:)``
149149
/// initializer instead.
150150
///
151-
/// > Warning: Due to limitations in SwiftUI's public API, `Image.redacted`
152-
/// > cannot be fully implemented in this backport. As a result, the default
153-
/// > placeholder effect in this initializer will appear different from SwiftUI's
154-
/// > native implementation. For a more consistent appearance across platforms,
155-
/// > consider using the `init(url:scale:content:placeholder:)` initializer
156-
/// > with a custom placeholder instead.
157-
///
158151
/// - Parameters:
159152
/// - url: The URL of the image to display.
160153
/// - scale: The scale to use for the image. The default is `1`. Set a
161154
/// different value when loading images designed for higher resolution
162155
/// displays. For example, set a value of `2` for an image that you
163156
/// would name with the `@2x` suffix if stored in a file on disk.
164-
@available(*, deprecated, message: "Use `init(url:scale:transaction:content:)` or `init(url:scale:content:placeholder:) instead.")
157+
@available(*, deprecated, message: "Use `init(url:scale:content:placeholder:) instead.")
165158
public init(url: URL?, scale: CGFloat = 1) where Content == Image {
166159
self.url = url
167160
self.scale = scale
168161
self.transaction = Transaction()
169162
self.content = { phase in
170-
phase.image ?? .redacted
163+
if let image = phase.image {
164+
return image
165+
} else if let redacted = Image.redacted {
166+
return redacted
167+
} else {
168+
Log.runtimeIssues(#"""
169+
Image.redacted is unavailable on this OS version (Available on iOS 18.x any may be available on future OS).
170+
This fallback uses an empty image, which does not match SwiftUI's native placeholder appearance.
171+
For consistent cross-platform behavior, use init(url:scale:content:placeholder:) with a custom placeholder view.
172+
"""#)
173+
return Image("")
174+
}
171175
}
172176
}
173177

Sources/OpenAsyncImage/Image.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,21 @@
3434
import SwiftUI
3535

3636
extension Image {
37-
static let redacted: Image = {
38-
// FIXME: Image.Resolved is not available in SwiftUI yet. Return a empty image for now.
39-
Image("")
40-
}()
37+
@_silgen_name("OpenAsyncImage_Image_Redacted_ptr")
38+
private static var redacted_18_ptr: UnsafeRawPointer?
39+
40+
private static var redacted_18: Image {
41+
@_silgen_name("OpenAsyncImage_Image_Redacted")
42+
get
43+
}
44+
45+
static var redacted: Image? {
46+
if redacted_18_ptr != nil {
47+
redacted_18
48+
} else {
49+
nil
50+
}
51+
}
4152
}
4253

4354
extension Image.Orientation {

Sources/OpenAsyncImage/Log.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Logging.swift
3+
// Modified from https://github.com/OpenSwiftUIProject/OpenSwiftUI/blob/main/Sources/OpenSwiftUICore/Log/Logging.swift
4+
// Kyle-Ye MIT License
5+
6+
import Foundation
7+
public import os.log
8+
9+
#if DEBUG
10+
package let dso = { () -> UnsafeMutableRawPointer in
11+
let count = _dyld_image_count()
12+
for i in 0 ..< count {
13+
if let name = _dyld_get_image_name(i) {
14+
let swiftString = String(cString: name)
15+
if swiftString.hasSuffix("/SwiftUI") {
16+
if let header = _dyld_get_image_header(i) {
17+
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
18+
}
19+
}
20+
}
21+
}
22+
return UnsafeMutableRawPointer(mutating: #dsohandle)
23+
}()
24+
#endif
25+
26+
@usableFromInline
27+
package enum Log {
28+
@usableFromInline
29+
package static var runtimeIssuesLog: OSLog = OSLog(subsystem: "com.apple.runtime-issues", category: "OpenAsyncImage")
30+
31+
@_transparent
32+
package static func runtimeIssues(
33+
_ message: @autoclosure () -> StaticString,
34+
_ args: @autoclosure () -> [CVarArg] = []
35+
) {
36+
#if DEBUG
37+
unsafeBitCast(
38+
os_log as (OSLogType, UnsafeRawPointer, OSLog, StaticString, CVarArg...) -> Void,
39+
to: ((OSLogType, UnsafeRawPointer, OSLog, StaticString, [CVarArg]) -> Void).self
40+
)(.fault, dso, runtimeIssuesLog, message(), args())
41+
#else
42+
os_log(.fault, log: runtimeIssuesLog, message(), args())
43+
#endif
44+
}
45+
}

0 commit comments

Comments
 (0)