diff --git a/Examples/Demo/Demo/DemoView.swift b/Examples/Demo/Demo/DemoView.swift index ea74b167..81b9f8ec 100644 --- a/Examples/Demo/Demo/DemoView.swift +++ b/Examples/Demo/Demo/DemoView.swift @@ -1,7 +1,7 @@ import MarkdownUI import SwiftUI -struct ThemeOption: Hashable { +struct ThemeOption: Hashable, Sendable { let name: String let theme: Theme diff --git a/Package.swift b/Package.swift index 61582c31..8985f0c4 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.6 +// swift-tools-version: 5.10 import PackageDescription @@ -28,6 +28,9 @@ let package = Package( dependencies: [ "cmark-gfm", .product(name: "NetworkImage", package: "NetworkImage"), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency"), ] ), .testTarget( diff --git a/Sources/MarkdownUI/DSL/Blocks/ListItem.swift b/Sources/MarkdownUI/DSL/Blocks/ListItem.swift index 0d4cbd92..ec29d427 100644 --- a/Sources/MarkdownUI/DSL/Blocks/ListItem.swift +++ b/Sources/MarkdownUI/DSL/Blocks/ListItem.swift @@ -23,7 +23,7 @@ import Foundation /// ``` /// /// ![](ListItem) -public struct ListItem: Hashable { +public struct ListItem: Hashable, Sendable { let children: [BlockNode] init(children: [BlockNode]) { diff --git a/Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift b/Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift index 7bcf9fa2..5b44d6c8 100644 --- a/Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift +++ b/Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift @@ -59,7 +59,7 @@ public protocol MarkdownContentProtocol { /// } /// } /// ``` -public struct MarkdownContent: Equatable, MarkdownContentProtocol { +public struct MarkdownContent: Equatable, MarkdownContentProtocol, Sendable { /// Returns a Markdown content value with the sum of the contents of all the container blocks /// present in this content. /// diff --git a/Sources/MarkdownUI/DSL/Blocks/TaskListItem.swift b/Sources/MarkdownUI/DSL/Blocks/TaskListItem.swift index e768b2b2..eb2b374e 100644 --- a/Sources/MarkdownUI/DSL/Blocks/TaskListItem.swift +++ b/Sources/MarkdownUI/DSL/Blocks/TaskListItem.swift @@ -22,7 +22,7 @@ import Foundation /// } /// } /// ``` -public struct TaskListItem: Hashable { +public struct TaskListItem: Hashable, Sendable { let isCompleted: Bool let children: [BlockNode] diff --git a/Sources/MarkdownUI/DSL/Inlines/SoftBreak.swift b/Sources/MarkdownUI/DSL/Inlines/SoftBreak.swift index 95551353..8847d711 100644 --- a/Sources/MarkdownUI/DSL/Inlines/SoftBreak.swift +++ b/Sources/MarkdownUI/DSL/Inlines/SoftBreak.swift @@ -3,7 +3,7 @@ import Foundation /// A soft break in a Markdown content block. /// /// A ``Markdown`` view will display a soft break as a space. -public struct SoftBreak: InlineContentProtocol { +public struct SoftBreak: InlineContentProtocol, Sendable { /// Creates a soft break inline element. public init() {} @@ -13,7 +13,7 @@ public struct SoftBreak: InlineContentProtocol { } extension SoftBreak { - public enum Mode { + public enum Mode: Sendable { /// Treat a soft break as a space case space diff --git a/Sources/MarkdownUI/Extensibility/AssetImageProvider.swift b/Sources/MarkdownUI/Extensibility/AssetImageProvider.swift index abeb0030..e54a6ce3 100644 --- a/Sources/MarkdownUI/Extensibility/AssetImageProvider.swift +++ b/Sources/MarkdownUI/Extensibility/AssetImageProvider.swift @@ -12,8 +12,8 @@ import SwiftUI /// } /// .markdownImageProvider(.asset) /// ``` -public struct AssetImageProvider: ImageProvider { - private let name: (URL) -> String +public struct AssetImageProvider: ImageProvider, Sendable { + private let name: @Sendable (URL) -> String private let bundle: Bundle? /// Creates an asset image provider. @@ -21,7 +21,7 @@ public struct AssetImageProvider: ImageProvider { /// - name: A closure that extracts the image resource name from the URL in the Markdown content. /// - bundle: The bundle where the image resources are located. Specify `nil` to search the app’s main bundle. public init( - name: @escaping (URL) -> String = \.lastPathComponent, + name: @Sendable @escaping (URL) -> String = \.lastPathComponent, bundle: Bundle? = nil ) { self.name = name @@ -29,7 +29,7 @@ public struct AssetImageProvider: ImageProvider { } public func makeImage(url: URL?) -> some View { - if let url = url, let image = self.image(url: url) { + if let url, let image = self.image(url: url) { ResizeToFit(idealSize: image.size) { Image(platformImage: image) .resizable() diff --git a/Sources/MarkdownUI/Extensibility/AssetInlineImageProvider.swift b/Sources/MarkdownUI/Extensibility/AssetInlineImageProvider.swift index c88f2fe7..737f7de4 100644 --- a/Sources/MarkdownUI/Extensibility/AssetInlineImageProvider.swift +++ b/Sources/MarkdownUI/Extensibility/AssetInlineImageProvider.swift @@ -2,7 +2,7 @@ import SwiftUI /// An inline image provider that loads images from resources located in an app or a module. public struct AssetInlineImageProvider: InlineImageProvider { - private let name: (URL) -> String + private let name: @Sendable (URL) -> String private let bundle: Bundle? /// Creates an asset inline image provider. @@ -10,7 +10,7 @@ public struct AssetInlineImageProvider: InlineImageProvider { /// - name: A closure that extracts the image resource name from the URL in the Markdown content. /// - bundle: The bundle where the image resources are located. Specify `nil` to search the app’s main bundle. public init( - name: @escaping (URL) -> String = \.lastPathComponent, + name: @Sendable @escaping (URL) -> String = \.lastPathComponent, bundle: Bundle? = nil ) { self.name = name diff --git a/Sources/MarkdownUI/Extensibility/CodeSyntaxHighlighter.swift b/Sources/MarkdownUI/Extensibility/CodeSyntaxHighlighter.swift index b725dd40..2f431ecf 100644 --- a/Sources/MarkdownUI/Extensibility/CodeSyntaxHighlighter.swift +++ b/Sources/MarkdownUI/Extensibility/CodeSyntaxHighlighter.swift @@ -4,7 +4,7 @@ import SwiftUI /// /// To configure the current code syntax highlighter for a view hierarchy, use the /// `markdownCodeSyntaxHighlighter(_:)` modifier. -public protocol CodeSyntaxHighlighter { +public protocol CodeSyntaxHighlighter: Sendable { /// Returns a text view configured with the syntax highlighted code. /// - Parameters: /// - code: The code block. diff --git a/Sources/MarkdownUI/Extensibility/ImageProvider.swift b/Sources/MarkdownUI/Extensibility/ImageProvider.swift index 937319b2..3dc45d0c 100644 --- a/Sources/MarkdownUI/Extensibility/ImageProvider.swift +++ b/Sources/MarkdownUI/Extensibility/ImageProvider.swift @@ -13,7 +13,7 @@ import SwiftUI /// } /// .markdownImageProvider(.asset) /// ``` -public protocol ImageProvider { +public protocol ImageProvider: Sendable { /// A view that loads and displays an image. associatedtype Body: View @@ -26,8 +26,8 @@ public protocol ImageProvider { @ViewBuilder func makeImage(url: URL?) -> Body } -struct AnyImageProvider: ImageProvider { - private let _makeImage: (URL?) -> AnyView +struct AnyImageProvider: ImageProvider, Sendable { + private let _makeImage: @Sendable (URL?) -> AnyView init(_ imageProvider: I) { self._makeImage = { diff --git a/Sources/MarkdownUI/Extensibility/InlineImageProvider.swift b/Sources/MarkdownUI/Extensibility/InlineImageProvider.swift index 06bd9e1c..b4c9c32e 100644 --- a/Sources/MarkdownUI/Extensibility/InlineImageProvider.swift +++ b/Sources/MarkdownUI/Extensibility/InlineImageProvider.swift @@ -4,7 +4,7 @@ import SwiftUI /// /// To configure the current inline image provider for a view hierarchy, /// use the `markdownInlineImageProvider(_:)` modifier. -public protocol InlineImageProvider { +public protocol InlineImageProvider: Sendable { /// Returns an image for the given URL. /// /// ``Markdown`` views call this method to load images within a line of text. diff --git a/Sources/MarkdownUI/Parser/BlockNode.swift b/Sources/MarkdownUI/Parser/BlockNode.swift index 4686106d..2f100ba1 100644 --- a/Sources/MarkdownUI/Parser/BlockNode.swift +++ b/Sources/MarkdownUI/Parser/BlockNode.swift @@ -1,6 +1,6 @@ import Foundation -enum BlockNode: Hashable { +enum BlockNode: Hashable, Sendable { case blockquote(children: [BlockNode]) case bulletedList(isTight: Bool, items: [RawListItem]) case numberedList(isTight: Bool, start: Int, items: [RawListItem]) @@ -35,26 +35,26 @@ extension BlockNode { } } -struct RawListItem: Hashable { +struct RawListItem: Hashable, Sendable { let children: [BlockNode] } -struct RawTaskListItem: Hashable { +struct RawTaskListItem: Hashable, Sendable { let isCompleted: Bool let children: [BlockNode] } -enum RawTableColumnAlignment: Character { +enum RawTableColumnAlignment: Character, Sendable { case none = "\0" case left = "l" case center = "c" case right = "r" } -struct RawTableRow: Hashable { +struct RawTableRow: Hashable, Sendable { let cells: [RawTableCell] } -struct RawTableCell: Hashable { +struct RawTableCell: Hashable, Sendable { let content: [InlineNode] } diff --git a/Sources/MarkdownUI/Theme/BlockStyle/BlockConfiguration.swift b/Sources/MarkdownUI/Theme/BlockStyle/BlockConfiguration.swift index 0ee2a56d..6e524f4d 100644 --- a/Sources/MarkdownUI/Theme/BlockStyle/BlockConfiguration.swift +++ b/Sources/MarkdownUI/Theme/BlockStyle/BlockConfiguration.swift @@ -5,6 +5,7 @@ import SwiftUI /// Most theme ``BlockStyle`` instances receive a `BlockConfiguration` input in their /// `body` closure. The configuration ``BlockConfiguration/label-swift.property`` /// property reflects the block's content. +@MainActor public struct BlockConfiguration { /// A type-erased view of a Markdown block. public struct Label: View { diff --git a/Sources/MarkdownUI/Theme/BlockStyle/BlockStyle.swift b/Sources/MarkdownUI/Theme/BlockStyle/BlockStyle.swift index 5dee5ca7..8c60000f 100644 --- a/Sources/MarkdownUI/Theme/BlockStyle/BlockStyle.swift +++ b/Sources/MarkdownUI/Theme/BlockStyle/BlockStyle.swift @@ -37,12 +37,13 @@ import SwiftUI /// ``` /// /// ![](CustomBlockquote) +@MainActor public struct BlockStyle { - private let body: (Configuration) -> AnyView + private let body: @MainActor (Configuration) -> AnyView /// Creates a block style that customizes a block by applying the given body. /// - Parameter body: A view builder that returns the customized block. - public init(@ViewBuilder body: @escaping (_ configuration: Configuration) -> Body) { + public init(@ViewBuilder body: @MainActor @escaping (_ configuration: Configuration) -> Body) { self.body = { AnyView(body($0)) } } @@ -54,7 +55,7 @@ public struct BlockStyle { extension BlockStyle where Configuration == Void { /// Creates a block style for a block with no content, like a thematic break. /// - Parameter body: A view builder that returns the customized block. - public init(@ViewBuilder body: @escaping () -> Body) { + public init(@ViewBuilder body: @MainActor @escaping () -> Body) { self.init { _ in body() } diff --git a/Sources/MarkdownUI/Theme/BlockStyle/ListMarkerConfiguration.swift b/Sources/MarkdownUI/Theme/BlockStyle/ListMarkerConfiguration.swift index b50e16cd..433689f2 100644 --- a/Sources/MarkdownUI/Theme/BlockStyle/ListMarkerConfiguration.swift +++ b/Sources/MarkdownUI/Theme/BlockStyle/ListMarkerConfiguration.swift @@ -4,7 +4,7 @@ import SwiftUI /// /// The theme ``Theme/bulletedListMarker`` and ``Theme/numberedListMarker`` /// block styles receive a `ListMarkerConfiguration` input in their `body` closure. -public struct ListMarkerConfiguration { +public struct ListMarkerConfiguration: Sendable { /// The list level (one-based) of the item to which the marker applies. public let listLevel: Int diff --git a/Sources/MarkdownUI/Theme/BlockStyle/TableBackgroundStyle.swift b/Sources/MarkdownUI/Theme/BlockStyle/TableBackgroundStyle.swift index 85e86af9..a3f061b0 100644 --- a/Sources/MarkdownUI/Theme/BlockStyle/TableBackgroundStyle.swift +++ b/Sources/MarkdownUI/Theme/BlockStyle/TableBackgroundStyle.swift @@ -27,13 +27,13 @@ import SwiftUI /// ``` /// /// ![](CustomTableBackground) -public struct TableBackgroundStyle { - let background: (_ row: Int, _ column: Int) -> AnyShapeStyle +public struct TableBackgroundStyle: Sendable { + let background: @Sendable (_ row: Int, _ column: Int) -> AnyShapeStyle /// Creates a table background style that customizes table backgrounds by applying a given closure /// to the background of each cell. /// - Parameter background: A closure that returns a shape style for a given table cell location. - public init(background: @escaping (_ row: Int, _ column: Int) -> S) { + public init(background: @Sendable @escaping (_ row: Int, _ column: Int) -> S) { self.background = { row, column in AnyShapeStyle(background(row, column)) } diff --git a/Sources/MarkdownUI/Theme/BlockStyle/TableBorderStyle.swift b/Sources/MarkdownUI/Theme/BlockStyle/TableBorderStyle.swift index 06a6921a..a6dfe232 100644 --- a/Sources/MarkdownUI/Theme/BlockStyle/TableBorderStyle.swift +++ b/Sources/MarkdownUI/Theme/BlockStyle/TableBorderStyle.swift @@ -30,7 +30,7 @@ import SwiftUI /// ``` /// /// ![](CustomTableBorders) -public struct TableBorderStyle { +public struct TableBorderStyle: Sendable { /// The visible table borders. public var visibleBorders: TableBorderSelector diff --git a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontProperties.swift b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontProperties.swift index 10090cd8..4c172fb5 100644 --- a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontProperties.swift +++ b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontProperties.swift @@ -1,9 +1,9 @@ import SwiftUI /// The characteristics of a font. -public struct FontProperties: Hashable { +public struct FontProperties: Hashable, Sendable { /// The font family. - public enum Family: Hashable { + public enum Family: Hashable, Sendable { /// The system font family. case system(Font.Design = .default) @@ -12,7 +12,7 @@ public struct FontProperties: Hashable { } /// The font family variant. - public enum FamilyVariant: Hashable { + public enum FamilyVariant: Hashable, Sendable { /// No variant. Use the current font family. case normal @@ -21,7 +21,7 @@ public struct FontProperties: Hashable { } /// The font caps variant. - public enum CapsVariant: Hashable { + public enum CapsVariant: Hashable, Sendable { /// Don't use a font caps variant. case normal @@ -36,7 +36,7 @@ public struct FontProperties: Hashable { } /// The font digit variant. - public enum DigitVariant: Hashable { + public enum DigitVariant: Hashable, Sendable { /// Don't use a font digit variant. case normal @@ -45,7 +45,7 @@ public struct FontProperties: Hashable { } /// The font style. - public enum Style { + public enum Style: Sendable { /// Don't use a font style. case normal @@ -102,7 +102,7 @@ public struct FontProperties: Hashable { set { self.widthStorage = newValue } } - private var widthStorage: AnyHashable? + nonisolated(unsafe) private var widthStorage: AnyHashable? // Marking this as `nonisolated(unsafe)`, because it should really be `Font.Width?`, but this is only available from iOS 16. Change to type `Font.Width?` and remove `nonisolated(unsafe)` after raising minimum target to iOS 16 /// The font size. public var size: CGFloat = Self.defaultSize diff --git a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontPropertiesAttribute.swift b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontPropertiesAttribute.swift index d2898c62..04cbeaa4 100644 --- a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontPropertiesAttribute.swift +++ b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontPropertiesAttribute.swift @@ -1,6 +1,6 @@ import Foundation -enum FontPropertiesAttribute: AttributedStringKey { +enum FontPropertiesAttribute: AttributedStringKey, Sendable { typealias Value = FontProperties static let name = "fontProperties" } diff --git a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontSize.swift b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontSize.swift index 9b20ae74..2d318202 100644 --- a/Sources/MarkdownUI/Theme/TextStyle/Styles/FontSize.swift +++ b/Sources/MarkdownUI/Theme/TextStyle/Styles/FontSize.swift @@ -1,8 +1,8 @@ import Foundation /// A text style that sets the font size. -public struct FontSize: TextStyle { - private enum Size { +public struct FontSize: TextStyle, Sendable { + private enum Size: Sendable { case points(CGFloat) case relative(RelativeSize) } diff --git a/Sources/MarkdownUI/Theme/TextStyle/TextStyle.swift b/Sources/MarkdownUI/Theme/TextStyle/TextStyle.swift index 29316dcb..77a7940c 100644 --- a/Sources/MarkdownUI/Theme/TextStyle/TextStyle.swift +++ b/Sources/MarkdownUI/Theme/TextStyle/TextStyle.swift @@ -57,6 +57,6 @@ import SwiftUI /// ``` /// /// ![](CustomBlockquote) -public protocol TextStyle { +public protocol TextStyle: Sendable { func _collectAttributes(in attributes: inout AttributeContainer) } diff --git a/Sources/MarkdownUI/Theme/Theme.swift b/Sources/MarkdownUI/Theme/Theme.swift index b5796066..e188a218 100644 --- a/Sources/MarkdownUI/Theme/Theme.swift +++ b/Sources/MarkdownUI/Theme/Theme.swift @@ -99,7 +99,8 @@ import SwiftUI /// } /// // More block styles... /// ``` -public struct Theme { +@MainActor +public struct Theme: Sendable { /// The default text style. public var text: TextStyle = EmptyTextStyle() diff --git a/Sources/MarkdownUI/Utility/Deprecations.swift b/Sources/MarkdownUI/Utility/Deprecations.swift index 7a60dffa..a114cc01 100644 --- a/Sources/MarkdownUI/Utility/Deprecations.swift +++ b/Sources/MarkdownUI/Utility/Deprecations.swift @@ -369,8 +369,8 @@ extension View { "BlockStyle" types. """ ) -public struct MarkdownStyle: Hashable { - public struct Font: Hashable { +public struct MarkdownStyle: Hashable, Sendable { + public struct Font: Hashable, Sendable { public static var largeTitle: Self { fatalError("Unimplemented") } public static var title: Self { fatalError("Unimplemented") } public static var title2: Self { fatalError("Unimplemented") } @@ -423,7 +423,7 @@ public struct MarkdownStyle: Hashable { } } - public struct HeadingScales: Hashable { + public struct HeadingScales: Hashable, Sendable { public init( h1: CGFloat, h2: CGFloat, @@ -444,7 +444,7 @@ public struct MarkdownStyle: Hashable { } } - public struct Measurements: Hashable { + public struct Measurements: Hashable, Sendable { public var codeFontScale: CGFloat public var headIndentStep: CGFloat public var tailIndentStep: CGFloat diff --git a/Sources/MarkdownUI/Utility/InlineNode+RawImageData.swift b/Sources/MarkdownUI/Utility/InlineNode+RawImageData.swift index 11f77d3f..71b1bd8b 100644 --- a/Sources/MarkdownUI/Utility/InlineNode+RawImageData.swift +++ b/Sources/MarkdownUI/Utility/InlineNode+RawImageData.swift @@ -1,6 +1,6 @@ import Foundation -struct RawImageData: Hashable { +struct RawImageData: Hashable, Sendable { var source: String var alt: String var destination: String? diff --git a/Sources/MarkdownUI/Utility/RelativeSize.swift b/Sources/MarkdownUI/Utility/RelativeSize.swift index 36ee9bfb..95626810 100644 --- a/Sources/MarkdownUI/Utility/RelativeSize.swift +++ b/Sources/MarkdownUI/Utility/RelativeSize.swift @@ -21,8 +21,8 @@ import SwiftUI /// FontSize(.em(2)) /// } /// ``` -public struct RelativeSize: Hashable { - enum Unit: Hashable { +public struct RelativeSize: Hashable, Sendable { + enum Unit: Hashable, Sendable { case em case rem } diff --git a/Sources/MarkdownUI/Views/Blocks/TableBorderSelector.swift b/Sources/MarkdownUI/Views/Blocks/TableBorderSelector.swift index 9b85d8a0..fbed0ff7 100644 --- a/Sources/MarkdownUI/Views/Blocks/TableBorderSelector.swift +++ b/Sources/MarkdownUI/Views/Blocks/TableBorderSelector.swift @@ -3,8 +3,8 @@ import SwiftUI /// A type that selects the visible borders on a Markdown table. /// /// You use a table border selector to select the visible borders when creating a ``TableBorderStyle``. -public struct TableBorderSelector { - var rectangles: (_ tableBounds: TableBounds, _ borderWidth: CGFloat) -> [CGRect] +public struct TableBorderSelector: Sendable { + var rectangles: @Sendable (_ tableBounds: TableBounds, _ borderWidth: CGFloat) -> [CGRect] } extension TableBorderSelector { diff --git a/Sources/MarkdownUI/Views/Blocks/TableBounds.swift b/Sources/MarkdownUI/Views/Blocks/TableBounds.swift index 2fc1eb9e..5345c078 100644 --- a/Sources/MarkdownUI/Views/Blocks/TableBounds.swift +++ b/Sources/MarkdownUI/Views/Blocks/TableBounds.swift @@ -1,6 +1,6 @@ import SwiftUI -struct TableBounds { +struct TableBounds: Sendable { var rowCount: Int { self.rows.count } @@ -111,7 +111,7 @@ extension View { } } -private struct TableCellIndex: Hashable { +private struct TableCellIndex: Hashable, Sendable { var row: Int var column: Int } diff --git a/Sources/MarkdownUI/Views/Environment/Environment+InlineImageProvider.swift b/Sources/MarkdownUI/Views/Environment/Environment+InlineImageProvider.swift index fa1b5918..171a7224 100644 --- a/Sources/MarkdownUI/Views/Environment/Environment+InlineImageProvider.swift +++ b/Sources/MarkdownUI/Views/Environment/Environment+InlineImageProvider.swift @@ -19,6 +19,6 @@ extension EnvironmentValues { } } -private struct InlineImageProviderKey: EnvironmentKey { +private struct InlineImageProviderKey: EnvironmentKey, Sendable { static let defaultValue: InlineImageProvider = .default }