Skip to content

Commit a0f9cec

Browse files
authored
Merge pull request #60 from rive-app/networking
New constructors for RiveFile
2 parents f8c7f23 + 3f2dbb4 commit a0f9cec

File tree

4 files changed

+153
-39
lines changed

4 files changed

+153
-39
lines changed

Example-iOS/Source/UIkit/SimpleAnimation.swift

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,36 @@
99
import UIKit
1010
import RiveRuntime
1111

12-
func getResourceBytes(resourceName: String, resourceExt: String=".riv") -> [UInt8] {
13-
guard let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExt) else {
14-
fatalError("Failed to locate \(resourceName) in bundle.")
15-
}
16-
guard let data = try? Data(contentsOf: url) else {
17-
fatalError("Failed to load \(url) from bundle.")
18-
}
19-
20-
// Import the data into a RiveFile
21-
return [UInt8](data)
22-
}
23-
24-
2512
class SimpleAnimationViewController: UIViewController {
2613
let resourceName = "truck_v7"
2714

2815
override public func loadView() {
2916
super.loadView()
3017

3118
let view = RiveView()
32-
guard let riveFile = RiveFile(byteArray: getResourceBytes(resourceName: resourceName)) else {
19+
guard let riveFile = RiveFile(resource: resourceName) else {
3320
fatalError("Failed to load RiveFile")
3421
}
22+
3523
view.configure(riveFile)
36-
3724
self.view = view
3825
}
3926
}
27+
28+
/*
29+
class SimpleAnimationViewController: UIViewController {
30+
let url = "https://cdn.rive.app/animations/truck.riv"
31+
32+
override public func loadView() {
33+
super.loadView()
34+
35+
let view = RiveView()
36+
guard let riveFile = RiveFile(httpUrl: url, with: view) else {
37+
fatalError("Unable to load RiveFile")
38+
}
39+
40+
view.configure(riveFile)
41+
self.view = view
42+
}
43+
}
44+
*/

Source/Renderer/RiveFile.mm

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
// Copyright © 2021 Rive. All rights reserved.
77
//
88

9-
109
#import <Rive.h>
1110
#import <RivePrivateHeaders.h>
1211

@@ -32,23 +31,6 @@ @implementation RiveFile {
3231
+ (uint)majorVersion { return UInt8(rive::File::majorVersion); }
3332
+ (uint)minorVersion { return UInt8(rive::File::minorVersion); }
3433

35-
- (void) import:(rive::BinaryReader)reader {
36-
rive::ImportResult result = rive::File::import(reader, &riveFile);
37-
if (result == rive::ImportResult::success) {
38-
return;
39-
}
40-
else if(result == rive::ImportResult::unsupportedVersion){
41-
@throw [[RiveException alloc] initWithName:@"UnsupportedVersion" reason:@"Unsupported Rive File Version." userInfo:nil];
42-
43-
}
44-
else if(result == rive::ImportResult::malformed){
45-
@throw [[RiveException alloc] initWithName:@"Malformed" reason:@"Malformed Rive File." userInfo:nil];
46-
}
47-
else {
48-
@throw [[RiveException alloc] initWithName:@"Unknown" reason:@"Unknown error loading file." userInfo:nil];
49-
}
50-
}
51-
5234
- (nullable instancetype)initWithByteArray:(NSArray *)array {
5335
if (self = [super init]) {
5436
UInt8* bytes;
@@ -60,6 +42,7 @@ - (nullable instancetype)initWithByteArray:(NSArray *)array {
6042
}];
6143
rive::BinaryReader reader = [self getReader:bytes byteLength:array.count];
6244
[self import:reader];
45+
self.isLoaded = true;
6346
}
6447
@finally {
6548
free(bytes);
@@ -74,11 +57,88 @@ - (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length
7457
if (self = [super init]) {
7558
rive::BinaryReader reader = [self getReader:bytes byteLength:length];
7659
[self import:reader];
60+
self.isLoaded = true;
61+
return self;
62+
}
63+
return nil;
64+
}
65+
66+
/*
67+
* Creates a RiveFile from a binary resource
68+
*/
69+
- (nullable instancetype)initWithResource:(NSString *)resourceName withExtension:(NSString *)extension {
70+
NSString *filepath = [[NSBundle mainBundle] pathForResource:resourceName ofType:extension];
71+
NSURL *fileUrl = [NSURL fileURLWithPath:filepath];
72+
NSData *fileData = [NSData dataWithContentsOfURL:fileUrl];
73+
UInt8 *bytePtr = (UInt8 *)[fileData bytes];
74+
75+
return [[RiveFile alloc] initWithBytes:bytePtr byteLength:fileData.length];
76+
}
77+
78+
/*
79+
* Creates a RiveFile from a binary resource, and assumes the resource extension is '.riv'
80+
*/
81+
- (nullable instancetype)initWithResource:(NSString *)resourceName {
82+
return [[RiveFile alloc] initWithResource:resourceName withExtension:@"riv"];
83+
}
84+
85+
/*
86+
* Creates a RiveFile from an HTTP url
87+
*/
88+
- (nullable instancetype)initWithHttpUrl:(NSString *)url withDelegate:(id<RiveFileDelegate>)delegate {
89+
self.isLoaded = false;
90+
if (self = [super init]) {
91+
self.delegate = delegate;
92+
// Set up the http download task
93+
NSURL *URL = [NSURL URLWithString:url];
94+
NSURLSession *session = [NSURLSession sessionWithConfiguration:
95+
[NSURLSessionConfiguration defaultSessionConfiguration]];
96+
NSURLSessionTask *task = [session downloadTaskWithURL:URL
97+
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
98+
if (!error) {
99+
// Load the data into the reader
100+
NSData *data = [NSData dataWithContentsOfURL: location];
101+
UInt8 *bytes = (UInt8 *)[data bytes];
102+
rive::BinaryReader reader = [self getReader:bytes byteLength:[data length]];
103+
[self import:reader];
104+
self.isLoaded = true;
105+
dispatch_async(dispatch_get_main_queue(), ^{
106+
if ([[NSThread currentThread] isMainThread]) {
107+
if ([self.delegate respondsToSelector:@selector(riveFileDidLoad:)]) {
108+
[self.delegate riveFileDidLoad:self];
109+
}
110+
}
111+
});
112+
}
113+
}];
114+
115+
// Kick off the http download
116+
[task resume];
117+
118+
// Return the as yet uninitialized RiveFile
77119
return self;
78120
}
121+
79122
return nil;
80123
}
81124

125+
- (void) import:(rive::BinaryReader)reader {
126+
rive::ImportResult result = rive::File::import(reader, &riveFile);
127+
if (result == rive::ImportResult::success) {
128+
return;
129+
}
130+
else if(result == rive::ImportResult::unsupportedVersion){
131+
@throw [[RiveException alloc] initWithName:@"UnsupportedVersion" reason:@"Unsupported Rive File Version." userInfo:nil];
132+
133+
}
134+
else if(result == rive::ImportResult::malformed){
135+
@throw [[RiveException alloc] initWithName:@"Malformed" reason:@"Malformed Rive File." userInfo:nil];
136+
}
137+
else {
138+
@throw [[RiveException alloc] initWithName:@"Unknown" reason:@"Unknown error loading file." userInfo:nil];
139+
}
140+
}
141+
82142
- (RiveArtboard *)artboard {
83143
rive::Artboard *artboard = riveFile->artboard();
84144
if (artboard == nullptr) {

Source/Renderer/include/RiveFile.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
NS_ASSUME_NONNULL_BEGIN
1616

1717
@class RiveArtboard;
18+
@protocol RiveFileDelegate;
1819

1920
/*
2021
* RiveFile
@@ -24,8 +25,17 @@ NS_ASSUME_NONNULL_BEGIN
2425
@property (class, readonly) uint majorVersion;
2526
@property (class, readonly) uint minorVersion;
2627

28+
// Is the Rive file loaded and ready for use?
29+
@property bool isLoaded;
30+
31+
// Delegate for calling when a file has finished loading
32+
@property id delegate;
33+
2734
- (nullable instancetype)initWithByteArray:(NSArray *)bytes;
2835
- (nullable instancetype)initWithBytes:(UInt8 *)bytes byteLength:(UInt64)length;
36+
- (nullable instancetype)initWithResource:(NSString *)resourceName withExtension:(NSString *)extension;
37+
- (nullable instancetype)initWithResource:(NSString *)resourceName;
38+
- (nullable instancetype)initWithHttpUrl:(NSString *)url withDelegate:(id<RiveFileDelegate>)delegate;
2939

3040
// Returns a reference to the default artboard
3141
- (RiveArtboard *)artboard;
@@ -42,7 +52,13 @@ NS_ASSUME_NONNULL_BEGIN
4252
// Returns the names of all artboards in the file.
4353
- (NSArray<NSString *> *)artboardNames;
4454

55+
@end
4556

57+
/*
58+
* Delegate to inform when a rive file is loaded
59+
*/
60+
@protocol RiveFileDelegate <NSObject>
61+
- (void)riveFileDidLoad:(RiveFile *)riveFile;
4662
@end
4763

4864
NS_ASSUME_NONNULL_END

Source/Views/RiveView.swift

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ class EventQueue {
8484
}
8585
}
8686

87+
/// Stores config options for a RiveFile when rive files load async
88+
struct ConfigOptions {
89+
let riveFile: RiveFile
90+
var artboard: String? = nil
91+
var animation: String? = nil
92+
var stateMachine: String?
93+
var autoPlay: Bool = true
94+
}
95+
8796
public class RiveView: UIView {
8897
// Configuration
8998
private var riveFile: RiveFile?
@@ -108,6 +117,9 @@ public class RiveView: UIView {
108117
public weak var inputsDelegate: InputsDelegate?
109118
public weak var stateChangeDelegate: StateChangeDelegate?
110119

120+
// Tracks config options when rive files load asynchronously
121+
private var configOptions: ConfigOptions?
122+
111123
// Queue of events that need to be done outside view updates
112124
private var eventQueue = EventQueue()
113125

@@ -150,7 +162,7 @@ public class RiveView: UIView {
150162
self.stopDelegate = stopDelegate
151163
self.inputsDelegate = inputsDelegate
152164
self.stateChangeDelegate = stateChangeDelegate
153-
self.configure(riveFile, andArtboard: artboard, andAnimation:animation, andStateMachine: stateMachine, andAutoPlay: autoplay)
165+
self.configure(riveFile, andArtboard: artboard, andAnimation: animation, andStateMachine: stateMachine, andAutoPlay: autoplay)
154166
}
155167

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

178+
// Handle when a Rive file is asynchronously loaded
179+
extension RiveView: RiveFileDelegate {
180+
public func riveFileDidLoad(_ riveFile: RiveFile) {
181+
self.configure(riveFile);
182+
}
183+
}
184+
166185
// MARK:- Configure
167186
extension RiveView {
168187
/// Configure fit to specify how and if the animation should be resized to fit its container.
@@ -206,6 +225,18 @@ extension RiveView {
206225
andStateMachine stateMachine: String?=nil,
207226
andAutoPlay autoPlay: Bool=true
208227
) {
228+
if !riveFile.isLoaded {
229+
// Save the config details for async call
230+
self.configOptions = ConfigOptions(
231+
riveFile: riveFile,
232+
artboard: artboard,
233+
animation: animation,
234+
stateMachine: stateMachine,
235+
autoPlay: autoPlay
236+
);
237+
return;
238+
}
239+
209240
clear()
210241
// Testing stuff
211242
NotificationCenter.default.addObserver(self, selector: #selector(animationWillEnterForeground),
@@ -217,9 +248,9 @@ extension RiveView {
217248
self.isOpaque = false
218249

219250
self.riveFile = riveFile
220-
self.autoPlay = autoPlay
251+
self.autoPlay = configOptions?.autoPlay ?? autoPlay
221252

222-
if let artboardName = artboard {
253+
if let artboardName = configOptions?.artboard ?? artboard {
223254
self._artboard = riveFile.artboard(fromName:artboardName)
224255
}else {
225256
self._artboard = riveFile.artboard()
@@ -239,16 +270,18 @@ extension RiveView {
239270

240271
// Start the animation loop
241272
if autoPlay {
242-
if let animationName = animation {
273+
if let animationName = configOptions?.animation ?? animation {
243274
play(animationName: animationName)
244-
}else if let stateMachineName = stateMachine {
275+
}else if let stateMachineName = configOptions?.stateMachine ?? stateMachine {
245276
play(animationName: stateMachineName, isStateMachine: true)
246277
}else {
247278
play()
248279
}
249-
}else {
280+
} else {
250281
advance(delta: 0)
251282
}
283+
// Clear out any config options
284+
self.configOptions = nil
252285
}
253286

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

0 commit comments

Comments
 (0)