lottie-ios 4.1.2 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +27 -9
- package/Lottie.xcodeproj/project.pbxproj +158 -70
- package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +2 -2
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/IDEFindNavigatorScopes.plist +5 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +258 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +13 -2
- package/Package.swift +2 -1
- package/README.md +3 -3
- package/Rakefile +8 -4
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +16 -2
- package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +6 -6
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +76 -7
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +66 -102
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +3 -3
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +66 -17
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +55 -32
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +16 -12
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +3 -3
- package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +24 -11
- package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +2 -2
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +13 -2
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +9 -1
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +22 -11
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +1 -1
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +13 -2
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +16 -7
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +20 -8
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +28 -9
- package/Sources/Private/Model/Assets/ImageAsset.swift +4 -3
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +2 -8
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +3 -14
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +11 -1
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+BackingConfiguration.swift +147 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Helpers.swift +351 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+MemoryFile.swift +183 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Progress.swift +66 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Reading.swift +144 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ReadingDeprecated.swift +49 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+Writing.swift +385 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+WritingDeprecated.swift +91 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Archive+ZIP64.swift +170 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipArchive.swift → ZipFoundation/Archive.swift} +150 -227
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+Compression.swift +403 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/Data+CompressionDeprecated.swift +44 -0
- package/Sources/Private/Model/DotLottie/{Zip → ZipFoundation}/Data+Serialization.swift +62 -0
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+Serialization.swift → ZipFoundation/Entry+Serialization.swift} +7 -7
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry+ZIP64.swift → ZipFoundation/Entry+ZIP64.swift} +13 -19
- package/Sources/Private/Model/DotLottie/{Zip/ZipEntry.swift → ZipFoundation/Entry.swift} +141 -10
- package/Sources/Private/Model/DotLottie/ZipFoundation/FileManager+ZIP.swift +368 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/README.md +24 -0
- package/Sources/Private/Model/DotLottie/ZipFoundation/URL+ZIP.swift +32 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +5 -14
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +31 -8
- package/Sources/Private/Model/Objects/Transform.swift +58 -17
- package/Sources/Private/Model/ShapeItems/Repeater.swift +41 -7
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +61 -7
- package/Sources/Private/Model/Text/TextAnimator.swift +37 -5
- package/Sources/Private/RootAnimationLayer.swift +3 -1
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +12 -4
- package/Sources/Private/Utility/Extensions/DataExtension.swift +14 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +11 -0
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +10 -13
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +28 -6
- package/Sources/Public/Animation/LottieAnimationHelpers.swift +12 -10
- package/Sources/Public/Animation/LottieAnimationView.swift +213 -186
- package/Sources/Public/DotLottie/DotLottieFile.swift +11 -34
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +101 -74
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +90 -0
- package/Sources/Public/iOS/LottieAnimationViewBase.swift +1 -1
- package/Sources/Public/macOS/LottieAnimationViewBase.macOS.swift +1 -1
- package/lottie-ios.podspec +1 -1
- package/package.json +1 -1
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/project.xcworkspace/xcuserdata/valentinperignon.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +0 -6
- package/LottieAnimation/LottieAnimation.xcodeproj/xcuserdata/valentinperignon.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +0 -134
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +0 -130
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +0 -59
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
import CoreFoundation
|
|
12
12
|
import Foundation
|
|
13
13
|
|
|
14
|
-
// MARK: -
|
|
14
|
+
// MARK: - Entry
|
|
15
15
|
|
|
16
16
|
/// A value that represents a file, a directory or a symbolic link within a ZIP `Archive`.
|
|
17
17
|
///
|
|
18
18
|
/// You can retrieve instances of `Entry` from an `Archive` via subscripting or iteration.
|
|
19
19
|
/// Entries are identified by their `path`.
|
|
20
|
-
struct
|
|
20
|
+
struct Entry: Equatable {
|
|
21
21
|
|
|
22
22
|
// MARK: Lifecycle
|
|
23
23
|
|
|
@@ -37,6 +37,34 @@ struct ZipEntry: Equatable {
|
|
|
37
37
|
|
|
38
38
|
// MARK: Internal
|
|
39
39
|
|
|
40
|
+
/// The type of an `Entry` in a ZIP `Archive`.
|
|
41
|
+
enum EntryType: Int {
|
|
42
|
+
/// Indicates a regular file.
|
|
43
|
+
case file
|
|
44
|
+
/// Indicates a directory.
|
|
45
|
+
case directory
|
|
46
|
+
/// Indicates a symbolic link.
|
|
47
|
+
case symlink
|
|
48
|
+
|
|
49
|
+
init(mode: mode_t) {
|
|
50
|
+
switch mode & S_IFMT {
|
|
51
|
+
case S_IFDIR:
|
|
52
|
+
self = .directory
|
|
53
|
+
case S_IFLNK:
|
|
54
|
+
self = .symlink
|
|
55
|
+
default:
|
|
56
|
+
self = .file
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
enum OSType: UInt {
|
|
62
|
+
case msdos = 0
|
|
63
|
+
case unix = 3
|
|
64
|
+
case osx = 19
|
|
65
|
+
case unused = 20
|
|
66
|
+
}
|
|
67
|
+
|
|
40
68
|
struct LocalFileHeader: DataSerializable {
|
|
41
69
|
let localFileHeaderSignature = UInt32(localFileHeaderStructSignature)
|
|
42
70
|
let versionNeededToExtract: UInt16
|
|
@@ -106,10 +134,6 @@ struct ZipEntry: Equatable {
|
|
|
106
134
|
}
|
|
107
135
|
}
|
|
108
136
|
|
|
109
|
-
static let localFileHeaderStructSignature = 0x04034b50
|
|
110
|
-
static let dataDescriptorStructSignature = 0x08074b50
|
|
111
|
-
static let centralDirectoryStructSignature = 0x02014b50
|
|
112
|
-
|
|
113
137
|
let centralDirectoryStructure: CentralDirectoryStructure
|
|
114
138
|
let localFileHeader: LocalFileHeader
|
|
115
139
|
let dataDescriptor: DefaultDataDescriptor?
|
|
@@ -135,16 +159,42 @@ struct ZipEntry: Equatable {
|
|
|
135
159
|
/// The `CRC32` checksum of the receiver.
|
|
136
160
|
///
|
|
137
161
|
/// - Note: Always returns `0` for entries of type `EntryType.directory`.
|
|
138
|
-
var checksum:
|
|
162
|
+
var checksum: CRC32 {
|
|
139
163
|
if centralDirectoryStructure.usesDataDescriptor {
|
|
140
164
|
return zip64DataDescriptor?.crc32 ?? dataDescriptor?.crc32 ?? 0
|
|
141
165
|
}
|
|
142
166
|
return centralDirectoryStructure.crc32
|
|
143
167
|
}
|
|
144
168
|
|
|
169
|
+
/// The `EntryType` of the receiver.
|
|
170
|
+
var type: EntryType {
|
|
171
|
+
// OS Type is stored in the upper byte of versionMadeBy
|
|
172
|
+
let osTypeRaw = centralDirectoryStructure.versionMadeBy >> 8
|
|
173
|
+
let osType = OSType(rawValue: UInt(osTypeRaw)) ?? .unused
|
|
174
|
+
var isDirectory = path.hasSuffix("/")
|
|
175
|
+
switch osType {
|
|
176
|
+
case .unix, .osx:
|
|
177
|
+
let mode = mode_t(centralDirectoryStructure.externalFileAttributes >> 16) & S_IFMT
|
|
178
|
+
switch mode {
|
|
179
|
+
case S_IFREG:
|
|
180
|
+
return .file
|
|
181
|
+
case S_IFDIR:
|
|
182
|
+
return .directory
|
|
183
|
+
case S_IFLNK:
|
|
184
|
+
return .symlink
|
|
185
|
+
default:
|
|
186
|
+
return isDirectory ? .directory : .file
|
|
187
|
+
}
|
|
188
|
+
case .msdos:
|
|
189
|
+
isDirectory = isDirectory || ((centralDirectoryStructure.externalFileAttributes >> 4) == 0x01)
|
|
190
|
+
fallthrough // For all other OSes we can only guess based on the directory suffix char
|
|
191
|
+
default: return isDirectory ? .directory : .file
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
145
195
|
/// Indicates whether or not the receiver is compressed.
|
|
146
196
|
var isCompressed: Bool {
|
|
147
|
-
localFileHeader.compressionMethod !=
|
|
197
|
+
localFileHeader.compressionMethod != CompressionMethod.none.rawValue
|
|
148
198
|
}
|
|
149
199
|
|
|
150
200
|
/// The size of the receiver's compressed data.
|
|
@@ -186,7 +236,7 @@ struct ZipEntry: Equatable {
|
|
|
186
236
|
return dataOffset
|
|
187
237
|
}
|
|
188
238
|
|
|
189
|
-
static func == (lhs:
|
|
239
|
+
static func == (lhs: Entry, rhs: Entry) -> Bool {
|
|
190
240
|
lhs.path == rhs.path
|
|
191
241
|
&& lhs.localFileHeader.crc32 == rhs.localFileHeader.crc32
|
|
192
242
|
&& lhs.centralDirectoryStructure.effectiveRelativeOffsetOfLocalHeader
|
|
@@ -203,7 +253,88 @@ struct ZipEntry: Equatable {
|
|
|
203
253
|
|
|
204
254
|
}
|
|
205
255
|
|
|
206
|
-
extension
|
|
256
|
+
extension Entry.CentralDirectoryStructure {
|
|
257
|
+
|
|
258
|
+
init(
|
|
259
|
+
localFileHeader: Entry.LocalFileHeader,
|
|
260
|
+
fileAttributes: UInt32,
|
|
261
|
+
relativeOffset: UInt32,
|
|
262
|
+
extraField: (length: UInt16, data: Data))
|
|
263
|
+
{
|
|
264
|
+
versionMadeBy = UInt16(789)
|
|
265
|
+
versionNeededToExtract = localFileHeader.versionNeededToExtract
|
|
266
|
+
generalPurposeBitFlag = localFileHeader.generalPurposeBitFlag
|
|
267
|
+
compressionMethod = localFileHeader.compressionMethod
|
|
268
|
+
lastModFileTime = localFileHeader.lastModFileTime
|
|
269
|
+
lastModFileDate = localFileHeader.lastModFileDate
|
|
270
|
+
crc32 = localFileHeader.crc32
|
|
271
|
+
compressedSize = localFileHeader.compressedSize
|
|
272
|
+
uncompressedSize = localFileHeader.uncompressedSize
|
|
273
|
+
fileNameLength = localFileHeader.fileNameLength
|
|
274
|
+
extraFieldLength = extraField.length
|
|
275
|
+
fileCommentLength = UInt16(0)
|
|
276
|
+
diskNumberStart = UInt16(0)
|
|
277
|
+
internalFileAttributes = UInt16(0)
|
|
278
|
+
externalFileAttributes = fileAttributes
|
|
279
|
+
relativeOffsetOfLocalHeader = relativeOffset
|
|
280
|
+
fileNameData = localFileHeader.fileNameData
|
|
281
|
+
extraFieldData = extraField.data
|
|
282
|
+
fileCommentData = Data()
|
|
283
|
+
if
|
|
284
|
+
let zip64ExtendedInformation = Entry.ZIP64ExtendedInformation.scanForZIP64Field(
|
|
285
|
+
in: extraFieldData,
|
|
286
|
+
fields: validFields)
|
|
287
|
+
{
|
|
288
|
+
extraFields = [zip64ExtendedInformation]
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
init(
|
|
293
|
+
centralDirectoryStructure: Entry.CentralDirectoryStructure,
|
|
294
|
+
zip64ExtendedInformation: Entry.ZIP64ExtendedInformation?,
|
|
295
|
+
relativeOffset: UInt32)
|
|
296
|
+
{
|
|
297
|
+
if let existingInfo = zip64ExtendedInformation {
|
|
298
|
+
extraFieldData = existingInfo.data
|
|
299
|
+
versionNeededToExtract = max(
|
|
300
|
+
centralDirectoryStructure.versionNeededToExtract,
|
|
301
|
+
Archive.Version.v45.rawValue)
|
|
302
|
+
} else {
|
|
303
|
+
extraFieldData = centralDirectoryStructure.extraFieldData
|
|
304
|
+
let existingVersion = centralDirectoryStructure.versionNeededToExtract
|
|
305
|
+
versionNeededToExtract = existingVersion < Archive.Version.v45.rawValue
|
|
306
|
+
? centralDirectoryStructure.versionNeededToExtract
|
|
307
|
+
: Archive.Version.v20.rawValue
|
|
308
|
+
}
|
|
309
|
+
extraFieldLength = UInt16(extraFieldData.count)
|
|
310
|
+
relativeOffsetOfLocalHeader = relativeOffset
|
|
311
|
+
versionMadeBy = centralDirectoryStructure.versionMadeBy
|
|
312
|
+
generalPurposeBitFlag = centralDirectoryStructure.generalPurposeBitFlag
|
|
313
|
+
compressionMethod = centralDirectoryStructure.compressionMethod
|
|
314
|
+
lastModFileTime = centralDirectoryStructure.lastModFileTime
|
|
315
|
+
lastModFileDate = centralDirectoryStructure.lastModFileDate
|
|
316
|
+
crc32 = centralDirectoryStructure.crc32
|
|
317
|
+
compressedSize = centralDirectoryStructure.compressedSize
|
|
318
|
+
uncompressedSize = centralDirectoryStructure.uncompressedSize
|
|
319
|
+
fileNameLength = centralDirectoryStructure.fileNameLength
|
|
320
|
+
fileCommentLength = centralDirectoryStructure.fileCommentLength
|
|
321
|
+
diskNumberStart = centralDirectoryStructure.diskNumberStart
|
|
322
|
+
internalFileAttributes = centralDirectoryStructure.internalFileAttributes
|
|
323
|
+
externalFileAttributes = centralDirectoryStructure.externalFileAttributes
|
|
324
|
+
fileNameData = centralDirectoryStructure.fileNameData
|
|
325
|
+
fileCommentData = centralDirectoryStructure.fileCommentData
|
|
326
|
+
if
|
|
327
|
+
let zip64ExtendedInformation = Entry.ZIP64ExtendedInformation.scanForZIP64Field(
|
|
328
|
+
in: extraFieldData,
|
|
329
|
+
fields: validFields)
|
|
330
|
+
{
|
|
331
|
+
extraFields = [zip64ExtendedInformation]
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
extension Entry.CentralDirectoryStructure {
|
|
337
|
+
|
|
207
338
|
var effectiveCompressedSize: UInt64 {
|
|
208
339
|
if isZIP64, let compressedSize = zip64ExtendedInformation?.compressedSize, compressedSize > 0 {
|
|
209
340
|
return compressedSize
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FileManager+ZIP.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension FileManager {
|
|
14
|
+
typealias CentralDirectoryStructure = Entry.CentralDirectoryStructure
|
|
15
|
+
|
|
16
|
+
class func attributes(from entry: Entry) -> [FileAttributeKey: Any] {
|
|
17
|
+
let centralDirectoryStructure = entry.centralDirectoryStructure
|
|
18
|
+
let entryType = entry.type
|
|
19
|
+
let fileTime = centralDirectoryStructure.lastModFileTime
|
|
20
|
+
let fileDate = centralDirectoryStructure.lastModFileDate
|
|
21
|
+
let defaultPermissions = entryType == .directory ? defaultDirectoryPermissions : defaultFilePermissions
|
|
22
|
+
var attributes = [.posixPermissions: defaultPermissions] as [FileAttributeKey: Any]
|
|
23
|
+
// Certain keys are not yet supported in swift-corelibs
|
|
24
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
25
|
+
attributes[.modificationDate] = Date(dateTime: (fileDate, fileTime))
|
|
26
|
+
#endif
|
|
27
|
+
let versionMadeBy = centralDirectoryStructure.versionMadeBy
|
|
28
|
+
guard let osType = Entry.OSType(rawValue: UInt(versionMadeBy >> 8)) else { return attributes }
|
|
29
|
+
|
|
30
|
+
let externalFileAttributes = centralDirectoryStructure.externalFileAttributes
|
|
31
|
+
let permissions = permissions(for: externalFileAttributes, osType: osType, entryType: entryType)
|
|
32
|
+
attributes[.posixPermissions] = NSNumber(value: permissions)
|
|
33
|
+
return attributes
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class func permissions(
|
|
37
|
+
for externalFileAttributes: UInt32,
|
|
38
|
+
osType: Entry.OSType,
|
|
39
|
+
entryType: Entry.EntryType)
|
|
40
|
+
-> UInt16
|
|
41
|
+
{
|
|
42
|
+
switch osType {
|
|
43
|
+
case .unix, .osx:
|
|
44
|
+
let permissions = mode_t(externalFileAttributes >> 16) & ~S_IFMT
|
|
45
|
+
let defaultPermissions = entryType == .directory ? defaultDirectoryPermissions : defaultFilePermissions
|
|
46
|
+
return permissions == 0 ? defaultPermissions : UInt16(permissions)
|
|
47
|
+
default:
|
|
48
|
+
return entryType == .directory ? defaultDirectoryPermissions : defaultFilePermissions
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class func externalFileAttributesForEntry(of type: Entry.EntryType, permissions: UInt16) -> UInt32 {
|
|
53
|
+
var typeInt: UInt16
|
|
54
|
+
switch type {
|
|
55
|
+
case .file:
|
|
56
|
+
typeInt = UInt16(S_IFREG)
|
|
57
|
+
case .directory:
|
|
58
|
+
typeInt = UInt16(S_IFDIR)
|
|
59
|
+
case .symlink:
|
|
60
|
+
typeInt = UInt16(S_IFLNK)
|
|
61
|
+
}
|
|
62
|
+
var externalFileAttributes = UInt32(typeInt | UInt16(permissions))
|
|
63
|
+
externalFileAttributes = (externalFileAttributes << 16)
|
|
64
|
+
return externalFileAttributes
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class func permissionsForItem(at URL: URL) throws -> UInt16 {
|
|
68
|
+
let fileManager = FileManager()
|
|
69
|
+
let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: URL.path)
|
|
70
|
+
var fileStat = stat()
|
|
71
|
+
lstat(entryFileSystemRepresentation, &fileStat)
|
|
72
|
+
let permissions = fileStat.st_mode
|
|
73
|
+
return UInt16(permissions)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class func fileModificationDateTimeForItem(at url: URL) throws -> Date {
|
|
77
|
+
let fileManager = FileManager()
|
|
78
|
+
guard fileManager.itemExists(at: url) else {
|
|
79
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: url.path])
|
|
80
|
+
}
|
|
81
|
+
let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
82
|
+
var fileStat = stat()
|
|
83
|
+
lstat(entryFileSystemRepresentation, &fileStat)
|
|
84
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
85
|
+
let modTimeSpec = fileStat.st_mtimespec
|
|
86
|
+
#else
|
|
87
|
+
let modTimeSpec = fileStat.st_mtim
|
|
88
|
+
#endif
|
|
89
|
+
|
|
90
|
+
let timeStamp = TimeInterval(modTimeSpec.tv_sec) + TimeInterval(modTimeSpec.tv_nsec) / 1000000000.0
|
|
91
|
+
let modDate = Date(timeIntervalSince1970: timeStamp)
|
|
92
|
+
return modDate
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
class func fileSizeForItem(at url: URL) throws -> Int64 {
|
|
96
|
+
let fileManager = FileManager()
|
|
97
|
+
guard fileManager.itemExists(at: url) else {
|
|
98
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: url.path])
|
|
99
|
+
}
|
|
100
|
+
let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
101
|
+
var fileStat = stat()
|
|
102
|
+
lstat(entryFileSystemRepresentation, &fileStat)
|
|
103
|
+
guard fileStat.st_size >= 0 else {
|
|
104
|
+
throw CocoaError(.fileReadTooLarge, userInfo: [NSFilePathErrorKey: url.path])
|
|
105
|
+
}
|
|
106
|
+
// `st_size` is a signed int value
|
|
107
|
+
return Int64(fileStat.st_size)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class func typeForItem(at url: URL) throws -> Entry.EntryType {
|
|
111
|
+
let fileManager = FileManager()
|
|
112
|
+
guard url.isFileURL, fileManager.itemExists(at: url) else {
|
|
113
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: url.path])
|
|
114
|
+
}
|
|
115
|
+
let entryFileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
116
|
+
var fileStat = stat()
|
|
117
|
+
lstat(entryFileSystemRepresentation, &fileStat)
|
|
118
|
+
return Entry.EntryType(mode: mode_t(fileStat.st_mode))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Zips the file or directory contents at the specified source URL to the destination URL.
|
|
122
|
+
///
|
|
123
|
+
/// If the item at the source URL is a directory, the directory itself will be
|
|
124
|
+
/// represented within the ZIP `Archive`. Calling this method with a directory URL
|
|
125
|
+
/// `file:///path/directory/` will create an archive with a `directory/` entry at the root level.
|
|
126
|
+
/// You can override this behavior by passing `false` for `shouldKeepParent`. In that case, the contents
|
|
127
|
+
/// of the source directory will be placed at the root of the archive.
|
|
128
|
+
/// - Parameters:
|
|
129
|
+
/// - sourceURL: The file URL pointing to an existing file or directory.
|
|
130
|
+
/// - destinationURL: The file URL that identifies the destination of the zip operation.
|
|
131
|
+
/// - shouldKeepParent: Indicates that the directory name of a source item should be used as root element
|
|
132
|
+
/// within the archive. Default is `true`.
|
|
133
|
+
/// - compressionMethod: Indicates the `CompressionMethod` that should be applied.
|
|
134
|
+
/// By default, `zipItem` will create uncompressed archives.
|
|
135
|
+
/// - progress: A progress object that can be used to track or cancel the zip operation.
|
|
136
|
+
/// - Throws: Throws an error if the source item does not exist or the destination URL is not writable.
|
|
137
|
+
func zipItem(
|
|
138
|
+
at sourceURL: URL,
|
|
139
|
+
to destinationURL: URL,
|
|
140
|
+
shouldKeepParent: Bool = true,
|
|
141
|
+
compressionMethod: CompressionMethod = .none,
|
|
142
|
+
progress: Progress? = nil)
|
|
143
|
+
throws
|
|
144
|
+
{
|
|
145
|
+
let fileManager = FileManager()
|
|
146
|
+
guard fileManager.itemExists(at: sourceURL) else {
|
|
147
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path])
|
|
148
|
+
}
|
|
149
|
+
guard !fileManager.itemExists(at: destinationURL) else {
|
|
150
|
+
throw CocoaError(.fileWriteFileExists, userInfo: [NSFilePathErrorKey: destinationURL.path])
|
|
151
|
+
}
|
|
152
|
+
guard let archive = Archive(url: destinationURL, accessMode: .create) else {
|
|
153
|
+
throw Archive.ArchiveError.unwritableArchive
|
|
154
|
+
}
|
|
155
|
+
let isDirectory = try FileManager.typeForItem(at: sourceURL) == .directory
|
|
156
|
+
if isDirectory {
|
|
157
|
+
let subPaths = try subpathsOfDirectory(atPath: sourceURL.path)
|
|
158
|
+
var totalUnitCount = Int64(0)
|
|
159
|
+
if let progress = progress {
|
|
160
|
+
totalUnitCount = subPaths.reduce(Int64(0)) {
|
|
161
|
+
let itemURL = sourceURL.appendingPathComponent($1)
|
|
162
|
+
let itemSize = archive.totalUnitCountForAddingItem(at: itemURL)
|
|
163
|
+
return $0 + itemSize
|
|
164
|
+
}
|
|
165
|
+
progress.totalUnitCount = totalUnitCount
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// If the caller wants to keep the parent directory, we use the lastPathComponent of the source URL
|
|
169
|
+
// as common base for all entries (similar to macOS' Archive Utility.app)
|
|
170
|
+
let directoryPrefix = sourceURL.lastPathComponent
|
|
171
|
+
for entryPath in subPaths {
|
|
172
|
+
let finalEntryPath = shouldKeepParent ? directoryPrefix + "/" + entryPath : entryPath
|
|
173
|
+
let finalBaseURL = shouldKeepParent ? sourceURL.deletingLastPathComponent() : sourceURL
|
|
174
|
+
if let progress = progress {
|
|
175
|
+
let itemURL = sourceURL.appendingPathComponent(entryPath)
|
|
176
|
+
let entryProgress = archive.makeProgressForAddingItem(at: itemURL)
|
|
177
|
+
progress.addChild(entryProgress, withPendingUnitCount: entryProgress.totalUnitCount)
|
|
178
|
+
try archive.addEntry(
|
|
179
|
+
with: finalEntryPath,
|
|
180
|
+
relativeTo: finalBaseURL,
|
|
181
|
+
compressionMethod: compressionMethod,
|
|
182
|
+
progress: entryProgress)
|
|
183
|
+
} else {
|
|
184
|
+
try archive.addEntry(
|
|
185
|
+
with: finalEntryPath,
|
|
186
|
+
relativeTo: finalBaseURL,
|
|
187
|
+
compressionMethod: compressionMethod)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
progress?.totalUnitCount = archive.totalUnitCountForAddingItem(at: sourceURL)
|
|
192
|
+
let baseURL = sourceURL.deletingLastPathComponent()
|
|
193
|
+
try archive.addEntry(
|
|
194
|
+
with: sourceURL.lastPathComponent,
|
|
195
|
+
relativeTo: baseURL,
|
|
196
|
+
compressionMethod: compressionMethod,
|
|
197
|
+
progress: progress)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Unzips the contents at the specified source URL to the destination URL.
|
|
202
|
+
///
|
|
203
|
+
/// - Parameters:
|
|
204
|
+
/// - sourceURL: The file URL pointing to an existing ZIP file.
|
|
205
|
+
/// - destinationURL: The file URL that identifies the destination directory of the unzip operation.
|
|
206
|
+
/// - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance.
|
|
207
|
+
/// - progress: A progress object that can be used to track or cancel the unzip operation.
|
|
208
|
+
/// - preferredEncoding: Encoding for entry paths. Overrides the encoding specified in the archive.
|
|
209
|
+
/// - Throws: Throws an error if the source item does not exist or the destination URL is not writable.
|
|
210
|
+
func unzipItem(
|
|
211
|
+
at sourceURL: URL,
|
|
212
|
+
to destinationURL: URL,
|
|
213
|
+
skipCRC32: Bool = false,
|
|
214
|
+
progress: Progress? = nil,
|
|
215
|
+
preferredEncoding: String.Encoding? = nil)
|
|
216
|
+
throws
|
|
217
|
+
{
|
|
218
|
+
let fileManager = FileManager()
|
|
219
|
+
guard fileManager.itemExists(at: sourceURL) else {
|
|
220
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path])
|
|
221
|
+
}
|
|
222
|
+
guard let archive = Archive(url: sourceURL, accessMode: .read, preferredEncoding: preferredEncoding) else {
|
|
223
|
+
throw Archive.ArchiveError.unreadableArchive
|
|
224
|
+
}
|
|
225
|
+
// Defer extraction of symlinks until all files & directories have been created.
|
|
226
|
+
// This is necessary because we can't create links to files that haven't been created yet.
|
|
227
|
+
let sortedEntries = archive.sorted { left, right -> Bool in
|
|
228
|
+
switch (left.type, right.type) {
|
|
229
|
+
case (.directory, .file): return true
|
|
230
|
+
case (.directory, .symlink): return true
|
|
231
|
+
case (.file, .symlink): return true
|
|
232
|
+
default: return false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
var totalUnitCount = Int64(0)
|
|
236
|
+
if let progress = progress {
|
|
237
|
+
totalUnitCount = sortedEntries.reduce(0) { $0 + archive.totalUnitCountForReading($1) }
|
|
238
|
+
progress.totalUnitCount = totalUnitCount
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for entry in sortedEntries {
|
|
242
|
+
let path = preferredEncoding == nil ? entry.path : entry.path(using: preferredEncoding!)
|
|
243
|
+
let entryURL = destinationURL.appendingPathComponent(path)
|
|
244
|
+
guard entryURL.isContained(in: destinationURL) else {
|
|
245
|
+
throw CocoaError(
|
|
246
|
+
.fileReadInvalidFileName,
|
|
247
|
+
userInfo: [NSFilePathErrorKey: entryURL.path])
|
|
248
|
+
}
|
|
249
|
+
let crc32: CRC32
|
|
250
|
+
if let progress = progress {
|
|
251
|
+
let entryProgress = archive.makeProgressForReading(entry)
|
|
252
|
+
progress.addChild(entryProgress, withPendingUnitCount: entryProgress.totalUnitCount)
|
|
253
|
+
crc32 = try archive.extract(entry, to: entryURL, skipCRC32: skipCRC32, progress: entryProgress)
|
|
254
|
+
} else {
|
|
255
|
+
crc32 = try archive.extract(entry, to: entryURL, skipCRC32: skipCRC32)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
func verifyChecksumIfNecessary() throws {
|
|
259
|
+
if skipCRC32 == false, crc32 != entry.checksum {
|
|
260
|
+
throw Archive.ArchiveError.invalidCRC32
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
try verifyChecksumIfNecessary()
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// MARK: - Helpers
|
|
268
|
+
|
|
269
|
+
func itemExists(at url: URL) -> Bool {
|
|
270
|
+
// Use `URL.checkResourceIsReachable()` instead of `FileManager.fileExists()` here
|
|
271
|
+
// because we don't want implicit symlink resolution.
|
|
272
|
+
// As per documentation, `FileManager.fileExists()` traverses symlinks and therefore a broken symlink
|
|
273
|
+
// would throw a `.fileReadNoSuchFile` false positive error.
|
|
274
|
+
// For ZIP files it may be intended to archive "broken" symlinks because they might be
|
|
275
|
+
// resolvable again when extracting the archive to a different destination.
|
|
276
|
+
(try? url.checkResourceIsReachable()) == true
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
func createParentDirectoryStructure(for url: URL) throws {
|
|
280
|
+
let parentDirectoryURL = url.deletingLastPathComponent()
|
|
281
|
+
try createDirectory(at: parentDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
extension Date {
|
|
287
|
+
|
|
288
|
+
// MARK: Lifecycle
|
|
289
|
+
|
|
290
|
+
init(dateTime: (UInt16, UInt16)) {
|
|
291
|
+
var msdosDateTime = Int(dateTime.0)
|
|
292
|
+
msdosDateTime <<= 16
|
|
293
|
+
msdosDateTime |= Int(dateTime.1)
|
|
294
|
+
var unixTime = tm()
|
|
295
|
+
unixTime.tm_sec = Int32((msdosDateTime & 31) * 2)
|
|
296
|
+
unixTime.tm_min = Int32((msdosDateTime >> 5) & 63)
|
|
297
|
+
unixTime.tm_hour = Int32((Int(dateTime.1) >> 11) & 31)
|
|
298
|
+
unixTime.tm_mday = Int32((msdosDateTime >> 16) & 31)
|
|
299
|
+
unixTime.tm_mon = Int32((msdosDateTime >> 21) & 15)
|
|
300
|
+
unixTime.tm_mon -= 1 // UNIX time struct month entries are zero based.
|
|
301
|
+
unixTime.tm_year = Int32(1980 + (msdosDateTime >> 25))
|
|
302
|
+
unixTime.tm_year -= 1900 // UNIX time structs count in "years since 1900".
|
|
303
|
+
let time = timegm(&unixTime)
|
|
304
|
+
self = Date(timeIntervalSince1970: TimeInterval(time))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// MARK: Internal
|
|
308
|
+
|
|
309
|
+
var fileModificationDateTime: (UInt16, UInt16) {
|
|
310
|
+
return (self.fileModificationDate, self.fileModificationTime)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
var fileModificationDate: UInt16 {
|
|
314
|
+
var time = time_t(timeIntervalSince1970)
|
|
315
|
+
guard let unixTime = gmtime(&time) else {
|
|
316
|
+
return 0
|
|
317
|
+
}
|
|
318
|
+
var year = unixTime.pointee.tm_year + 1900 // UNIX time structs count in "years since 1900".
|
|
319
|
+
// ZIP uses the MSDOS date format which has a valid range of 1980 - 2099.
|
|
320
|
+
year = year >= 1980 ? year : 1980
|
|
321
|
+
year = year <= 2099 ? year : 2099
|
|
322
|
+
let month = unixTime.pointee.tm_mon + 1 // UNIX time struct month entries are zero based.
|
|
323
|
+
let day = unixTime.pointee.tm_mday
|
|
324
|
+
return (UInt16)(day + (month * 32) + ((year - 1980) * 512))
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
var fileModificationTime: UInt16 {
|
|
328
|
+
var time = time_t(timeIntervalSince1970)
|
|
329
|
+
guard let unixTime = gmtime(&time) else {
|
|
330
|
+
return 0
|
|
331
|
+
}
|
|
332
|
+
let hour = unixTime.pointee.tm_hour
|
|
333
|
+
let minute = unixTime.pointee.tm_min
|
|
334
|
+
let second = unixTime.pointee.tm_sec
|
|
335
|
+
return (UInt16)((second / 2) + (minute * 32) + (hour * 2048))
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#if swift(>=4.2)
|
|
340
|
+
#else
|
|
341
|
+
|
|
342
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
343
|
+
#else
|
|
344
|
+
|
|
345
|
+
// The swift-corelibs-foundation version of NSError.swift was missing a convenience method to create
|
|
346
|
+
// error objects from error codes. (https://github.com/apple/swift-corelibs-foundation/pull/1420)
|
|
347
|
+
// We have to provide an implementation for non-Darwin platforms using Swift versions < 4.2.
|
|
348
|
+
|
|
349
|
+
extension CocoaError {
|
|
350
|
+
static func error(_ code: CocoaError.Code, userInfo: [AnyHashable: Any]? = nil, url: URL? = nil) -> Error {
|
|
351
|
+
var info: [String: Any] = userInfo as? [String: Any] ?? [:]
|
|
352
|
+
if let url = url {
|
|
353
|
+
info[NSURLErrorKey] = url
|
|
354
|
+
}
|
|
355
|
+
return NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: info)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#endif
|
|
360
|
+
#endif
|
|
361
|
+
|
|
362
|
+
extension URL {
|
|
363
|
+
func isContained(in parentDirectoryURL: URL) -> Bool {
|
|
364
|
+
// Ensure this URL is contained in the passed in URL
|
|
365
|
+
let parentDirectoryURL = URL(fileURLWithPath: parentDirectoryURL.path, isDirectory: true).standardized
|
|
366
|
+
return standardized.absoluteString.hasPrefix(parentDirectoryURL.absoluteString)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## ZipFoundation
|
|
2
|
+
|
|
3
|
+
This directory includes the source code of the ZIPFoundation library, from the following release:
|
|
4
|
+
https://github.com/weichsel/ZIPFoundation/releases/tag/0.9.16
|
|
5
|
+
|
|
6
|
+
Lottie is distributed via multiple package managers (SPM, Cocoapods, Carthage, and NPM),
|
|
7
|
+
each with different packaging and compilation requirements.
|
|
8
|
+
|
|
9
|
+
Due to limitations of these package managers, we can't depend on / import
|
|
10
|
+
a separate ZIPFoundation module / library. Instead, we include the source
|
|
11
|
+
directly within the Lottie library and compile everything as a single unit.
|
|
12
|
+
|
|
13
|
+
### Update instructions
|
|
14
|
+
|
|
15
|
+
From time to time we may need to update to a more recent version of ZIPFoundation.
|
|
16
|
+
When doing this, follow these steps:
|
|
17
|
+
|
|
18
|
+
1. Download the latest release from https://github.com/weichsel/ZIPFoundation
|
|
19
|
+
and replace the source code in this directory with the updated code.
|
|
20
|
+
|
|
21
|
+
2. Update the URL at the top of this file to indicate what release is being used.
|
|
22
|
+
|
|
23
|
+
3. Change all of the `public` symbols defined in this module to instead be `internal`
|
|
24
|
+
to prevent Lottie from exposing any ZIPFoundation APIs.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// URL+ZIP.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension URL {
|
|
14
|
+
|
|
15
|
+
static func temporaryReplacementDirectoryURL(for archive: Archive) -> URL {
|
|
16
|
+
#if swift(>=5.0) || os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
17
|
+
if
|
|
18
|
+
archive.url.isFileURL,
|
|
19
|
+
let tempDir = try? FileManager().url(
|
|
20
|
+
for: .itemReplacementDirectory,
|
|
21
|
+
in: .userDomainMask,
|
|
22
|
+
appropriateFor: archive.url,
|
|
23
|
+
create: true)
|
|
24
|
+
{
|
|
25
|
+
return tempDir
|
|
26
|
+
}
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
return URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(
|
|
30
|
+
ProcessInfo.processInfo.globallyUniqueString)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -4,36 +4,27 @@ import UIKit
|
|
|
4
4
|
#endif
|
|
5
5
|
|
|
6
6
|
extension Bundle {
|
|
7
|
-
func getAnimationData(_ name: String, subdirectory: String? = nil) throws -> Data
|
|
7
|
+
func getAnimationData(_ name: String, subdirectory: String? = nil) throws -> Data {
|
|
8
8
|
// Check for files in the bundle at the given path
|
|
9
9
|
let name = name.removingJSONSuffix()
|
|
10
10
|
if let url = url(forResource: name, withExtension: "json", subdirectory: subdirectory) {
|
|
11
11
|
return try Data(contentsOf: url)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Check for data assets
|
|
15
|
-
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
|
|
14
|
+
// Check for data assets
|
|
16
15
|
let assetKey = subdirectory != nil ? "\(subdirectory ?? "")/\(name)" : name
|
|
17
|
-
return
|
|
18
|
-
#else
|
|
19
|
-
return nil
|
|
20
|
-
#endif
|
|
16
|
+
return try Data(assetName: assetKey, in: self)
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
func dotLottieData(_ name: String, subdirectory: String? = nil) throws -> Data
|
|
19
|
+
func dotLottieData(_ name: String, subdirectory: String? = nil) throws -> Data {
|
|
24
20
|
// Check for files in the bundle at the given path
|
|
25
21
|
let name = name.removingDotLottieSuffix()
|
|
26
22
|
if let url = url(forResource: name, withExtension: "lottie", subdirectory: subdirectory) {
|
|
27
23
|
return try Data(contentsOf: url)
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
// Check for data assets (not available on macOS)
|
|
31
|
-
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
|
|
32
26
|
let assetKey = subdirectory != nil ? "\(subdirectory ?? "")/\(name)" : name
|
|
33
|
-
return
|
|
34
|
-
#else
|
|
35
|
-
return nil
|
|
36
|
-
#endif
|
|
27
|
+
return try Data(assetName: assetKey, in: self)
|
|
37
28
|
}
|
|
38
29
|
}
|
|
39
30
|
|