Skip to content

Commit

Permalink
Merge pull request #60 from appcelerator/v3
Browse files Browse the repository at this point in the history
v3.0.0
  • Loading branch information
cb1kenobi committed Jun 23, 2020
2 parents fe639e0 + d14ea6f commit a85af2e
Show file tree
Hide file tree
Showing 18 changed files with 266 additions and 396 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,13 @@
# v3.0.0 (Jun 23, 2020)

* BREAKING CHANGE: Dropped support for Node.js 10.12.0. Please use Node.js 10.13.0 LTS or newer.
* BREAKING CHANGE: Removed syslog relay API. With iOS 10, syslog no longer returned app specific
messages. Then in iOS 13, starting the syslog relay service seems to not work reliably.
* fix: Fix assertion failure where async handle was being deleted before libuv had fully closed
the handle.
* fix: Flush pending debug log messages when error is thrown initializing relay.
* chore: Updated dependencies.

# v2.1.0 (Jun 9, 2020)

* feat: Added Electron compatibility.
Expand Down
2 changes: 1 addition & 1 deletion Jenkinsfile
Expand Up @@ -2,7 +2,7 @@
library 'pipeline-library'

runNPMPackage {
nodeVersions = [ '10.19.0', '12.16.1', '13.11.0' ]
nodeVersions = [ '10.19.0', '12.18.0', '14.4.0' ]
platformsWithLabels = [
'osx': 'xcode-9'
]
Expand Down
78 changes: 10 additions & 68 deletions README.md
@@ -1,15 +1,11 @@
# node-ios-device [![Build Status][3]][4] [![Greenkeeper badge][5]][6]
# node-ios-device

Queries connected iOS devices, installs apps, and relays log output.

## Prerequisites

`node-ios-device` only works on macOS 10.11 or newer and requires N-API version 3 and the following
Node.js versions:

* Node.js
* v8.12.x or newer
* v10.2.0 or newer
`node-ios-device` only works on macOS 10.11 or newer. It use N-API version 3 and requires Node.js
10.13.0 LTS or newer.

## Installation

Expand All @@ -23,16 +19,15 @@ $ node-ios-device
USAGE: node-ios-device <command> [options]
COMMANDS:
forward Connects to a port on an device and relays messages
install Install an app on the specified device
list Lists connected devices
syslog Outputs a devices syslog messages
watch Listens for devices to be connected/disconnected
forward Connects to a port on an device and relays messages
i, install Install an app on the specified device
ls, list, devices Lists connected devices
watch, track-devices Listens for devices to be connected/disconnected
GLOBAL OPTIONS:
--no-color Disable colors
-h,--help Displays the help screen
-v,--version Outputs the version
--no-color Disable colors
-h, --help Displays the help screen
-v, --version Outputs the version
```

## Example
Expand All @@ -55,12 +50,6 @@ handle.on('error', console.error);
iosDevice.install('<device udid>', '/path/to/my.app');
console.log('Success!');

// relay the syslog output to the console
iosDevice
.syslog('<device udid>')
.on('data', console.log)
.on('end', () => console.log('Device disconnected'));

// relay output from a TCP port created by an iOS app
iosDevice
.forward('<device udid>', 1337)
Expand All @@ -76,10 +65,6 @@ Retrieves an array of all connected iOS devices.

Returns an `Array` of device objects.

Note that only devices connected via a USB cable will be returned. Devices connected via Wi-Fi will
not be returned. The main reason we do this is because you can only relay the syslog from USB
connected devices.

Device objects contain the following information:

* `udid` - The device's unique device id (e.g. "a4cbe14c0441a2bf87f397602653a4ac71eb0336")
Expand Down Expand Up @@ -134,45 +119,6 @@ Currently, an `appPath` that begins with `~` is not supported.

The `appPath` must resolve to an iOS .app, not the .ipa file.

### `syslog(udid)`

Relays the syslog from the iOS device.

> Starting with iOS 10, the syslog no longer contains application specific output. If you want
> output for a specific app, then you will need to use a TCP socket. See
> [`forward()`](#forwardudid-port) for more info.
* `{String} udid` - The device udid

Returns a `Handle` instance that contains a `stop()` method to discontinue emitting messages.

> NOTE: `syslog()` only supports USB connected devices. Wi-Fi-only connected devices will not work.
#### Event: `'data'`

Emitted for each line of output. Empty lines are omitted.

- `{String} message` - The log message.

#### Event: 'end'

Emitted when the device is physically disconnected. Note that this does not unregister the internal
callback. You must manually call `handle.stop()` to cleanup.

#### Example:

```js
const handle = iosDevice
.syslog('<device udid>')
.on('data', console.log)
.on('end', () => console.log('End of syslog'));

setTimeout(function () {
// turn off logging after 1 minute
handle.stop();
}, 60000);
```

### `forward(udid, port)`

Relays messages from a server running on the device on the specified port.
Expand Down Expand Up @@ -234,7 +180,3 @@ in this distribution for more information.

[1]: https://github.com/appcelerator/node-ios-device/blob/master/LICENSE
[2]: https://www.npmjs.com/package/snooplogg
[3]: https://travis-ci.org/appcelerator/node-ios-device.svg?branch=master
[4]: https://travis-ci.org/appcelerator/node-ios-device
[5]: https://badges.greenkeeper.io/appcelerator/node-ios-device.svg
[6]: https://greenkeeper.io/
13 changes: 0 additions & 13 deletions bin/node-ios-device
Expand Up @@ -51,19 +51,6 @@ new CLI({
console.log(JSON.stringify(iosDevice.list(), null, ' '));
}
},
syslog: {
args: [
{ name: 'udid', desc: 'The iOS device\'s unique device identifier' }
],
desc: 'Outputs a devices syslog messages',
action({ argv }) {
return new Promise(resolve => {
iosDevice.syslog(selectDevice(argv.udid))
.on('data', console.log)
.on('end', resolve);
});
}
},
watch: {
aliases: 'track-devices',
desc: 'Listens for devices to be connected/disconnected',
Expand Down
8 changes: 4 additions & 4 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "node-ios-device",
"version": "2.1.0",
"version": "3.0.0",
"description": "Simple library for detecting and installing apps on iOS devices",
"main": "./src/index",
"gypfile": true,
Expand Down Expand Up @@ -34,21 +34,21 @@
"test": "JUNIT_REPORT_PATH=junit.xml mocha -r chai --reporter mocha-jenkins-reporter test/**/test-*.js"
},
"dependencies": {
"cli-kit": "^1.2.0",
"cli-kit": "^1.2.3",
"napi-macros": "^2.0.0",
"node-gyp-build": "^4.2.2",
"snooplogg": "^3.0.0"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^7.2.0",
"mocha": "^8.0.1",
"mocha-jenkins-reporter": "^0.4.3",
"prebuildify": "^4.0.0"
},
"homepage": "https://github.com/appcelerator/node-ios-device",
"bugs": "https://github.com/appcelerator/node-ios-device/issues",
"repository": "https://github.com/appcelerator/node-ios-device",
"engines": {
"node": "^8.12.0 || >=10.2.0"
"node": ">=10.13.0"
}
}
4 changes: 2 additions & 2 deletions src/device-interface.cpp
Expand Up @@ -6,8 +6,8 @@ namespace node_ios_device {
/**
* Initialzies the device interface.
*/
DeviceInterface::DeviceInterface(std::string& udid, am_device& dev) :
dev(dev), udid(udid), numConnections(0) {}
DeviceInterface::DeviceInterface(std::string& udid, am_device& dev, uint32_t type) :
dev(dev), type(type), udid(udid), numConnections(0) {}

/**
* Cleanup the device interface, namely disconnects and stops the active session.
Expand Down
3 changes: 2 additions & 1 deletion src/device-interface.h
Expand Up @@ -19,7 +19,7 @@ enum InterfaceType { USB, WiFi };
*/
class DeviceInterface {
public:
DeviceInterface(std::string& udid, am_device& dev);
DeviceInterface(std::string& udid, am_device& dev, uint32_t type);
~DeviceInterface();

void connect();
Expand All @@ -30,6 +30,7 @@ class DeviceInterface {
void startService(const char* serviceName, service_conn_t* connection);

am_device dev;
uint32_t type;

private:
std::string udid;
Expand Down
19 changes: 5 additions & 14 deletions src/device.cpp
Expand Up @@ -12,7 +12,6 @@ namespace node_ios_device {
*/
Device::Device(napi_env env, std::string& udid, am_device& dev, std::weak_ptr<CFRunLoopRef> runloop) :
portRelay(env, runloop),
syslogRelay(env, runloop),
env(env),
udid(udid) {

Expand Down Expand Up @@ -60,7 +59,7 @@ DeviceInterface* Device::config(am_device& dev, bool isAdd) {
if (type == 1) {
if (isAdd && !usb) {
LOG_DEBUG_1("Device::config", "Device %s connected via USB", udid.c_str())
usb = std::make_shared<DeviceInterface>(udid, dev);
usb = std::make_shared<DeviceInterface>(udid, dev, type);
return usb.get();
} else if (!isAdd && usb) {
LOG_DEBUG_1("Device::config", "Device %s disconnected via USB", udid.c_str())
Expand All @@ -69,14 +68,16 @@ DeviceInterface* Device::config(am_device& dev, bool isAdd) {
} else if (type == 2) {
if (isAdd && !wifi) {
LOG_DEBUG_1("Device::config", "Device %s connected via Wi-Fi", udid.c_str())
wifi = std::make_shared<DeviceInterface>(udid, dev);
wifi = std::make_shared<DeviceInterface>(udid, dev, type);
return wifi.get();
} else if (!isAdd && wifi) {
LOG_DEBUG_1("Device::config", "Device %s disconnected via Wi-Fi", udid.c_str())
wifi = nullptr;
}
} else {
throw std::runtime_error("Unknown device interface type");
std::stringstream error;
error << "Unknown device interface type " << type;
throw std::runtime_error(error.str());
}

return NULL;
Expand Down Expand Up @@ -107,16 +108,6 @@ void Device::install(std::string& appPath) {
}
}

/**
* Starts or stops syslog relaying.
*/
void Device::syslog(uint8_t action, napi_value listener) {
if (action == RELAY_START && !usb) {
throw std::runtime_error("syslog requires a USB connected iOS device");
}
syslogRelay.config(action, listener, usb);
}

/**
* Serialized the device info to a JavaScript object.
*/
Expand Down
3 changes: 0 additions & 3 deletions src/device.h
Expand Up @@ -15,7 +15,6 @@ namespace node_ios_device {
LOG_DEBUG_EXTERN_VARS

class PortRelay;
class SyslogRelay;

enum DevicePropType { Boolean, String };

Expand Down Expand Up @@ -44,15 +43,13 @@ class Device {
void forward(uint8_t action, napi_value nport, napi_value listener);
void install(std::string& appPath);
inline bool isDisconnected() const { return !usb && !wifi; }
void syslog(uint8_t action, napi_value listener);
napi_value toJS();

std::shared_ptr<DeviceInterface> usb;
std::shared_ptr<DeviceInterface> wifi;

private:
PortRelay portRelay;
SyslogRelay syslogRelay;
napi_env env;
std::string udid;
std::map<const char*, std::unique_ptr<DeviceProp>> props;
Expand Down
28 changes: 20 additions & 8 deletions src/deviceman.cpp
Expand Up @@ -9,16 +9,28 @@ DeviceMan::DeviceMan(napi_env env) :
env(env),
initialized(false),
initTimer(NULL),
runloop(NULL) {}
runloop(NULL) {

notifyChange = new uv_async_t;
}

/**
* Releases the async handle, unsubscribes from iOS device notifications, and stops the runloop.
*/
DeviceMan::~DeviceMan() {
LOG_DEBUG_THREAD_ID("DeviceMan::~DeviceMan", "Shutting down device manager")
::uv_close((uv_handle_t*)&notifyChange, NULL);

::uv_close(
(uv_handle_t*)notifyChange,
[](uv_handle_t* handle) {
delete (uv_async_t *)handle;
}
);

::AMDeviceNotificationUnsubscribe(deviceNotification);

stopInitTimer();

if (runloop) {
::CFRunLoopStop(*runloop);
runloop = NULL;
Expand All @@ -34,7 +46,7 @@ void DeviceMan::config(napi_value listener, WatchAction action) {
NAPI_THROW("DeviceMan::config", "ERROR_NAPI_CREATE_REFERENCE", ::napi_create_reference(env, listener, 1, &ref))

LOG_DEBUG("DeviceMan::config", "Adding listener")
::uv_ref((uv_handle_t*)&notifyChange);
::uv_ref((uv_handle_t*)notifyChange);
{
std::lock_guard<std::mutex> lock(listenersLock);
listeners.push_back(ref);
Expand All @@ -59,7 +71,7 @@ void DeviceMan::config(napi_value listener, WatchAction action) {

if (same) {
LOG_DEBUG("DeviceMan::config", "Removing listener")
::uv_unref((uv_handle_t*)&notifyChange);
::uv_unref((uv_handle_t*)notifyChange);
it = listeners.erase(it);
} else {
++it;
Expand Down Expand Up @@ -170,12 +182,12 @@ void DeviceMan::init() {
// block Node from exiting
uv_loop_t* loop;
::napi_get_uv_event_loop(env, &loop);
notifyChange.data = &self;
::uv_async_init(loop, &notifyChange, [](uv_async_t* handle) {
notifyChange->data = &self;
::uv_async_init(loop, notifyChange, [](uv_async_t* handle) {
std::shared_ptr<DeviceMan>* deviceman = static_cast<std::shared_ptr<DeviceMan>*>(handle->data);
(*deviceman)->dispatch();
});
::uv_unref((uv_handle_t*)&notifyChange);
::uv_unref((uv_handle_t*)notifyChange);

LOG_DEBUG_THREAD_ID("DeviceMan::init", "Starting background thread")
std::thread(&DeviceMan::run, this).detach();
Expand Down Expand Up @@ -253,7 +265,7 @@ void DeviceMan::onDeviceNotification(am_device_notification_callback_info* info)
// we need to notify if devices changed and this must be done outside the
// scopes above so that the mutex is unlocked
if (changed) {
::uv_async_send(&notifyChange);
::uv_async_send(notifyChange);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/deviceman.h
Expand Up @@ -40,7 +40,7 @@ class DeviceMan : public std::enable_shared_from_this<DeviceMan> {
std::shared_ptr<DeviceMan> self;

napi_env env;
uv_async_t notifyChange;
uv_async_t* notifyChange;

std::mutex deviceMutex;
std::map<std::string, std::shared_ptr<Device>> devices;
Expand Down

0 comments on commit a85af2e

Please sign in to comment.