Skip to content

Commit

Permalink
Adds loading RiveFiles over HTTP
Browse files Browse the repository at this point in the history
  • Loading branch information
mjohnsullivan committed Jun 2, 2021
1 parent d61c704 commit 3f2dbb4
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 8 deletions.
20 changes: 19 additions & 1 deletion Example-iOS/Source/UIkit/SimpleAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,26 @@ class SimpleAnimationViewController: UIViewController {
guard let riveFile = RiveFile(resource: resourceName) else {
fatalError("Failed to load RiveFile")
}

view.configure(riveFile)

self.view = view
}
}

/*
class SimpleAnimationViewController: UIViewController {
let url = "https://cdn.rive.app/animations/truck.riv"

override public func loadView() {
super.loadView()

let view = RiveView()
guard let riveFile = RiveFile(httpUrl: url, with: view) else {
fatalError("Unable to load RiveFile")
}

view.configure(riveFile)
self.view = view
}
}
*/
43 changes: 42 additions & 1 deletion Source/Renderer/RiveFile.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// Copyright © 2021 Rive. All rights reserved.
//


#import <Rive.h>
#import <RivePrivateHeaders.h>

Expand Down Expand Up @@ -43,6 +42,7 @@ - (nullable instancetype)initWithByteArray:(NSArray *)array {
}];
rive::BinaryReader reader = [self getReader:bytes byteLength:array.count];
[self import:reader];
self.isLoaded = true;
}
@finally {
free(bytes);
Expand All @@ -57,6 +57,7 @@ - (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length
if (self = [super init]) {
rive::BinaryReader reader = [self getReader:bytes byteLength:length];
[self import:reader];
self.isLoaded = true;
return self;
}
return nil;
Expand All @@ -81,6 +82,46 @@ - (nullable instancetype)initWithResource:(NSString *)resourceName {
return [[RiveFile alloc] initWithResource:resourceName withExtension:@"riv"];
}

/*
* Creates a RiveFile from an HTTP url
*/
- (nullable instancetype)initWithHttpUrl:(NSString *)url withDelegate:(id<RiveFileDelegate>)delegate {
self.isLoaded = false;
if (self = [super init]) {
self.delegate = delegate;
// Set up the http download task
NSURL *URL = [NSURL URLWithString:url];
NSURLSession *session = [NSURLSession sessionWithConfiguration:
[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionTask *task = [session downloadTaskWithURL:URL
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (!error) {
// Load the data into the reader
NSData *data = [NSData dataWithContentsOfURL: location];
UInt8 *bytes = (UInt8 *)[data bytes];
rive::BinaryReader reader = [self getReader:bytes byteLength:[data length]];
[self import:reader];
self.isLoaded = true;
dispatch_async(dispatch_get_main_queue(), ^{
if ([[NSThread currentThread] isMainThread]) {
if ([self.delegate respondsToSelector:@selector(riveFileDidLoad:)]) {
[self.delegate riveFileDidLoad:self];
}
}
});
}
}];

// Kick off the http download
[task resume];

// Return the as yet uninitialized RiveFile
return self;
}

return nil;
}

- (void) import:(rive::BinaryReader)reader {
rive::ImportResult result = rive::File::import(reader, &riveFile);
if (result == rive::ImportResult::success) {
Expand Down
14 changes: 14 additions & 0 deletions Source/Renderer/include/RiveFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
NS_ASSUME_NONNULL_BEGIN

@class RiveArtboard;
@protocol RiveFileDelegate;

/*
* RiveFile
Expand All @@ -24,10 +25,17 @@ NS_ASSUME_NONNULL_BEGIN
@property (class, readonly) uint majorVersion;
@property (class, readonly) uint minorVersion;

// Is the Rive file loaded and ready for use?
@property bool isLoaded;

// Delegate for calling when a file has finished loading
@property id delegate;

- (nullable instancetype)initWithByteArray:(NSArray *)bytes;
- (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length;
- (nullable instancetype)initWithResource:(NSString *)resourceName withExtension:(NSString *)extension;
- (nullable instancetype)initWithResource:(NSString *)resourceName;
- (nullable instancetype)initWithHttpUrl:(NSString *)url withDelegate:(id<RiveFileDelegate>)delegate;

// Returns a reference to the default artboard
- (RiveArtboard *)artboard;
Expand All @@ -44,7 +52,13 @@ NS_ASSUME_NONNULL_BEGIN
// Returns the names of all artboards in the file.
- (NSArray<NSString *> *)artboardNames;

@end

/*
* Delegate to inform when a rive file is loaded
*/
@protocol RiveFileDelegate <NSObject>
- (void)riveFileDidLoad:(RiveFile *)riveFile;
@end

NS_ASSUME_NONNULL_END
Expand Down
45 changes: 39 additions & 6 deletions Source/Views/RiveView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ class EventQueue {
}
}

/// Stores config options for a RiveFile when rive files load async
struct ConfigOptions {
let riveFile: RiveFile
var artboard: String? = nil
var animation: String? = nil
var stateMachine: String?
var autoPlay: Bool = true
}

public class RiveView: UIView {
// Configuration
private var riveFile: RiveFile?
Expand All @@ -108,6 +117,9 @@ public class RiveView: UIView {
public weak var inputsDelegate: InputsDelegate?
public weak var stateChangeDelegate: StateChangeDelegate?

// Tracks config options when rive files load asynchronously
private var configOptions: ConfigOptions?

// Queue of events that need to be done outside view updates
private var eventQueue = EventQueue()

Expand Down Expand Up @@ -150,7 +162,7 @@ public class RiveView: UIView {
self.stopDelegate = stopDelegate
self.inputsDelegate = inputsDelegate
self.stateChangeDelegate = stateChangeDelegate
self.configure(riveFile, andArtboard: artboard, andAnimation:animation, andStateMachine: stateMachine, andAutoPlay: autoplay)
self.configure(riveFile, andArtboard: artboard, andAnimation: animation, andStateMachine: stateMachine, andAutoPlay: autoplay)
}

/// Minimalist constructor, call `.configure` to customize the `RiveView` later.
Expand All @@ -163,6 +175,13 @@ public class RiveView: UIView {
}
}

// Handle when a Rive file is asynchronously loaded
extension RiveView: RiveFileDelegate {
public func riveFileDidLoad(_ riveFile: RiveFile) {
self.configure(riveFile);
}
}

// MARK:- Configure
extension RiveView {
/// Configure fit to specify how and if the animation should be resized to fit its container.
Expand Down Expand Up @@ -206,6 +225,18 @@ extension RiveView {
andStateMachine stateMachine: String?=nil,
andAutoPlay autoPlay: Bool=true
) {
if !riveFile.isLoaded {
// Save the config details for async call
self.configOptions = ConfigOptions(
riveFile: riveFile,
artboard: artboard,
animation: animation,
stateMachine: stateMachine,
autoPlay: autoPlay
);
return;
}

clear()
// Testing stuff
NotificationCenter.default.addObserver(self, selector: #selector(animationWillEnterForeground),
Expand All @@ -217,9 +248,9 @@ extension RiveView {
self.isOpaque = false

self.riveFile = riveFile
self.autoPlay = autoPlay
self.autoPlay = configOptions?.autoPlay ?? autoPlay

if let artboardName = artboard {
if let artboardName = configOptions?.artboard ?? artboard {
self._artboard = riveFile.artboard(fromName:artboardName)
}else {
self._artboard = riveFile.artboard()
Expand All @@ -239,16 +270,18 @@ extension RiveView {

// Start the animation loop
if autoPlay {
if let animationName = animation {
if let animationName = configOptions?.animation ?? animation {
play(animationName: animationName)
}else if let stateMachineName = stateMachine {
}else if let stateMachineName = configOptions?.stateMachine ?? stateMachine {
play(animationName: stateMachineName, isStateMachine: true)
}else {
play()
}
}else {
} else {
advance(delta: 0)
}
// Clear out any config options
self.configOptions = nil
}

/// Stop playback, clear any created animation or state machine instances.
Expand Down

0 comments on commit 3f2dbb4

Please sign in to comment.