From 6633c7e8cb7fcc3d127b202805da1f98ca6b581d Mon Sep 17 00:00:00 2001 From: Evandro Date: Tue, 22 Jun 2021 22:20:52 +0800 Subject: [PATCH] [FEAT] Adds ability to create dotLottie files from JSON --- Example/dotLottieLoader/ViewController.swift | 15 +++++++ README.md | 10 ++++- Sources/dotLottieLoader/DotLottieFile.swift | 40 +++++++++++++++++++ Sources/dotLottieLoader/DotLottieLoader.swift | 22 ++++++++++ .../dotLottieLoader/DotLottieManifest.swift | 15 +++++-- dotLottieLoader.podspec | 2 +- 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/Example/dotLottieLoader/ViewController.swift b/Example/dotLottieLoader/ViewController.swift index d4d48ee..09790e4 100644 --- a/Example/dotLottieLoader/ViewController.swift +++ b/Example/dotLottieLoader/ViewController.swift @@ -7,12 +7,27 @@ // import UIKit +import dotLottieLoader class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. + + DotLottieUtils.isLogEnabled = true + DotLottieLoader.dotLottie(fromJsonLottieAt: URL(string: "https://assets7.lottiefiles.com/packages/lf20_6k4jsmai.json")!) { url in + // file compressed into dotLottie + guard let url = url else { return } + DotLottieLoader.load(from: url) { dotLottieFile in + // file decompressed from dotLottie + guard let dotLottieFile = dotLottieFile else { + print("invalid dotLottie file") + return + } + print("dotLottieFile decompressed successfuly with \(dotLottieFile.animations.count) animation\(dotLottieFile.animations.count == 1 ? "" : "s")") + } + } } override func didReceiveMemoryWarning() { diff --git a/README.md b/README.md index a10d3b9..8bc7f0d 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ pod 'dotLottieLoader' ### Swift Package Manager ```swift -.package(url: "https://github.com/dotlottie/dotLottieLoader-ios.git", from: "0.1.3") +.package(url: "https://github.com/dotlottie/dotLottieLoader-ios.git", from: "0.1.4") ``` ## Using dotLottie @@ -74,6 +74,14 @@ DotLottieLoader.load(from: URL(string:"https://dotlottie.io/sample_files/animati } ``` +##### Creating .lottie file from JSON animation file + +```swift +DotLottieLoader.dotLottie(fromJsonLottieAt: URL(string: "https://assets7.lottiefiles.com/packages/lf20_6k4jsmai.json")!) { dotLottieFileUrl in + // share or play `dotLottieFileUrl` using [DotLottie library](https://github.com/dotlottie/dotlottie-ios) +} +``` + ## Author [Evandro Harrison Hoffmann](https://github.com/eharrison) | evandro.hoffmann@gmail.com diff --git a/Sources/dotLottieLoader/DotLottieFile.swift b/Sources/dotLottieLoader/DotLottieFile.swift index dde24ef..2894d63 100644 --- a/Sources/dotLottieLoader/DotLottieFile.swift +++ b/Sources/dotLottieLoader/DotLottieFile.swift @@ -89,4 +89,44 @@ public struct DotLottieFile { } } + /// Creates dotLottieFile from animation json + /// - Parameters: + /// - url: url of JSON lottie animation + /// - directory: directory to save file + /// - loop: loop enabled + /// - themeColor: theme color + /// - Returns: URL of .lottie file + static func compress(jsonLottieAt url: URL, in directory: URL = DotLottieUtils.tempDirectoryURL, loop: Bool = true, themeColor: String = "#ffffff") -> URL? { + Zip.addCustomFileExtension(DotLottieUtils.dotLottieExtension) + + do { + let fileName = url.deletingPathExtension().lastPathComponent + let dotLottieDirectory = directory.appendingPathComponent(fileName) + try FileManager.default.createDirectory(at: dotLottieDirectory, withIntermediateDirectories: true, attributes: nil) + + let animationsDirectory = dotLottieDirectory.appendingPathComponent("animations") + try FileManager.default.createDirectory(at: animationsDirectory, withIntermediateDirectories: true, attributes: nil) + + let animationData = try Data(contentsOf: url) + try animationData.write(to: animationsDirectory.appendingPathComponent(fileName).appendingPathExtension("json")) + + let manifest = DotLottieManifest(animations: [ + DotLottieAnimation(loop: loop, themeColor: themeColor, speed: 1.0, id: fileName) + ], version: "1.0", author: "LottieFiles", generator: "LottieFiles dotLottieLoader-iOS 0.1.4") + let manifestUrl = dotLottieDirectory.appendingPathComponent("manifest").appendingPathExtension("json") + let manifestData = try manifest.encode() + try manifestData.write(to: manifestUrl) + + let dotLottieUrl = directory.appendingPathComponent(fileName).appendingPathExtension("lottie") + try Zip.zipFiles(paths: [animationsDirectory, manifestUrl], zipFilePath: dotLottieUrl, password: nil, compression: .DefaultCompression, progress: { progress in + DotLottieUtils.log("Compressing dotLottie file: \(progress)") + }) + + return dotLottieUrl + } catch { + DotLottieUtils.log("Extraction of dotLottie archive failed with error: \(error)") + return nil + } + } + } diff --git a/Sources/dotLottieLoader/DotLottieLoader.swift b/Sources/dotLottieLoader/DotLottieLoader.swift index bf7a3af..ac4ceee 100644 --- a/Sources/dotLottieLoader/DotLottieLoader.swift +++ b/Sources/dotLottieLoader/DotLottieLoader.swift @@ -81,4 +81,26 @@ public class DotLottieLoader { } }).resume() } + + /// Creates .lottie file from a json animation + /// - Parameters: + /// - jsonUrl: URL to JSON lottie animation + /// - loop: loop enabled + /// - themeColor: theme color in HEX + /// - completion: URL to .lottie file + public static func dotLottie(fromJsonLottieAt jsonUrl: URL, loop: Bool = true, themeColor: String = "#ffffff", completion: @escaping (URL?) -> Void) { + guard jsonUrl.isJsonFile else { + DotLottieUtils.log("Not a json file") + return + } + + guard let dotLottieUrl = DotLottieFile.compress(jsonLottieAt: jsonUrl, loop: loop, themeColor: themeColor) else { + DotLottieUtils.log("Failed to create dotLottie file") + completion(nil) + return + } + + DotLottieUtils.log("Created dotLottie file at \(dotLottieUrl.absoluteString)") + completion(dotLottieUrl) + } } diff --git a/Sources/dotLottieLoader/DotLottieManifest.swift b/Sources/dotLottieLoader/DotLottieManifest.swift index cde3a87..224653c 100644 --- a/Sources/dotLottieLoader/DotLottieManifest.swift +++ b/Sources/dotLottieLoader/DotLottieManifest.swift @@ -9,7 +9,7 @@ import Foundation /// Manifest model for .lottie File -public struct DotLottieManifest: Decodable { +public struct DotLottieManifest: Codable { public var animations: [DotLottieAnimation] public var version: String public var author: String @@ -20,7 +20,15 @@ public struct DotLottieManifest: Decodable { /// - Throws: Error /// - Returns: .lottie Manifest model public static func decode(from data: Data) throws -> DotLottieManifest? { - return try? JSONDecoder().decode(DotLottieManifest.self, from: data) + try? JSONDecoder().decode(DotLottieManifest.self, from: data) + } + + /// Encodes to data + /// - Parameter encoder: JSONEncoder + /// - Throws: Error + /// - Returns: encoded Data + func encode(with encoder: JSONEncoder = JSONEncoder()) throws -> Data { + try encoder.encode(self) } /// Loads manifest from given URL @@ -30,10 +38,11 @@ public struct DotLottieManifest: Decodable { guard let data = try? Data(contentsOf: url) else { return nil } return try? decode(from: data) } + } /// Animation model for .lottie File -public struct DotLottieAnimation: Decodable { +public struct DotLottieAnimation: Codable { public var loop: Bool public var themeColor: String public var speed: Float diff --git a/dotLottieLoader.podspec b/dotLottieLoader.podspec index d069bd4..7bca0b9 100644 --- a/dotLottieLoader.podspec +++ b/dotLottieLoader.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'dotLottieLoader' - s.version = '0.1.3' + s.version = '0.1.4' s.summary = 'An iOS library to natively load .lottie files https://dotlottie.io/' s.description = <<-DESC