mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
Add logging to rive-ios
Adds (structured) logging via the iOS `Logger` API. Under the hood, this uses `os_log` for both in-memory and on-disk logging, depending on which level is used; this means _where_ is handled by the system, we just provide the level. The API is a little interesting; you can't have a "generic" `log` function that takes in the message. iOS requires that you use interpolation when logging. Logging is structured so that various categories are set under one subsystem. These categories are: view model, state machine, artboard, file, and view. Each of these can log one of debug, info, default, error, and fault levels. The developer can filter which categories and levels can be logged; Xcode also supports filtering within the console. Logging itself is split into three things: the categories, the levels, and the logging. Within each "category" of logging, there exist events that can be logged. These are enums with associated values. When using Objective-C, there are helper functions that under-the-hood call logging functions with these events. Since there are a few categories, and various events for each category, these categories are split into extensions on `RiveLogger`. At the end of the day, there exists a single log function, which ensures a log category and level are available for logging, and then a log function that is essentially a switch statement on each event, logging the (interpolated) message. These logging events are then utilized in the files mentioned above; the categories match the files where logging has been added. Primarily setters are called, or errors that may not be handled, but may be useful. Fatal errors are also logged. When adding new logging: 1. Check if an existing extension exists. If not, create one. 2. Create an enum of possible events. 3. Create a `log` function that takes the model, and the event. 4. Create a `_log` function that verifies that an event has been called. ## Example usage ```swift // Somewhere early in the app lifecycle… RiveLogger.isEnabled = true RiveLogger.isVerbose = true // advances are considered verbose RiveLogger.levels = [.fatal] // filter for only specific levels, such as fatal errors RiveLogger.categories = [.stateMachine, .viewModel] // filter for only specific categories ``` Diffs= e1fc239974 Add logging to rive-ios (#8252) Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
||||
9d5076b88363c7811a9a70107ee43013b8bfd0cb
|
||||
e1fc239974df492517f74f30a456f765d4bea96c
|
||||
|
||||
@@ -50,6 +50,13 @@
|
||||
ReferencedContainer = "container:RiveExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "IDEPreferLogStreaming"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -15,6 +15,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
RenderContextManager.shared().defaultRenderer = RendererType.riveRenderer
|
||||
RiveLogger.isEnabled = true
|
||||
RiveLogger.categories = [.viewModel, .model]
|
||||
RiveLogger.levels = [.debug]
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,15 @@
|
||||
E599DCFA2AAFA06100D1E49A /* rating_animation.riv in Resources */ = {isa = PBXBuildFile; fileRef = E599DCF82AAFA06100D1E49A /* rating_animation.riv */; };
|
||||
F21F08142C66526D00FFA205 /* RiveFallbackFontDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21F08132C66526D00FFA205 /* RiveFallbackFontDescriptor.swift */; };
|
||||
F23626AA2C8F90FA00727D9A /* nested_text_run.riv in Resources */ = {isa = PBXBuildFile; fileRef = F23626A92C8F90FA00727D9A /* nested_text_run.riv */; };
|
||||
F2610DD22CA5B4C40090D50B /* RiveLogger+StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD12CA5B4C40090D50B /* RiveLogger+StateMachine.swift */; };
|
||||
F2610DD42CA5B5460090D50B /* RiveLogger+Artboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD32CA5B5460090D50B /* RiveLogger+Artboard.swift */; };
|
||||
F2610DD62CA5B5DD0090D50B /* RiveLogger+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD52CA5B5DD0090D50B /* RiveLogger+ViewModel.swift */; };
|
||||
F2610DD82CA5B6570090D50B /* RiveLogger+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD72CA5B6570090D50B /* RiveLogger+Model.swift */; };
|
||||
F2610DDA2CA5B84B0090D50B /* RiveLogger+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DD92CA5B84B0090D50B /* RiveLogger+File.swift */; };
|
||||
F2610DE12CA5FBE30090D50B /* RiveLogger+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2610DE02CA5FBE30090D50B /* RiveLogger+View.swift */; };
|
||||
F28DE4532C5002D900F3C379 /* RiveModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28DE4522C5002D900F3C379 /* RiveModelTests.swift */; };
|
||||
F2CCA9792C9B2799007DC0D2 /* referenced_image_asset.riv in Resources */ = {isa = PBXBuildFile; fileRef = F2CCA9782C9B2799007DC0D2 /* referenced_image_asset.riv */; };
|
||||
F2CCA9C22C9E13BA007DC0D2 /* RiveLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2CCA9C12C9E13BA007DC0D2 /* RiveLogger.swift */; };
|
||||
F2D285492C6D469900728340 /* RiveFallbackFontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D285482C6D469900728340 /* RiveFallbackFontProvider.swift */; };
|
||||
F2ECC2312C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2ECC2302C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift */; };
|
||||
F2ECC23A2C66B949008B20E5 /* RiveFontTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2ECC2382C66B920008B20E5 /* RiveFontTests.swift */; };
|
||||
@@ -203,8 +210,15 @@
|
||||
E599DCF82AAFA06100D1E49A /* rating_animation.riv */ = {isa = PBXFileReference; lastKnownFileType = file; name = rating_animation.riv; path = "Example-iOS/Assets/rating_animation.riv"; sourceTree = SOURCE_ROOT; };
|
||||
F21F08132C66526D00FFA205 /* RiveFallbackFontDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFallbackFontDescriptor.swift; sourceTree = "<group>"; };
|
||||
F23626A92C8F90FA00727D9A /* nested_text_run.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = nested_text_run.riv; sourceTree = "<group>"; };
|
||||
F2610DD12CA5B4C40090D50B /* RiveLogger+StateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+StateMachine.swift"; sourceTree = "<group>"; };
|
||||
F2610DD32CA5B5460090D50B /* RiveLogger+Artboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+Artboard.swift"; sourceTree = "<group>"; };
|
||||
F2610DD52CA5B5DD0090D50B /* RiveLogger+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+ViewModel.swift"; sourceTree = "<group>"; };
|
||||
F2610DD72CA5B6570090D50B /* RiveLogger+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+Model.swift"; sourceTree = "<group>"; };
|
||||
F2610DD92CA5B84B0090D50B /* RiveLogger+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+File.swift"; sourceTree = "<group>"; };
|
||||
F2610DE02CA5FBE30090D50B /* RiveLogger+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveLogger+View.swift"; sourceTree = "<group>"; };
|
||||
F28DE4522C5002D900F3C379 /* RiveModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveModelTests.swift; sourceTree = "<group>"; };
|
||||
F2CCA9782C9B2799007DC0D2 /* referenced_image_asset.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = referenced_image_asset.riv; sourceTree = "<group>"; };
|
||||
F2CCA9C12C9E13BA007DC0D2 /* RiveLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveLogger.swift; sourceTree = "<group>"; };
|
||||
F2D285482C6D469900728340 /* RiveFallbackFontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFallbackFontProvider.swift; sourceTree = "<group>"; };
|
||||
F2ECC2302C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RiveFallbackFontDescriptor+Extensions.swift"; sourceTree = "<group>"; };
|
||||
F2ECC2382C66B920008B20E5 /* RiveFontTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFontTests.swift; sourceTree = "<group>"; };
|
||||
@@ -370,6 +384,7 @@
|
||||
C9C73ED324FC478800EF9516 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F2CCA9C02C9E13B2007DC0D2 /* Logging */,
|
||||
C3468E5727EB9887008652FD /* RiveView.swift */,
|
||||
C3468E5927ECC7C6008652FD /* RiveViewModel.swift */,
|
||||
C3468E5B27ED4C41008652FD /* RiveModel.swift */,
|
||||
@@ -416,6 +431,20 @@
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F2CCA9C02C9E13B2007DC0D2 /* Logging */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F2CCA9C12C9E13BA007DC0D2 /* RiveLogger.swift */,
|
||||
F2610DD52CA5B5DD0090D50B /* RiveLogger+ViewModel.swift */,
|
||||
F2610DD12CA5B4C40090D50B /* RiveLogger+StateMachine.swift */,
|
||||
F2610DD32CA5B5460090D50B /* RiveLogger+Artboard.swift */,
|
||||
F2610DD72CA5B6570090D50B /* RiveLogger+Model.swift */,
|
||||
F2610DD92CA5B84B0090D50B /* RiveLogger+File.swift */,
|
||||
F2610DE02CA5FBE30090D50B /* RiveLogger+View.swift */,
|
||||
);
|
||||
path = Logging;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -581,17 +610,22 @@
|
||||
2A707937272628AD00C035A1 /* rive_renderer_view.mm in Sources */,
|
||||
C34609FC27FF9114002DBCB7 /* RiveFile+Extensions.swift in Sources */,
|
||||
C3468E5827EB9887008652FD /* RiveView.swift in Sources */,
|
||||
F2610DD82CA5B6570090D50B /* RiveLogger+Model.swift in Sources */,
|
||||
F2610DD62CA5B5DD0090D50B /* RiveLogger+ViewModel.swift in Sources */,
|
||||
E5964A982A9697B600140479 /* RiveEvent.mm in Sources */,
|
||||
F21F08142C66526D00FFA205 /* RiveFallbackFontDescriptor.swift in Sources */,
|
||||
F2CCA9C22C9E13BA007DC0D2 /* RiveLogger.swift in Sources */,
|
||||
043025F82AFA46EF00320F2E /* CDNFileAssetLoader.mm in Sources */,
|
||||
043026042AFBA04100320F2E /* RiveFactory.mm in Sources */,
|
||||
04BE5434264D267900427B39 /* LayerState.mm in Sources */,
|
||||
F2610DDA2CA5B84B0090D50B /* RiveLogger+File.swift in Sources */,
|
||||
C9601F2B250C25930032AA07 /* CoreGraphicsRenderer.mm in Sources */,
|
||||
C9601F2B250C25930032AA07 /* CoreGraphicsRenderer.mm in Sources */,
|
||||
043025F42AF90EAC00320F2E /* RiveFileAssetLoader.mm in Sources */,
|
||||
F2D285492C6D469900728340 /* RiveFallbackFontProvider.swift in Sources */,
|
||||
043025FC2AFA862E00320F2E /* FileAssetLoaderAdapter.mm in Sources */,
|
||||
83DE4C912AA8DD7B00B88B72 /* RenderContextManager.mm in Sources */,
|
||||
F2610DE12CA5FBE30090D50B /* RiveLogger+View.swift in Sources */,
|
||||
C3E2B580282F242400A8651B /* RiveStateMachineInstance+Extensions.swift in Sources */,
|
||||
046FB7F5264EAA60000129B1 /* RiveSMIInput.mm in Sources */,
|
||||
043026002AFA915B00320F2E /* RiveFileAsset.mm in Sources */,
|
||||
@@ -599,10 +633,12 @@
|
||||
C3745FD3282BFAB90087F4AF /* FPSCounterView.swift in Sources */,
|
||||
F2ECC2312C666824008B20E5 /* RiveFallbackFontDescriptor+Extensions.swift in Sources */,
|
||||
C9C741F524FC510200EF9516 /* Rive.mm in Sources */,
|
||||
F2610DD42CA5B5460090D50B /* RiveLogger+Artboard.swift in Sources */,
|
||||
046FB7F8264EAA60000129B1 /* RiveStateMachineInstance.mm in Sources */,
|
||||
C3468E5C27ED4C41008652FD /* RiveModel.swift in Sources */,
|
||||
046FB7FF264EAA61000129B1 /* RiveFile.mm in Sources */,
|
||||
046FB7F2264EAA60000129B1 /* RiveArtboard.mm in Sources */,
|
||||
F2610DD22CA5B4C40090D50B /* RiveLogger+StateMachine.swift in Sources */,
|
||||
046FB7F4264EAA60000129B1 /* RiveLinearAnimationInstance.mm in Sources */,
|
||||
E57798A62A72C9C500FF25C3 /* RiveTextValueRun.mm in Sources */,
|
||||
);
|
||||
|
||||
54
Source/Logging/RiveLogger+Artboard.swift
Normal file
54
Source/Logging/RiveLogger+Artboard.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// RiveLogger+Artboard.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerArtboardEvent {
|
||||
case advance(Double)
|
||||
case error(String)
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let artboard = Logger(subsystem: subsystem, category: "rive-artboard")
|
||||
|
||||
@objc(logArtboard:advance:) static func log(artboard: RiveArtboard, advance: Double) {
|
||||
log(artboard: artboard, event: .advance(advance))
|
||||
}
|
||||
|
||||
@objc(logArtboard:error:) static func log(artboard: RiveArtboard, error: String) {
|
||||
log(artboard: artboard, event: .error(error))
|
||||
}
|
||||
|
||||
static func log(artboard: RiveArtboard, event: RiveLoggerArtboardEvent) {
|
||||
switch event {
|
||||
case .advance(let elapsed):
|
||||
guard isVerbose else { return }
|
||||
_log(event: event, level: .debug) {
|
||||
Self.artboard.debug("\(self.prefix(for: artboard))Advanced by \(elapsed)s")
|
||||
}
|
||||
case .error(let error):
|
||||
_log(event: event, level: .error) {
|
||||
Self.artboard.error("\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func _log(event: RiveLoggerArtboardEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.artboard),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
|
||||
private static func prefix(for artboard: RiveArtboard) -> String {
|
||||
return "\(artboard.name()): "
|
||||
}
|
||||
}
|
||||
99
Source/Logging/RiveLogger+File.swift
Normal file
99
Source/Logging/RiveLogger+File.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// RiveLogger+File.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerFileEvent {
|
||||
case fatalError(String)
|
||||
case error(String)
|
||||
case loadingAsset(RiveFileAsset)
|
||||
case loadedFontAssetFromURL(URL, RiveFontAsset)
|
||||
case loadedImageAssetFromURL(URL, RiveImageAsset)
|
||||
case loadedAsset(RiveFileAsset)
|
||||
case loadedFromURL(URL)
|
||||
case loadingFromResource(String)
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let file = Logger(subsystem: subsystem, category: "rive-file")
|
||||
|
||||
@objc(logFile:error:) static func log(file: RiveFile?, error message: String) {
|
||||
log(file: file, event: .error(message))
|
||||
}
|
||||
|
||||
@objc(logLoadingAsset:) static func log(loadingAsset asset: RiveFileAsset) {
|
||||
log(file: nil, event: .loadingAsset(asset))
|
||||
}
|
||||
|
||||
@objc(logFontAssetLoad:fromURL:) static func log(fontAssetLoad fontAsset: RiveFontAsset, from url: URL) {
|
||||
log(file: nil, event: .loadedFontAssetFromURL(url, fontAsset))
|
||||
}
|
||||
|
||||
@objc(logImageAssetLoad:fromURL:) static func log(imageAssetLoad imageAsset: RiveImageAsset, from url: URL) {
|
||||
log(file: nil, event: .loadedImageAssetFromURL(url, imageAsset))
|
||||
}
|
||||
|
||||
@objc(logAssetLoaded:) static func log(assetLoaded asset: RiveFileAsset) {
|
||||
log(file: nil, event: .loadedAsset(asset))
|
||||
}
|
||||
|
||||
@objc(logLoadedFromURL:) static func log(loadedFromURL url: URL) {
|
||||
log(file: nil, event: .loadedFromURL(url))
|
||||
}
|
||||
|
||||
@objc(logLoadingFromResource:) static func log(loadingFromResource name: String) {
|
||||
log(file: nil, event: .loadingFromResource(name))
|
||||
}
|
||||
|
||||
static func log(file: RiveFile?, event: RiveLoggerFileEvent) {
|
||||
switch event {
|
||||
case .fatalError(let message):
|
||||
_log(event: event, level: .fault) {
|
||||
Self.file.fault("\(message)")
|
||||
}
|
||||
case .error(let message):
|
||||
_log(event: event, level: .error) {
|
||||
Self.file.error("\(message)")
|
||||
}
|
||||
case .loadingAsset(let asset):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loading asset \(asset.name())")
|
||||
}
|
||||
case .loadedAsset(let asset):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loaded asset \(asset.name())")
|
||||
}
|
||||
case .loadedFontAssetFromURL(let url, let asset):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loaded font asset \(asset.name()) from URL: \(url)")
|
||||
}
|
||||
case .loadedImageAssetFromURL(let url, let asset):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loaded image asset \(asset.name()) from URL: \(url)")
|
||||
}
|
||||
case .loadedFromURL(let url):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loaded file \(url)")
|
||||
}
|
||||
case .loadingFromResource(let name):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.file.debug("Loading resource \(name)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func _log(event: RiveLoggerFileEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.file),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
}
|
||||
112
Source/Logging/RiveLogger+Model.swift
Normal file
112
Source/Logging/RiveLogger+Model.swift
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// RiveLogger+Model.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerModelEvent {
|
||||
case volume(Float)
|
||||
case artboardByName(String)
|
||||
case artboardByIndex(Int)
|
||||
case defaultArtboard
|
||||
case error(String)
|
||||
case stateMachineByName(String)
|
||||
case stateMachineByIndex(Int)
|
||||
case defaultStateMachine
|
||||
case animationByName(String)
|
||||
case animationByIndex(Int)
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let model = Logger(subsystem: subsystem, category: "rive-model")
|
||||
|
||||
static func log(model: RiveModel, event: RiveLoggerModelEvent) {
|
||||
switch event {
|
||||
case .volume(let volume):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Volume set to \(volume)"
|
||||
)
|
||||
}
|
||||
case .artboardByName(let name):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Artboard set to artboard named \(name)"
|
||||
)
|
||||
}
|
||||
case .artboardByIndex(let index):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Artboard set to artboard at index \(index)"
|
||||
)
|
||||
}
|
||||
case .defaultArtboard:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Artboard set to default artboard"
|
||||
)
|
||||
}
|
||||
case .error(let message):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))\(message)"
|
||||
)
|
||||
}
|
||||
case .stateMachineByName(let name):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))State machine set to state machine named \(name)"
|
||||
)
|
||||
|
||||
}
|
||||
case .stateMachineByIndex(let index):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))State machine set to state machine at index \(index)"
|
||||
)
|
||||
}
|
||||
case .defaultStateMachine:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))State machine set to default state machine"
|
||||
)
|
||||
}
|
||||
case .animationByName(let name):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Animation set to animation named \(name)"
|
||||
)
|
||||
}
|
||||
case .animationByIndex(let index):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.model.debug(
|
||||
"\(self.prefix(for: model))Animation set to animation at index \(index)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func _log(event: RiveLoggerModelEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.model),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
|
||||
private static func prefix(for model: RiveModel) -> String {
|
||||
if let stateMachine = model.stateMachine {
|
||||
return "[\(stateMachine.name())]: "
|
||||
} else if let animation = model.animation {
|
||||
return "[\(animation.name())]: "
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Source/Logging/RiveLogger+StateMachine.swift
Normal file
59
Source/Logging/RiveLogger+StateMachine.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// RiveLogger+StateMachine.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerStateMachineEvent {
|
||||
case advance(Double)
|
||||
case eventReceived(RiveEvent)
|
||||
case error(String)
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let stateMachine = Logger(subsystem: subsystem, category: "rive-state-machine")
|
||||
|
||||
@objc(logStateMachine:advance:) static func log(stateMachine: RiveStateMachineInstance, advance: Double) {
|
||||
log(stateMachine: stateMachine, event: .advance(advance))
|
||||
}
|
||||
|
||||
@objc(logStateMachine:error:) static func log(stateMachine: RiveStateMachineInstance, error: String) {
|
||||
log(stateMachine: stateMachine, event: .error(error))
|
||||
}
|
||||
|
||||
static func log(stateMachine: RiveStateMachineInstance, event: RiveLoggerStateMachineEvent) {
|
||||
switch event {
|
||||
case .advance(let elapsed):
|
||||
guard isVerbose else { return }
|
||||
_log(event: event, level: .debug) {
|
||||
Self.stateMachine.debug("\(self.prefix(for: stateMachine))Advancing by \(elapsed)s")
|
||||
}
|
||||
case .eventReceived(let receivedEvent):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.stateMachine.debug("\(self.prefix(for: stateMachine))Received event \(receivedEvent.name())")
|
||||
}
|
||||
case .error(let error):
|
||||
_log(event: event, level: .error) {
|
||||
Self.stateMachine.error("\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func _log(event: RiveLoggerStateMachineEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.stateMachine),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
|
||||
private static func prefix(for stateMachine: RiveStateMachineInstance) -> String {
|
||||
return "\(stateMachine.name()): "
|
||||
}
|
||||
}
|
||||
99
Source/Logging/RiveLogger+View.swift
Normal file
99
Source/Logging/RiveLogger+View.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// RiveLogger+View.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerViewEvent {
|
||||
case touchBegan(CGPoint)
|
||||
case touchMoved(CGPoint)
|
||||
case touchEnded(CGPoint)
|
||||
case touchCancelled(CGPoint)
|
||||
case play
|
||||
case pause
|
||||
case stop
|
||||
case reset
|
||||
case advance(Double)
|
||||
case eventReceived(String)
|
||||
case drawing(CGSize)
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let view = Logger(subsystem: subsystem, category: "rive-view")
|
||||
|
||||
static func log(view: RiveView, event: RiveLoggerViewEvent) {
|
||||
switch event {
|
||||
case .touchBegan(let point):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Touch began at {\(point.x),\(point.y)}")
|
||||
}
|
||||
case .touchMoved(let point):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Touch moved to {\(point.x),\(point.y)}")
|
||||
}
|
||||
case .touchEnded(let point):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Touch ended at {\(point.x),\(point.y)}")
|
||||
}
|
||||
case .touchCancelled(let point):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Touch cancelled at {\(point.x),\(point.y)}")
|
||||
}
|
||||
case .play:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Playing")
|
||||
}
|
||||
case .pause:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Paused")
|
||||
}
|
||||
case .stop:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Stopped")
|
||||
}
|
||||
case .reset:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Reset")
|
||||
}
|
||||
case .advance(let elapsed):
|
||||
guard isVerbose else { return }
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Advancing by \(elapsed)s")
|
||||
}
|
||||
case .eventReceived(let name):
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Received event \(name)")
|
||||
}
|
||||
case .drawing(let size):
|
||||
guard isVerbose else { return }
|
||||
_log(event: event, level: .debug) {
|
||||
Self.view.debug("\(self.prefix(for: view))Drawing size {\(size.width),\(size.height)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func _log(event: RiveLoggerViewEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.view),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
|
||||
private static func prefix(for view: RiveView) -> String {
|
||||
if let stateMachine = view.riveModel?.stateMachine {
|
||||
return "[\(stateMachine.name())]: "
|
||||
} else if let animation = view.riveModel?.animation {
|
||||
return "[\(animation.name())]: "
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
Source/Logging/RiveLogger+ViewModel.swift
Normal file
111
Source/Logging/RiveLogger+ViewModel.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// RiveLogger+ViewModel.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/26/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
enum RiveLoggerViewModelEvent {
|
||||
case booleanInput(String, String?, Bool)
|
||||
case floatInput(String, String?, Float)
|
||||
case doubleInput(String, String?, Double)
|
||||
case triggerInput(String, String?)
|
||||
case textRun(String, String?, String)
|
||||
case error(String)
|
||||
case fatalError(String)
|
||||
case play
|
||||
case pause
|
||||
case stop
|
||||
case reset
|
||||
}
|
||||
|
||||
extension RiveLogger {
|
||||
private static let viewModel = Logger(subsystem: subsystem, category: "rive-view-model")
|
||||
|
||||
static func log(viewModel: RiveViewModel, event: RiveLoggerViewModelEvent) {
|
||||
switch event {
|
||||
case .booleanInput(let name, let path, let value):
|
||||
_log(event: event, level: .debug) {
|
||||
let atPath = path != nil ? " at path \(path!) " : " "
|
||||
Self.viewModel.debug(
|
||||
"\(self.prefix(for: viewModel))Input \(name)\(atPath)set to \(value)"
|
||||
)
|
||||
}
|
||||
case .floatInput(let name, let path, let value):
|
||||
_log(event: event, level: .debug) {
|
||||
let atPath = path != nil ? " at path \(path!) " : " "
|
||||
Self.viewModel.debug(
|
||||
"\(self.prefix(for: viewModel))Input \(name)\(atPath)set to \(value)"
|
||||
)
|
||||
}
|
||||
case .doubleInput(let name, let path, let value):
|
||||
_log(event: event, level: .debug) {
|
||||
let atPath = path != nil ? " at path \(path!) " : " "
|
||||
Self.viewModel.debug(
|
||||
"\(self.prefix(for: viewModel))Input \(name)\(atPath)set to \(value)"
|
||||
)
|
||||
}
|
||||
case .triggerInput(let name, let path):
|
||||
_log(event: event, level: .debug) {
|
||||
let atPath = path != nil ? " at path: \(path!) " : " "
|
||||
Self.viewModel.debug(
|
||||
"\(self.prefix(for: viewModel))Input \(name)\(atPath) triggered"
|
||||
)
|
||||
}
|
||||
case .textRun(let name, let path, let value):
|
||||
_log(event: event, level: .debug) {
|
||||
let atPath = path != nil ? " at path \(path!) " : " "
|
||||
Self.viewModel.debug(
|
||||
"\(self.prefix(for: viewModel))Text run \(name)\(atPath)set to \(value)"
|
||||
)
|
||||
}
|
||||
case .error(let message):
|
||||
_log(event: event, level: .error) {
|
||||
Self.viewModel.error("\(self.prefix(for: viewModel))\(message)")
|
||||
}
|
||||
case .fatalError(let message):
|
||||
_log(event: event, level: .fault) {
|
||||
Self.viewModel.fault("\(self.prefix(for: viewModel))\(message)")
|
||||
}
|
||||
case .play:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.viewModel.debug("\(self.prefix(for: viewModel))Playing")
|
||||
}
|
||||
case .pause:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.viewModel.debug("\(self.prefix(for: viewModel))Paused")
|
||||
}
|
||||
case .stop:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.viewModel.debug("\(self.prefix(for: viewModel))Stopped")
|
||||
}
|
||||
case .reset:
|
||||
_log(event: event, level: .debug) {
|
||||
Self.viewModel.debug("\(self.prefix(for: viewModel))Reset")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private func _log(event: RiveLoggerViewModelEvent, level: RiveLogLevel, log: () -> Void) {
|
||||
guard isEnabled,
|
||||
categories.contains(.viewModel),
|
||||
levels.contains(level)
|
||||
else { return }
|
||||
|
||||
log()
|
||||
}
|
||||
|
||||
private static func prefix(for viewModel: RiveViewModel) -> String {
|
||||
if let stateMachine = viewModel.riveModel?.stateMachine {
|
||||
return "[\(stateMachine.name())]: "
|
||||
} else if let animation = viewModel.riveModel?.animation {
|
||||
return "[\(animation.name())]: "
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Source/Logging/RiveLogger.swift
Normal file
99
Source/Logging/RiveLogger.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// RiveLogger.swift
|
||||
// RiveRuntime
|
||||
//
|
||||
// Created by David Skuza on 9/20/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
// MARK: - RiveLogLevel
|
||||
|
||||
/// An option set of possible log levels, checked when attempting to log.
|
||||
@objc public class RiveLogLevel: NSObject, OptionSet {
|
||||
public var rawValue: Int
|
||||
|
||||
public required init(rawValue: Int) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
/// A log level that captures debug information
|
||||
@objc public static let debug = RiveLogLevel(rawValue: 1 << 0)
|
||||
/// A log level that captures additional information.
|
||||
@objc public static let info = RiveLogLevel(rawValue: 1 << 1)
|
||||
/// The default log level.
|
||||
@objc public static let `default` = RiveLogLevel(rawValue: 1 << 2)
|
||||
/// A log level that captures an error.
|
||||
@objc public static let error = RiveLogLevel(rawValue: 1 << 3)
|
||||
/// A log level that captures a fatal error, or fault.
|
||||
@objc public static let fault = RiveLogLevel(rawValue: 1 << 4)
|
||||
|
||||
/// An option set containing no levels.
|
||||
@objc public static let none: RiveLogLevel = []
|
||||
/// An option set containing all possible levels.
|
||||
@objc public static let all: RiveLogLevel = [.debug, .info, .default, .error, .fault]
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let other = object as? RiveLogLevel else { return false }
|
||||
return rawValue == other.rawValue
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return rawValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RiveLogCategory
|
||||
|
||||
@objc public class RiveLogCategory: NSObject, OptionSet {
|
||||
public var rawValue: Int
|
||||
|
||||
public required init(rawValue: Int) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
/// The category used when logging from a Rive state machine.
|
||||
@objc public static let stateMachine = RiveLogCategory(rawValue: 1 << 0)
|
||||
/// The category used when logging from a Rive artboard.
|
||||
@objc public static let artboard = RiveLogCategory(rawValue: 1 << 1)
|
||||
/// The category used when logging from a Rive view model.
|
||||
@objc public static let viewModel = RiveLogCategory(rawValue: 1 << 2)
|
||||
/// The category used when logging from a Rive model.
|
||||
@objc public static let model = RiveLogCategory(rawValue: 1 << 3)
|
||||
/// The category used when logging from a Rive file.
|
||||
@objc public static let file = RiveLogCategory(rawValue: 1 << 4)
|
||||
/// The category used when logging from a Rive view.
|
||||
@objc public static let view = RiveLogCategory(rawValue: 1 << 5)
|
||||
|
||||
/// An option set of no categories.
|
||||
@objc public static let none: RiveLogCategory = []
|
||||
/// An option set containing all possible categories
|
||||
@objc public static let all: RiveLogCategory = [.stateMachine, .artboard, .viewModel, .model, .file, .view]
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let other = object as? RiveLogCategory else { return false }
|
||||
return rawValue == other.rawValue
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return rawValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RiveLogger
|
||||
|
||||
@objc public final class RiveLogger: NSObject {
|
||||
static let subsystem = Bundle(for: RiveLogger.self).bundleIdentifier!
|
||||
|
||||
/// A Bool indicating whether logging is enabled or not.
|
||||
@objc public static var isEnabled = false
|
||||
/// A Bool indicating whether verbose logs are enabled or not.
|
||||
/// - Note: Logs that emit a constant stream of information, such as state machine advances, are considererd verbose.
|
||||
@objc public static var isVerbose = false
|
||||
/// A set of levels that should be logged. Only used when `isEnabled` is set to `true`.
|
||||
@objc public static var levels: RiveLogLevel = .all
|
||||
/// A set of categories that should be logged. Only used when `isEnabled` is set to `true`.
|
||||
@objc public static var categories: RiveLogCategory = .all
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <RiveFileAsset.h>
|
||||
#import <RiveFactory.h>
|
||||
#import <CDNFileAssetLoader.h>
|
||||
#import <RiveRuntime/RiveRuntime-Swift.h>
|
||||
|
||||
@implementation CDNFileAssetLoader
|
||||
{}
|
||||
@@ -38,14 +39,26 @@
|
||||
|
||||
if ([asset isKindOfClass:[RiveFontAsset class]])
|
||||
{
|
||||
[(RiveFontAsset*)asset font:[factory decodeFont:data]];
|
||||
RiveFontAsset* fontAsset = (RiveFontAsset*)asset;
|
||||
[fontAsset font:[factory decodeFont:data]];
|
||||
[RiveLogger logFontAssetLoad:fontAsset fromURL:URL];
|
||||
}
|
||||
else if ([asset isKindOfClass:[RiveImageAsset class]])
|
||||
{
|
||||
[(RiveImageAsset*)asset
|
||||
renderImage:[factory decodeImage:data]];
|
||||
RiveImageAsset* imageAsset = (RiveImageAsset*)asset;
|
||||
[imageAsset renderImage:[factory decodeImage:data]];
|
||||
[RiveLogger logImageAssetLoad:imageAsset fromURL:URL];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* message =
|
||||
[NSString stringWithFormat:
|
||||
@"Failed to load asset from URL %@: %@",
|
||||
URL.absoluteString,
|
||||
error.localizedDescription];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
}
|
||||
}];
|
||||
|
||||
// Kick off the http download
|
||||
@@ -108,7 +121,13 @@
|
||||
andData:(NSData*)data
|
||||
andFactory:(RiveFactory*)factory
|
||||
{
|
||||
return _loadAsset(asset, data, factory);
|
||||
[RiveLogger logLoadingAsset:asset];
|
||||
bool loaded = _loadAsset(asset, data, factory);
|
||||
if (loaded)
|
||||
{
|
||||
[RiveLogger logAssetLoaded:asset];
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#import <Rive.h>
|
||||
#import <RivePrivateHeaders.h>
|
||||
#import <RiveRuntime/RiveRuntime-Swift.h>
|
||||
|
||||
// MARK: - Globals
|
||||
|
||||
@@ -229,6 +230,7 @@ static int artInstanceCount = 0;
|
||||
|
||||
- (void)advanceBy:(double)elapsedSeconds
|
||||
{
|
||||
[RiveLogger logArtboard:self advance:elapsedSeconds];
|
||||
_artboardInstance->advance(elapsedSeconds);
|
||||
}
|
||||
|
||||
@@ -299,6 +301,13 @@ static int artInstanceCount = 0;
|
||||
rive::SMIBool* smi = _artboardInstance->getBool(stdName, stdPath);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logArtboard:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find input named %@ at path %@",
|
||||
name,
|
||||
path]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -325,6 +334,13 @@ static int artInstanceCount = 0;
|
||||
rive::SMITrigger* smi = _artboardInstance->getTrigger(stdName, stdPath);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logArtboard:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find input named %@ at path %@",
|
||||
name,
|
||||
path]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -351,6 +367,13 @@ static int artInstanceCount = 0;
|
||||
rive::SMINumber* smi = _artboardInstance->getNumber(stdName, stdPath);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logArtboard:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find input named %@ at path %@",
|
||||
name,
|
||||
path]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#import <RenderContextManager.h>
|
||||
#import <RiveFileAssetLoader.h>
|
||||
#import <CDNFileAssetLoader.h>
|
||||
#import <RiveRuntime/RiveRuntime-Swift.h>
|
||||
|
||||
#import <FileAssetLoaderAdapter.hpp>
|
||||
|
||||
@@ -180,6 +181,10 @@
|
||||
// QUESTION: good ideas on how we can combine a few of these into following
|
||||
// the same path better?
|
||||
// there's a lot of copy pasta here.
|
||||
|
||||
[RiveLogger logLoadingFromResource:[NSString stringWithFormat:@"%@.%@",
|
||||
resourceName,
|
||||
extension]];
|
||||
NSString* filepath = [[NSBundle mainBundle] pathForResource:resourceName
|
||||
ofType:extension];
|
||||
NSURL* fileUrl = [NSURL fileURLWithPath:filepath];
|
||||
@@ -222,6 +227,9 @@
|
||||
customAssetLoader:(nonnull LoadAsset)customAssetLoader
|
||||
error:(NSError* __autoreleasing _Nullable* _Nullable)error
|
||||
{
|
||||
[RiveLogger logLoadingFromResource:[NSString stringWithFormat:@"%@.%@",
|
||||
resourceName,
|
||||
extension]];
|
||||
NSString* filepath = [[NSBundle mainBundle] pathForResource:resourceName
|
||||
ofType:extension];
|
||||
NSURL* fileUrl = [NSURL fileURLWithPath:filepath];
|
||||
@@ -253,6 +261,7 @@
|
||||
customAssetLoader:(nonnull LoadAsset)customAssetLoader
|
||||
withDelegate:(nonnull id<RiveFileDelegate>)delegate
|
||||
{
|
||||
[RiveLogger logLoadingFromResource:url];
|
||||
self.isLoaded = false;
|
||||
if (self = [super init])
|
||||
{
|
||||
@@ -280,6 +289,7 @@
|
||||
customAssetLoader:customAssetLoader
|
||||
error:&error];
|
||||
self.isLoaded = true;
|
||||
[RiveLogger logLoadedFromURL:URL];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([[NSThread currentThread] isMainThread])
|
||||
{
|
||||
@@ -292,6 +302,14 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* message = [NSString
|
||||
stringWithFormat:@"Failed to load file from URL %@: %@",
|
||||
URL.absoluteString,
|
||||
error.localizedDescription];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
}
|
||||
}];
|
||||
|
||||
// Kick off the http download
|
||||
@@ -354,32 +372,41 @@
|
||||
switch (result)
|
||||
{
|
||||
case rive::ImportResult::unsupportedVersion:
|
||||
{
|
||||
NSString* message = @"Unsupported Rive File Version";
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveUnsupportedVersion
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey :
|
||||
@"Unsupported Rive File Version",
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"UnsupportedVersion"
|
||||
}];
|
||||
break;
|
||||
}
|
||||
case rive::ImportResult::malformed:
|
||||
*error = [NSError
|
||||
errorWithDomain:RiveErrorDomain
|
||||
code:RiveMalformedFile
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Malformed Rive File.",
|
||||
@"name" : @"Malformed"
|
||||
}];
|
||||
{
|
||||
NSString* message = @"Malformed Rive File.";
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveMalformedFile
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"Malformed"
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
NSString* message = @"Unknown error loading file.";
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveUnknownError
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey :
|
||||
@"Unknown error loading file.",
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"Unknown"
|
||||
}];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -389,13 +416,14 @@
|
||||
auto artboard = riveFile->artboardDefault();
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
*error = [NSError
|
||||
errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardsFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"No Artboards Found.",
|
||||
@"name" : @"NoArtboardsFound"
|
||||
}];
|
||||
NSString* message = @"No Artboards Found.";
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardsFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"NoArtboardsFound"
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
@@ -414,15 +442,15 @@
|
||||
auto artboard = riveFile->artboardAt(index);
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
*error = [NSError
|
||||
errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : [NSString
|
||||
stringWithFormat:@"No Artboard Found at index %ld.",
|
||||
(long)index],
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
NSString* message = [NSString
|
||||
stringWithFormat:@"No Artboard Found at index %ld.", (long)index];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
return [[RiveArtboard alloc] initWithArtboard:std::move(artboard)];
|
||||
@@ -434,15 +462,15 @@
|
||||
auto artboard = riveFile->artboardNamed(stdName);
|
||||
if (artboard == nullptr)
|
||||
{
|
||||
*error = [NSError
|
||||
errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : [NSString
|
||||
stringWithFormat:@"No Artboard Found with name %@.",
|
||||
name],
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
NSString* message = [NSString
|
||||
stringWithFormat:@"No Artboard Found with name %@.", name];
|
||||
[RiveLogger logFile:nil error:message];
|
||||
*error = [NSError errorWithDomain:RiveErrorDomain
|
||||
code:RiveNoArtboardFound
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : message,
|
||||
@"name" : @"NoArtboardFound"
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#import <Rive.h>
|
||||
#import <RivePrivateHeaders.h>
|
||||
#import <RiveRuntime/RiveRuntime-Swift.h>
|
||||
|
||||
// MARK: - Globals
|
||||
|
||||
@@ -103,6 +104,7 @@ RiveHitResult RiveHitResultFromRuntime(rive::HitResult result)
|
||||
|
||||
- (bool)advanceBy:(double)elapsedSeconds
|
||||
{
|
||||
[RiveLogger logStateMachine:self advance:elapsedSeconds];
|
||||
return instance->advanceAndApply(elapsedSeconds);
|
||||
}
|
||||
|
||||
@@ -121,6 +123,11 @@ RiveHitResult RiveHitResultFromRuntime(rive::HitResult result)
|
||||
rive::SMIBool* smi = instance->getBool(stdName);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logStateMachine:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find input named %@", name]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -145,6 +152,11 @@ RiveHitResult RiveHitResultFromRuntime(rive::HitResult result)
|
||||
rive::SMITrigger* smi = instance->getTrigger(stdName);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logStateMachine:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find trigger named %@", name]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -169,6 +181,11 @@ RiveHitResult RiveHitResultFromRuntime(rive::HitResult result)
|
||||
rive::SMINumber* smi = instance->getNumber(stdName);
|
||||
if (smi == nullptr)
|
||||
{
|
||||
[RiveLogger
|
||||
logStateMachine:self
|
||||
error:[NSString
|
||||
stringWithFormat:
|
||||
@"Could not find input named %@", name]];
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -43,6 +43,7 @@ import Combine
|
||||
return _volume
|
||||
}
|
||||
set {
|
||||
RiveLogger.log(model: self, event: .volume(newValue))
|
||||
_volume = newValue
|
||||
artboard?.__volume = newValue
|
||||
}
|
||||
@@ -53,6 +54,7 @@ import Combine
|
||||
/// Sets a new Artboard and makes the current StateMachine and Animation nil
|
||||
open func setArtboard(_ name: String) throws {
|
||||
do {
|
||||
RiveLogger.log(model: self, event: .artboardByName(name))
|
||||
stateMachine = nil
|
||||
animation = nil
|
||||
artboard = try riveFile.artboard(fromName: name)
|
||||
@@ -69,21 +71,38 @@ import Combine
|
||||
animation = nil
|
||||
artboard = try riveFile.artboard(from: index)
|
||||
artboard.__volume = _volume
|
||||
RiveLogger.log(model: self, event: .artboardByIndex(index))
|
||||
}
|
||||
catch {
|
||||
let errorMessage = "Artboard at index \(index) not found"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidArtboard(errorMessage)
|
||||
}
|
||||
catch { throw RiveModelError.invalidArtboard("Index \(index) not found") }
|
||||
} else {
|
||||
// This tries to find the 'default' Artboard
|
||||
do {
|
||||
artboard = try riveFile.artboard()
|
||||
artboard.__volume = _volume
|
||||
RiveLogger.log(model: self, event: .defaultArtboard)
|
||||
}
|
||||
catch {
|
||||
let errorMessage = "No Default Artboard"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidArtboard(errorMessage)
|
||||
}
|
||||
catch { throw RiveModelError.invalidArtboard("No Default Artboard") }
|
||||
}
|
||||
}
|
||||
|
||||
open func setStateMachine(_ name: String) throws {
|
||||
do { stateMachine = try artboard.stateMachine(fromName: name) }
|
||||
catch { throw RiveModelError.invalidStateMachine("Name \(name) not found") }
|
||||
do {
|
||||
stateMachine = try artboard.stateMachine(fromName: name)
|
||||
RiveLogger.log(model: self, event: .stateMachineByName(name))
|
||||
}
|
||||
catch {
|
||||
let errorMessage = "State machine named \(name) not found"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidStateMachine(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
open func setStateMachine(_ index: Int? = nil) throws {
|
||||
@@ -91,32 +110,53 @@ import Combine
|
||||
// Set by index
|
||||
if let index = index {
|
||||
stateMachine = try artboard.stateMachine(from: index)
|
||||
RiveLogger.log(model: self, event: .stateMachineByIndex(index))
|
||||
}
|
||||
|
||||
// Set from Artboard's default StateMachine configured in editor
|
||||
else if let defaultStateMachine = artboard.defaultStateMachine() {
|
||||
stateMachine = defaultStateMachine
|
||||
RiveLogger.log(model: self, event: .defaultStateMachine)
|
||||
}
|
||||
|
||||
// Set by index 0 as a fallback
|
||||
else {
|
||||
stateMachine = try artboard.stateMachine(from: 0)
|
||||
RiveLogger.log(model: self, event: .stateMachineByIndex(0))
|
||||
}
|
||||
}
|
||||
catch { throw RiveModelError.invalidStateMachine("Index \(index ?? 0) not found") }
|
||||
catch {
|
||||
let errorMessage = "State machine at index \(index ?? 0) not found"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidStateMachine(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
open func setAnimation(_ name: String) throws {
|
||||
guard animation?.name() != name else { return }
|
||||
do { animation = try artboard.animation(fromName: name) }
|
||||
catch { throw RiveModelError.invalidAnimation("Name \(name) not found") }
|
||||
do {
|
||||
animation = try artboard.animation(fromName: name)
|
||||
RiveLogger.log(model: self, event: .animationByName(name))
|
||||
}
|
||||
catch {
|
||||
let errorMessage = "Animation named \(name) not found"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidAnimation(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
open func setAnimation(_ index: Int? = nil) throws {
|
||||
// Defaults to 0 as it's assumed to be the first element in the collection
|
||||
let index = index ?? 0
|
||||
do { animation = try artboard.animation(from: index) }
|
||||
catch { throw RiveModelError.invalidAnimation("Index \(index) not found") }
|
||||
do {
|
||||
animation = try artboard.animation(from: index)
|
||||
RiveLogger.log(model: self, event: .animationByIndex(index))
|
||||
}
|
||||
catch {
|
||||
let errorMessage = "Animation at index index \(index) not found"
|
||||
RiveLogger.log(model: self, event: .error(errorMessage))
|
||||
throw RiveModelError.invalidAnimation(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@@ -149,6 +149,8 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
/// Starts the render loop
|
||||
internal func play() {
|
||||
RiveLogger.log(view: self, event: .play)
|
||||
|
||||
eventQueue.add {
|
||||
self.playerDelegate?.player(playedWithModel: self.riveModel)
|
||||
}
|
||||
@@ -159,6 +161,8 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
/// Asks the render loop to stop on the next cycle
|
||||
internal func pause() {
|
||||
RiveLogger.log(view: self, event: .pause)
|
||||
|
||||
if isPlaying {
|
||||
eventQueue.add {
|
||||
self.playerDelegate?.player(pausedWithModel: self.riveModel)
|
||||
@@ -169,6 +173,8 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
/// Asks the render loop to stop on the next cycle
|
||||
internal func stop() {
|
||||
RiveLogger.log(view: self, event: .stop)
|
||||
|
||||
playerDelegate?.player(stoppedWithModel: riveModel)
|
||||
isPlaying = false
|
||||
|
||||
@@ -176,8 +182,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
internal func reset() {
|
||||
RiveLogger.log(view: self, event: .reset)
|
||||
|
||||
lastTime = 0
|
||||
|
||||
|
||||
if !isPlaying {
|
||||
advance(delta: 0)
|
||||
}
|
||||
@@ -265,6 +273,7 @@ open class RiveView: RiveRendererView {
|
||||
if (firedEventCount > 0) {
|
||||
for i in 0..<firedEventCount {
|
||||
let event = stateMachine.reportedEvent(at: i)
|
||||
RiveLogger.log(view: self, event: .eventReceived(event.name()))
|
||||
stateMachineDelegate?.onRiveEventReceived?(onRiveEvent: event)
|
||||
}
|
||||
}
|
||||
@@ -288,10 +297,12 @@ open class RiveView: RiveRendererView {
|
||||
|
||||
// This will be true when coming to a hault automatically
|
||||
if wasPlaying {
|
||||
RiveLogger.log(view: self, event: .pause)
|
||||
playerDelegate?.player(pausedWithModel: riveModel)
|
||||
}
|
||||
}
|
||||
|
||||
RiveLogger.log(view: self, event: .advance(delta))
|
||||
playerDelegate?.player(didAdvanceby: delta, riveModel: riveModel)
|
||||
|
||||
// Trigger a redraw
|
||||
@@ -303,6 +314,7 @@ open class RiveView: RiveRendererView {
|
||||
// This prevents breaking when loading RiveFile async
|
||||
guard let artboard = riveModel?.artboard else { return }
|
||||
|
||||
RiveLogger.log(view: self, event: .drawing(size))
|
||||
let newFrame = CGRect(origin: rect.origin, size: size)
|
||||
align(with: newFrame, contentRect: artboard.bounds(), alignment: alignment, fit: fit)
|
||||
draw(with: artboard)
|
||||
@@ -326,8 +338,12 @@ open class RiveView: RiveRendererView {
|
||||
// MARK: - UIResponder
|
||||
#if os(iOS)
|
||||
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchBegan) {
|
||||
let result = $0.touchBegan(atLocation: $1)
|
||||
guard let touch = touches.first else { return }
|
||||
|
||||
handleTouch(touch, delegate: stateMachineDelegate?.touchBegan) { stateMachine, location in
|
||||
let result = stateMachine.touchBegan(atLocation: location)
|
||||
RiveLogger.log(view: self, event: .touchBegan(location))
|
||||
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .began)
|
||||
}
|
||||
@@ -339,8 +355,12 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchMoved) {
|
||||
let result = $0.touchMoved(atLocation: $1)
|
||||
guard let touch = touches.first else { return }
|
||||
|
||||
handleTouch(touch, delegate: stateMachineDelegate?.touchMoved) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchMoved(location))
|
||||
|
||||
let result = stateMachine.touchMoved(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
|
||||
}
|
||||
@@ -352,8 +372,12 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchEnded) {
|
||||
let result = $0.touchEnded(atLocation: $1)
|
||||
guard let touch = touches.first else { return }
|
||||
|
||||
handleTouch(touch, delegate: stateMachineDelegate?.touchEnded) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchEnded(location))
|
||||
|
||||
let result = stateMachine.touchEnded(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .ended)
|
||||
}
|
||||
@@ -365,8 +389,12 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
handleTouch(touches.first!, delegate: stateMachineDelegate?.touchCancelled) {
|
||||
let result = $0.touchCancelled(atLocation: $1)
|
||||
guard let touch = touches.first else { return }
|
||||
|
||||
handleTouch(touch, delegate: stateMachineDelegate?.touchCancelled) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchCancelled(location))
|
||||
|
||||
let result = stateMachine.touchCancelled(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .cancelled)
|
||||
}
|
||||
@@ -409,8 +437,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
#else
|
||||
open override func mouseDown(with event: NSEvent) {
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchBegan) {
|
||||
let result = $0.touchBegan(atLocation: $1)
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchBegan) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchBegan(location))
|
||||
|
||||
let result = stateMachine.touchBegan(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .began)
|
||||
}
|
||||
@@ -422,8 +452,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func mouseMoved(with event: NSEvent) {
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) {
|
||||
let result = $0.touchMoved(atLocation: $1)
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchMoved(location))
|
||||
|
||||
let result = stateMachine.touchMoved(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
|
||||
}
|
||||
@@ -435,8 +467,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func mouseDragged(with event: NSEvent) {
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) {
|
||||
let result = $0.touchMoved(atLocation: $1)
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchMoved) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchMoved(location))
|
||||
|
||||
let result = stateMachine.touchMoved(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .moved)
|
||||
}
|
||||
@@ -448,8 +482,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func mouseUp(with event: NSEvent) {
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchEnded) {
|
||||
let result = $0.touchEnded(atLocation: $1)
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchEnded) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchEnded(location))
|
||||
|
||||
let result = stateMachine.touchEnded(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .ended)
|
||||
}
|
||||
@@ -461,8 +497,10 @@ open class RiveView: RiveRendererView {
|
||||
}
|
||||
|
||||
open override func mouseExited(with event: NSEvent) {
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchCancelled) {
|
||||
let result = $0.touchCancelled(atLocation: $1)
|
||||
handleTouch(event, delegate: stateMachineDelegate?.touchCancelled) { stateMachine, location in
|
||||
RiveLogger.log(view: self, event: .touchCancelled(location))
|
||||
|
||||
let result = stateMachine.touchCancelled(atLocation: location)
|
||||
if let stateMachine = riveModel?.stateMachine {
|
||||
stateMachineDelegate?.stateMachine?(stateMachine, didReceiveHitResult: result, from: .cancelled)
|
||||
}
|
||||
|
||||
@@ -241,23 +241,26 @@ import Combine
|
||||
|
||||
// We're not checking if a StateMachine is "ended" or "ExitState"
|
||||
// But we may want to in the future to enable restarting it by playing again
|
||||
|
||||
RiveLogger.log(viewModel: self, event: .play)
|
||||
riveView?.play()
|
||||
}
|
||||
|
||||
/// Halts the active Animation or StateMachine and will resume from it's current position when next played
|
||||
@objc open func pause() {
|
||||
RiveLogger.log(viewModel: self, event: .pause)
|
||||
riveView?.pause()
|
||||
}
|
||||
|
||||
/// Halts the active Animation or StateMachine and sets it at its starting position
|
||||
@objc open func stop() {
|
||||
RiveLogger.log(viewModel: self, event: .stop)
|
||||
resetCurrentModel()
|
||||
riveView?.stop()
|
||||
}
|
||||
|
||||
/// Sets the active Animation or StateMachine back to their starting position
|
||||
@objc open func reset() {
|
||||
RiveLogger.log(viewModel: self, event: .reset)
|
||||
resetCurrentModel()
|
||||
riveView?.reset()
|
||||
}
|
||||
@@ -266,8 +269,12 @@ import Combine
|
||||
|
||||
/// Instantiates elements in the model needed to play in a `RiveView`
|
||||
@objc open func configureModel(artboardName: String? = nil, stateMachineName: String? = nil, animationName: String? = nil) throws {
|
||||
guard let model = riveModel else { fatalError("Cannot configure nil RiveModel") }
|
||||
|
||||
guard let model = riveModel else {
|
||||
let errorMessage = "Cannot configure nil RiveModel"
|
||||
RiveLogger.log(viewModel: self, event: .fatalError(errorMessage))
|
||||
fatalError(errorMessage)
|
||||
}
|
||||
|
||||
model.animation = nil
|
||||
model.stateMachine = nil
|
||||
|
||||
@@ -300,7 +307,11 @@ import Combine
|
||||
|
||||
/// Puts the active Animation or StateMachine back to their starting position
|
||||
private func resetCurrentModel() {
|
||||
guard let model = riveModel else { fatalError("Current model is nil") }
|
||||
guard let model = riveModel else {
|
||||
let errorMessage = "Current model is nil"
|
||||
RiveLogger.log(viewModel: self, event: .fatalError(errorMessage))
|
||||
fatalError(errorMessage)
|
||||
}
|
||||
try! configureModel(
|
||||
artboardName: model.artboard.name(),
|
||||
stateMachineName: model.stateMachine?.name(),
|
||||
@@ -321,6 +332,7 @@ import Combine
|
||||
/// Provide the active StateMachine a `Trigger` input
|
||||
/// - Parameter inputName: The name of a `Trigger` input on the active StateMachine
|
||||
@objc open func triggerInput(_ inputName: String) {
|
||||
RiveLogger.log(viewModel: self, event: .triggerInput(inputName, nil))
|
||||
riveModel?.stateMachine?.getTrigger(inputName).fire()
|
||||
play()
|
||||
}
|
||||
@@ -330,6 +342,7 @@ import Combine
|
||||
/// - inputName: The name of a `Boolean` input on the active StateMachine
|
||||
/// - value: A Bool value for the input
|
||||
@objc(setBooleanInput::) open func setInput(_ inputName: String, value: Bool) {
|
||||
RiveLogger.log(viewModel: self, event: .booleanInput(inputName, nil, value))
|
||||
riveModel?.stateMachine?.getBool(inputName).setValue(value)
|
||||
play()
|
||||
}
|
||||
@@ -339,6 +352,7 @@ import Combine
|
||||
/// - inputName: The name of a `Number` input on the active StateMachine
|
||||
/// - value: A Float value for the input
|
||||
@objc(setFloatInput::) open func setInput(_ inputName: String, value: Float) {
|
||||
RiveLogger.log(viewModel: self, event: .floatInput(inputName, nil, value))
|
||||
riveModel?.stateMachine?.getNumber(inputName).setValue(value)
|
||||
play()
|
||||
}
|
||||
@@ -348,6 +362,7 @@ import Combine
|
||||
/// - inputName: The name of a `Number` input on the active StateMachine
|
||||
/// - value: A Double value for the input
|
||||
@objc(setDoubleInput::) open func setInput(_ inputName: String, value: Double) {
|
||||
RiveLogger.log(viewModel: self, event: .doubleInput(inputName, nil, value))
|
||||
setInput(inputName, value: Float(value))
|
||||
}
|
||||
|
||||
@@ -356,6 +371,7 @@ import Combine
|
||||
/// - inputName: The name of a `Trigger` input on the active StateMachine
|
||||
/// - path: A String representing the path to the nested artboard delimited by "/" (ie. "Nested" or "Level1/Level2/Level3")
|
||||
open func triggerInput(_ inputName: String, path: String) {
|
||||
RiveLogger.log(viewModel: self, event: .triggerInput(inputName, path))
|
||||
riveModel?.artboard?.getTrigger(inputName, path: path).fire()
|
||||
play()
|
||||
}
|
||||
@@ -366,6 +382,7 @@ import Combine
|
||||
/// - value: A Bool value for the input
|
||||
/// - path: A String representing the path to the nested artboard delimited by "/" (ie. "Nested" or "Level1/Level2/Level3")
|
||||
open func setInput(_ inputName: String, value: Bool, path: String) {
|
||||
RiveLogger.log(viewModel: self, event: .booleanInput(inputName, path, value))
|
||||
riveModel?.artboard?.getBool(inputName, path: path).setValue(value)
|
||||
play()
|
||||
}
|
||||
@@ -376,6 +393,7 @@ import Combine
|
||||
/// - value: A Float value for the input
|
||||
/// - path: A String representing the path to the nested artboard delimited by "/" (ie. "Nested" or "Level1/Level2/Level3")
|
||||
open func setInput(_ inputName: String, value: Float, path: String) {
|
||||
RiveLogger.log(viewModel: self, event: .floatInput(inputName, path, value))
|
||||
riveModel?.artboard?.getNumber(inputName, path: path).setValue(value);
|
||||
play()
|
||||
}
|
||||
@@ -386,6 +404,7 @@ import Combine
|
||||
/// - value: A Double value for the input
|
||||
/// - path: A String representing the path to the nested artboard delimited by "/" (ie. "Nested" or "Level1/Level2/Level3")
|
||||
open func setInput(_ inputName: String, value: Double, path: String) {
|
||||
RiveLogger.log(viewModel: self, event: .doubleInput(inputName, path, value))
|
||||
setInput(inputName, value: Float(value), path: path)
|
||||
}
|
||||
|
||||
@@ -418,13 +437,16 @@ import Combine
|
||||
/// - value: A String value for the text run
|
||||
@objc open func setTextRunValue(_ textRunName: String, textValue: String) throws {
|
||||
if let textRun = riveModel?.artboard?.textRun(textRunName) {
|
||||
RiveLogger.log(viewModel: self, event: .textRun(textRunName, nil, textValue))
|
||||
textRun.setText(textValue)
|
||||
|
||||
if isPlaying == false {
|
||||
riveView?.advance(delta: 0)
|
||||
}
|
||||
} else {
|
||||
throw RiveError.textValueRunError("Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard")
|
||||
let errorMessage = "Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard"
|
||||
RiveLogger.log(viewModel: self, event: .error(errorMessage))
|
||||
throw RiveError.textValueRunError(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,13 +458,16 @@ import Combine
|
||||
/// - Note: If the specified path is empty, the parent artboard will be used to find the text run.
|
||||
@objc open func setTextRunValue(_ textRunName: String, path: String, textValue: String) throws {
|
||||
if let textRun = riveModel?.artboard?.textRun(textRunName, path: path) {
|
||||
RiveLogger.log(viewModel: self, event: .textRun(textRunName, path, textValue))
|
||||
textRun.setText(textValue)
|
||||
|
||||
if isPlaying == false {
|
||||
riveView?.advance(delta: 0)
|
||||
}
|
||||
} else {
|
||||
throw RiveError.textValueRunError("Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard")
|
||||
let errorMessage = "Could not set text value on text run: \(textRunName) as the text run could not be found from the active artboard"
|
||||
RiveLogger.log(viewModel: self, event: .error(errorMessage))
|
||||
throw RiveError.textValueRunError(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import Foundation
|
||||
|
||||
public extension RiveFile {
|
||||
convenience init(name fileName: String, extension ext: String = ".riv", in bundle: Bundle = .main, loadCdn: Bool=true, customLoader: LoadAsset? = nil) throws {
|
||||
RiveLogger.log(loadingFromResource: "\(fileName)\(ext)")
|
||||
let byteArray = RiveFile.getBytes(fileName: fileName, extension: ext, in: bundle)
|
||||
if (customLoader == nil){
|
||||
try self.init(byteArray: byteArray, loadCdn: loadCdn)
|
||||
@@ -20,10 +21,14 @@ public extension RiveFile {
|
||||
|
||||
static func getBytes(fileName: String, extension ext: String = ".riv", in bundle: Bundle = .main) -> [UInt8] {
|
||||
guard let url = bundle.url(forResource: fileName, withExtension: ext) else {
|
||||
fatalError("Failed to locate \(fileName) in bundle \(bundle).")
|
||||
let errorMessage = "Failed to locate \(fileName) in bundle \(bundle)."
|
||||
RiveLogger.log(file: nil, event: .fatalError(errorMessage))
|
||||
fatalError(errorMessage)
|
||||
}
|
||||
guard let data = try? Data(contentsOf: url) else {
|
||||
fatalError("Failed to load \(url) from bundle.")
|
||||
let errorMessage = "Failed to load \(url) from bundle."
|
||||
RiveLogger.log(file: nil, event: .fatalError(errorMessage))
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// Import the data into a RiveFile
|
||||
|
||||
Reference in New Issue
Block a user