Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BREAKING on iOS] Add blob implementation with WebSocket integration #11417

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
684 changes: 684 additions & 0 deletions Examples/2048/2048.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

730 changes: 730 additions & 0 deletions Examples/Movies/Movies.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

728 changes: 728 additions & 0 deletions Examples/TicTacToe/TicTacToe.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

152 changes: 152 additions & 0 deletions Libraries/Blob/Blob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule Blob
* @flow
*/

'use strict';

const uuid = require('uuid');
const invariant = require('fbjs/lib/invariant');
const { BlobModule } = require('NativeModules');

import type { BlobProps } from './BlobTypes';

/**
* Opaque JS representation of some binary data in native.
*
* The API is modeled after the W3C Blob API, with one caveat
* regarding explicit deallocation. Refer to the `close()`
* method for further details.
*
* Example usage in a React component:
*
* class WebSocketImage extends React.Component {
* state = {blob: null};
* componentDidMount() {
* let ws = this.ws = new WebSocket(...);
* ws.binaryType = 'blob';
* ws.onmessage = (event) => {
* if (this.state.blob) {
* this.state.blob.close();
* }
* this.setState({blob: event.data});
* };
* }
* componentUnmount() {
* if (this.state.blob) {
* this.state.blob.close();
* }
* this.ws.close();
* }
* render() {
* if (!this.state.blob) {
* return <View />;
* }
* return <Image source={{uri: URL.createObjectURL(this.state.blob)}} />;
* }
* }
*
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob
*/
class Blob {
/**
* Size of the data contained in the Blob object, in bytes.
*/
size: number;
/*
* String indicating the MIME type of the data contained in the Blob.
* If the type is unknown, this string is empty.
*/
type: string;

/*
* Unique id to identify the blob on native side (non-standard)
*/
blobId: string;
/*
* Offset to indicate part of blob, used when sliced (non-standard)
*/
offset: number;

/**
* Construct blob instance from blob data from native.
* Used internally by modules like XHR, WebSocket, etc.
*/
static create(props: BlobProps): Blob {
return Object.assign(Object.create(Blob.prototype), props);
}

/**
* Constructor for JS consumers.
* Currently we only support creating Blobs from other Blobs.
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
*/
constructor(parts: Array<Blob>, options: any) {
let blobId = uuid.v4();
let size = 0;
parts.forEach((part) => {
invariant(part instanceof Blob, 'Can currently only create a Blob from other Blobs');
size += part.size;
});
BlobModule.createFromParts(parts, blobId);
return Blob.create({
blobId,
offset: 0,
size,
});
}

/*
* This method is used to create a new Blob object containing
* the data in the specified range of bytes of the source Blob.
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice
*/
slice(start?: number, end?: number): Blob {
let offset = this.offset;
let size = this.size;
if (typeof start === 'number') {
if (start > size) {
start = size;
}
offset += start;
size -= start;

if (typeof end === 'number') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to throw if it's not a number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remember if it throws on web. Will have to check and ensure we match the behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not throwing here because browsers don't throw in case of invalid type.

if (end < 0) {
end = this.size + end;
}
size = end - start;
}
}
return Blob.create({
blobId: this.blobId,
offset,
size,
});
}

/**
* This method is in the standard, but not actually implemented by
* any browsers at this point. It's important for how Blobs work in
* React Native, however, since we cannot de-allocate resources automatically,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could hook into JSC's finalizer callback to clean this up, but we currently don't have a good cross-platform approach to creating custom JS objects. It also would be an issue with the Websocket executor.

* so consumers need to explicitly de-allocate them.
*
* Note that the semantics around Blobs created via `blob.slice()`
* and `new Blob([blob])` are different. `blob.slice()` creates a
* new *view* onto the same binary data, so calling `close()` on any
* of those views is enough to deallocate the data, whereas
* `new Blob([blob, ...])` actually copies the data in memory.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good comment!

close() {
BlobModule.release(this.blobId);
}
}

module.exports = Blob;
22 changes: 22 additions & 0 deletions Libraries/Blob/BlobTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

export type BlobProps = {
blobId: string;
offset: number;
size: number;
type?: string;
};

export type FileProps = BlobProps & {
name: string;
lastModified: number;
};