Skip to content

Commit afb6e30

Browse files
committed
Fix image loading is not loding image on init in editor
1 parent d8f6c60 commit afb6e30

10 files changed

+127
-11
lines changed

RichEditorDemo/RichEditorDemo/RichEditorDemo.entitlements

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5-
<key>com.apple.security.app-sandbox</key>
6-
<true/>
7-
<key>com.apple.security.files.user-selected.read-only</key>
8-
<true/>
5+
<key>com.apple.security.app-sandbox</key>
6+
<true/>
7+
<key>com.apple.security.files.user-selected.read-only</key>
8+
<true/>
9+
<key>com.apple.security.network.client</key>
10+
<true/>
911
</dict>
1012
</plist>

Sources/RichEditorSwiftUI/Actions/RichTextAction.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public enum RichTextAction: Identifiable, Equatable {
8080

8181
/// Set link
8282
case setLink(String? = nil)
83+
84+
/// Update Image Attachment as image takes time to download
85+
case updateImageAttachments([ImageAttachment])
86+
8387
}
8488

8589
extension RichTextAction {
@@ -114,6 +118,7 @@ extension RichTextAction {
114118
case .undoLatestChange: .richTextUndo
115119
case .setHeaderStyle: .richTextIgnoreIt
116120
case .setLink: .richTextLink
121+
case .updateImageAttachments: .richTextIgnoreIt
117122
}
118123
}
119124

@@ -164,7 +169,7 @@ extension RichTextAction {
164169
case .toggleStyle(let style): style.titleKey
165170
case .undoLatestChange: .actionUndoLatestChange
166171
case .setLink: .link
167-
case .setHeaderStyle: .ignoreIt
172+
case .setHeaderStyle, .updateImageAttachments: .ignoreIt
168173
}
169174
}
170175
}

Sources/RichEditorSwiftUI/BaseFoundation/RichTextCoordinator+Actions.swift

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ import Foundation
7676
} else {
7777
removeLink()
7878
}
79+
case .updateImageAttachments(let attachments):
80+
attachments.forEach({
81+
textView.setImageAttachment(imageAttachment: $0)
82+
})
7983
}
8084
}
8185
}

Sources/RichEditorSwiftUI/BaseFoundation/RichTextCoordinator.swift

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
super.init()
7575
self.textView.delegate = self
7676
subscribeToUserActions()
77+
context.onTextViewDidEndWithSetUp()
7778
}
7879
#if canImport(UIKit)
7980

Sources/RichEditorSwiftUI/Components/RichTextViewComponent+Pasting.swift

+19-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ extension RichTextViewComponent {
7676
let isSelectedRange = (index == selectedRange.location)
7777
if isSelectedRange { deleteCharacters(in: selectedRange) }
7878
if move { moveInputCursor(to: index) }
79-
var insertedString: NSMutableAttributedString = .init()
79+
let insertedString: NSMutableAttributedString = .init()
8080
images.reversed().forEach {
8181
insertedString.append(performPasteImage($0, at: index) ?? .init())
8282
}
@@ -88,6 +88,12 @@ extension RichTextViewComponent {
8888
#endif
8989
}
9090

91+
public func setImageAttachment(imageAttachment: ImageAttachment) {
92+
guard let range = imageAttachment.range else { return }
93+
let image = imageAttachment.image
94+
performSetImageAttachment(image, at: range)
95+
}
96+
9197
/**
9298
Paste text into the text view, at a certain index.
9399

@@ -154,3 +160,15 @@ extension RichTextViewComponent {
154160
}
155161
}
156162
#endif
163+
164+
#if iOS || macOS || os(tvOS) || os(visionOS)
165+
extension RichTextViewComponent {
166+
fileprivate func performSetImageAttachment(
167+
_ image: ImageRepresentable,
168+
at range: NSRange
169+
) {
170+
guard let attachmentString = getAttachmentString(for: image) else { return }
171+
mutableAttributedString?.replaceCharacters(in: range, with: attachmentString)
172+
}
173+
}
174+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// RichAttributes+ImageDownload.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 31/12/24.
6+
//
7+
8+
import Foundation
9+
10+
extension RichAttributes {
11+
func getImage() async -> ImageRepresentable? {
12+
if let imageUrl = image {
13+
let image = try? await ImageDownloadManager.shared.fetchImage(from: imageUrl)
14+
return image
15+
}
16+
return nil
17+
}
18+
}

Sources/RichEditorSwiftUI/Data/Models/RichAttributes.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,10 @@ extension RichAttributes {
222222
? (byAdding ? att.align! : nil) : self.align),
223223
///nil link indicates removal as well so removing link if `byAdding == false && att.link == nil`
224224
link: (att.link != nil
225-
? (byAdding ? att.link! : nil) : (att.link == nil && !byAdding) ? nil : self.link),
226-
image: (att.image != nil ? (byAdding ? att.image! : nil) : self.image)
225+
? (byAdding ? att.link! : nil)
226+
: (att.link == nil && !byAdding) ? nil : self.link),
227+
image: (att.image != nil
228+
? (byAdding ? att.image! : nil) : self.image)
227229
)
228230
}
229231
}

Sources/RichEditorSwiftUI/Images/ImageAttachment.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77

88
import Foundation
99

10-
public class ImageAttachment {
10+
public class ImageAttachment: Equatable {
11+
public static func == (lhs: ImageAttachment, rhs: ImageAttachment) -> Bool {
12+
return lhs.id == rhs.id && lhs.image == rhs.image
13+
}
14+
1115
public let id: String
1216
public let image: ImageRepresentable
1317
internal var range: NSRange? = nil

Sources/RichEditorSwiftUI/Images/ImageDownloadManager.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ public class ImageDownloadManager {
5757
userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
5858
}
5959

60-
let (data, _) = try await URLSession.shared.data(from: url)
61-
60+
let (data, response) = try await URLSession.shared.data(from: url)
6261
guard let image = ImageRepresentable(data: data) else {
6362
throw NSError(
6463
domain: "ImageDownloadManager", code: 500,

Sources/RichEditorSwiftUI/UI/Context/RichEditorState.swift

+63
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,69 @@ public class RichEditorState: ObservableObject {
232232
}
233233
}
234234

235+
//MARK: - Handle Image download
236+
extension RichEditorState {
237+
func onTextViewDidEndWithSetUp() {
238+
setupWithImage()
239+
}
240+
241+
func setupWithImage() {
242+
let imageSpans = internalSpans.filter({ $0.attributes?.image != nil })
243+
guard !imageSpans.isEmpty else { return }
244+
imageSpans.forEach { item in
245+
Task { @MainActor [weak self] in
246+
guard let attributes = item.attributes else { return }
247+
let image = await attributes.getImage()
248+
if let image, let imageUrl = attributes.image {
249+
let attachment = ImageAttachment(image: image, url: imageUrl)
250+
attachment.updateRange(with: item.spanRange)
251+
self?.actionPublisher.send(.updateImageAttachments([attachment]))
252+
}
253+
}
254+
}
255+
}
256+
257+
func setupWithImageAttachment(imageAttachment: [ImageAttachment]) {
258+
let richText = internalRichText
259+
var tempSpans: [RichTextSpanInternal] = []
260+
var text = ""
261+
richText.spans.forEach({
262+
let span = RichTextSpanInternal(
263+
from: text.utf16Length,
264+
to: (text.utf16Length + $0.insert.utf16Length - 1),
265+
attributes: $0.attributes)
266+
267+
tempSpans.append(span)
268+
text += $0.insert
269+
})
270+
271+
let str = NSMutableAttributedString(string: text)
272+
273+
tempSpans.forEach { span in
274+
str.addAttributes(
275+
span.attributes?.toAttributes(font: .standardRichTextFont)
276+
?? [:], range: span.spanRange)
277+
if span.attributes?.color == nil {
278+
var color: ColorRepresentable = .clear
279+
#if os(watchOS)
280+
color = .black
281+
#else
282+
color = RichTextView.Theme.standard.fontColor
283+
#endif
284+
str.addAttributes(
285+
[.foregroundColor: color], range: span.spanRange)
286+
}
287+
if let imageUrl = span.attributes?.image,
288+
let image = imageAttachment.first(where: { $0.url == imageUrl })
289+
{
290+
str.addAttribute(.attachment, value: image.image, range: span.spanRange)
291+
}
292+
}
293+
294+
self.attributedString = str
295+
}
296+
}
297+
235298
extension RichEditorState {
236299

237300
/// Whether or not the context has a selected range.

0 commit comments

Comments
 (0)