Skip to content

Commit

Permalink
Support using SwiftUI LottieView with strict concurrency enabled (#2126)
Browse files Browse the repository at this point in the history
  • Loading branch information
calda authored Aug 3, 2023
1 parent 3a02801 commit 25a50f2
Show file tree
Hide file tree
Showing 37 changed files with 85 additions and 31 deletions.
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 {

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 { }
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

0 comments on commit 25a50f2

Please sign in to comment.