Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using SwiftUI LottieView with strict concurrency enabled #2126

Merged
merged 8 commits into from
Aug 3, 2023
Merged
4 changes: 4 additions & 0 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "-warnings-as-errors -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand Down Expand Up @@ -407,6 +408,7 @@
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "-warnings-as-errors -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand Down Expand Up @@ -443,6 +445,7 @@
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "-enable-upcoming-feature StrictConcurrency -warn-concurrency";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.lottie.example.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
Expand Down Expand Up @@ -472,6 +475,7 @@
"@executable_path/Frameworks",
);
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "-enable-upcoming-feature StrictConcurrency -warn-concurrency";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.lottie.example.iOS;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
Expand Down
1 change: 1 addition & 0 deletions Example/Example/RemoteAnimationDemoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SwiftUI

// MARK: - AnimationListView

@MainActor
struct RemoteAnimationsDemoView: View {
Comment on lines +9 to 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should't Views be main actors by default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so too, but you get a concurrency error without this. Maybe only body is implicitly main-actor but not the other properties?


struct Item: Hashable {
Expand Down
6 changes: 6 additions & 0 deletions Lottie.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3229,6 +3229,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -3262,6 +3263,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -3334,6 +3336,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SDKROOT = macosx;
Expand Down Expand Up @@ -3368,6 +3371,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SDKROOT = macosx;
Expand Down Expand Up @@ -3400,6 +3404,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SDKROOT = appletvos;
Expand Down Expand Up @@ -3434,6 +3439,7 @@
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
OTHER_SWIFT_FLAGS = "-warnings-as-errors";
PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.Lottie;
PRODUCT_NAME = Lottie;
SDKROOT = appletvos;
Expand Down
6 changes: 3 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ namespace :build do

desc 'Builds the Lottie package for iOS'
task :iOS do
xcodebuild('build -scheme "Lottie (iOS)" -destination generic/platform=iOS -workspace Lottie.xcworkspace')
xcodebuild('build -scheme "Lottie (iOS)" -destination generic/platform=iOS -workspace Lottie.xcworkspace OTHER_SWIFT_FLAGS="-warnings-as-errors"')
end

desc 'Builds the Lottie package for macOS'
task :macOS do
xcodebuild('build -scheme "Lottie (macOS)" -destination generic/platform=macOS -workspace Lottie.xcworkspace')
xcodebuild('build -scheme "Lottie (macOS)" -destination generic/platform=macOS -workspace Lottie.xcworkspace OTHER_SWIFT_FLAGS="-warnings-as-errors"')
end

desc 'Builds the Lottie package for tvOS'
task :tvOS do
xcodebuild('build -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -workspace Lottie.xcworkspace')
xcodebuild('build -scheme "Lottie (tvOS)" -destination generic/platform=tvOS -workspace Lottie.xcworkspace OTHER_SWIFT_FLAGS="-warnings-as-errors"')
end
end

Expand Down
8 changes: 8 additions & 0 deletions Sources/Private/Model/Assets/Asset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import Foundation

// MARK: - Asset

public class Asset: Codable, DictionaryInitializable {

// MARK: Lifecycle
Expand Down Expand Up @@ -41,3 +43,9 @@ public class Asset: Codable, DictionaryInitializable {
case id
}
}

// MARK: Sendable

/// Since `Asset` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
/// All `Asset` subclasses are immutable `Sendable` values.
extension Asset: @unchecked Sendable { }
2 changes: 1 addition & 1 deletion Sources/Private/Model/Assets/AssetLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

final class AssetLibrary: Codable, AnyInitializable {
final class AssetLibrary: Codable, AnyInitializable, Sendable {

// MARK: Lifecycle

Expand Down
4 changes: 4 additions & 0 deletions Sources/Private/Model/Keyframes/KeyframeGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ extension KeyframeGroup: Hashable where T: Hashable {
}
}

// MARK: Sendable

extension KeyframeGroup: Sendable where T: Sendable { }

extension Keyframe {
/// Creates a copy of this `Keyframe` with the same timing data, but a different value
func withValue<Value>(_ newValue: Value) -> Keyframe<Value> {
Expand Down
6 changes: 6 additions & 0 deletions Sources/Private/Model/Layers/LayerModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,9 @@ extension Array where Element == LayerModel {
}
}
}

// MARK: - LayerModel + Sendable

/// Since `LayerModel` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
/// All `LayerModel` subclasses are immutable `Sendable` values.
extension LayerModel: @unchecked Sendable { }
2 changes: 1 addition & 1 deletion Sources/Private/Model/Objects/Marker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

/// A time marker
final class Marker: Codable, DictionaryInitializable {
final class Marker: Codable, Sendable, DictionaryInitializable {

// MARK: Lifecycle

Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/Model/ShapeItems/GradientFill.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - GradientType

enum GradientType: Int, Codable {
enum GradientType: Int, Codable, Sendable {
case none
case linear
case radial
Expand Down
4 changes: 2 additions & 2 deletions Sources/Private/Model/ShapeItems/GradientStroke.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - LineCap

enum LineCap: Int, Codable {
enum LineCap: Int, Codable, Sendable {
case none
case butt
case round
Expand All @@ -18,7 +18,7 @@ enum LineCap: Int, Codable {

// MARK: - LineJoin

enum LineJoin: Int, Codable {
enum LineJoin: Int, Codable, Sendable {
case none
case miter
case round
Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/Model/ShapeItems/Merge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - MergeMode

enum MergeMode: Int, Codable {
enum MergeMode: Int, Codable, Sendable {
case none
case merge
case add
Expand Down
8 changes: 7 additions & 1 deletion Sources/Private/Model/ShapeItems/ShapeItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extension ShapeType: ClassFamily {

// MARK: - ShapeType

enum ShapeType: String, Codable {
enum ShapeType: String, Codable, Sendable {
case ellipse = "el"
case fill = "fl"
case gradientFill = "gf"
Expand Down Expand Up @@ -165,3 +165,9 @@ extension Array where Element == ShapeItem {
}
}
}

// MARK: - ShapeItem + Sendable

/// Since `ShapeItem` isn't `final`, we have to use `@unchecked Sendable` instead of `Sendable.`
/// All `ShapeItem` subclasses are immutable `Sendable` values.
extension ShapeItem: @unchecked Sendable { }
2 changes: 1 addition & 1 deletion Sources/Private/Model/ShapeItems/Star.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - StarType

enum StarType: Int, Codable {
enum StarType: Int, Codable, Sendable {
case none
case star
case polygon
Expand Down
4 changes: 2 additions & 2 deletions Sources/Private/Model/Text/Font.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - Font

final class Font: Codable, DictionaryInitializable {
final class Font: Codable, Sendable, DictionaryInitializable {

// MARK: Lifecycle

Expand Down Expand Up @@ -41,7 +41,7 @@ final class Font: Codable, DictionaryInitializable {
// MARK: - FontList

/// A list of fonts
final class FontList: Codable, DictionaryInitializable {
final class FontList: Codable, Sendable, DictionaryInitializable {

// MARK: Lifecycle

Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/Model/Text/Glyph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

/// A model that holds a vector character
final class Glyph: Codable, DictionaryInitializable {
final class Glyph: Codable, Sendable, DictionaryInitializable {

// MARK: Lifecycle

Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/Utility/LottieAnimationSource.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Created by Cal Stephens on 7/26/23.
// Copyright © 2023 Airbnb Inc. All rights reserved.

public enum LottieAnimationSource {
public enum LottieAnimationSource: Sendable {
case lottieAnimation(LottieAnimation)
case dotLottieFile(DotLottieFile)

Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/Utility/Primitives/VectorsExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ extension Double {
// MARK: - LottieVector2D

/// Needed for decoding json {x: y:} to a CGPoint
public struct LottieVector2D: Codable, Hashable {
public struct LottieVector2D: Codable, Hashable, Sendable {

// MARK: Lifecycle

Expand Down
8 changes: 4 additions & 4 deletions Sources/Public/Animation/LottieAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - CoordinateSpace

public enum CoordinateSpace: Int, Codable {
public enum CoordinateSpace: Int, Codable, Sendable {
case type2d
case type3d
}
Expand All @@ -20,7 +20,7 @@ public enum CoordinateSpace: Int, Codable {
///
/// A `LottieAnimation` holds all of the animation data backing a Lottie Animation.
/// Codable, see JSON schema [here](https://github.com/airbnb/lottie-web/tree/master/docs/json).
public final class LottieAnimation: Codable, DictionaryInitializable {
public final class LottieAnimation: Codable, Sendable, DictionaryInitializable {

// MARK: Lifecycle

Expand Down Expand Up @@ -164,7 +164,7 @@ public final class LottieAnimation: Codable, DictionaryInitializable {
/// - reducedMotion
/// - reduced_motion
/// - reduced-motion
lazy private(set) var reducedMotionMarker: Marker? = {
var reducedMotionMarker: Marker? {
let allowedReducedMotionMarkerNames = Set([
"reduced motion",
"reduced_motion",
Expand All @@ -175,5 +175,5 @@ public final class LottieAnimation: Codable, DictionaryInitializable {
return markers?.first(where: { marker in
allowedReducedMotionMarkerNames.contains(marker.name.lowercased())
})
}()
}
}
3 changes: 2 additions & 1 deletion Sources/Public/AnimationCache/AnimationCacheProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
//

import Foundation

/// `AnimationCacheProvider` is a protocol that describes an Animation Cache.
/// Animation Cache is used when loading `LottieAnimation` models. Using an Animation Cache
/// can increase performance when loading an animation multiple times.
///
/// Lottie comes with a prebuilt LRU Animation Cache.
public protocol AnimationCacheProvider: AnyObject {
public protocol AnimationCacheProvider: AnyObject, Sendable {

func animation(forKey: String) -> LottieAnimation?

Expand Down
11 changes: 5 additions & 6 deletions Sources/Public/AnimationCache/DefaultAnimationCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Foundation
/// The default size of the cache is 100.
///
/// This cache implementation also responds to memory pressure, as it's backed by `NSCache`.
public class DefaultAnimationCache: AnimationCacheProvider {
public class DefaultAnimationCache: AnimationCacheProvider, @unchecked Sendable {

// MARK: Lifecycle

Expand All @@ -27,10 +27,9 @@ public class DefaultAnimationCache: AnimationCacheProvider {
public static let sharedCache = DefaultAnimationCache()

/// The size of the cache.
public var cacheSize = defaultCacheCountLimit {
didSet {
cache.countLimit = cacheSize
}
public var cacheSize: Int {
get { cache.countLimit }
set { cache.countLimit = newValue }
}

/// Clears the Cache.
Expand All @@ -50,5 +49,5 @@ public class DefaultAnimationCache: AnimationCacheProvider {

private static let defaultCacheCountLimit = 100

private var cache = NSCache<NSString, LottieAnimation>()
private let cache = NSCache<NSString, LottieAnimation>()
}
7 changes: 7 additions & 0 deletions Sources/Public/DotLottie/DotLottieFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,10 @@ extension String {
(self as NSString).deletingPathExtension
}
}

// MARK: - DotLottieFile + Sendable

// Mark `DotLottieFile` as `@unchecked Sendable` to allow it to be used when strict concurrency is enabled.
// In the future, it may be necessary to make changes to the internal implementation of `DotLottieFile`
// to make it truly thread-safe.
extension DotLottieFile: @unchecked Sendable { }
calda marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions Sources/Public/Keyframes/Keyframe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ extension Keyframe: Hashable where T: Hashable {
hasher.combine(spatialOutTangent)
}
}

// MARK: Sendable

extension Keyframe: Sendable where T: Sendable { }
4 changes: 2 additions & 2 deletions Sources/Public/Primitives/Vectors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

// MARK: - LottieVector1D

public struct LottieVector1D: Hashable {
public struct LottieVector1D: Hashable, Sendable {

public init(_ value: Double) {
self.value = value
Expand All @@ -23,7 +23,7 @@ public struct LottieVector1D: Hashable {

/// A three dimensional vector.
/// These vectors are encoded and decoded from [Double]
public struct LottieVector3D: Hashable {
public struct LottieVector3D: Hashable, Sendable {

public let x: Double
public let y: Double
Expand Down
Loading
Loading