Skip to content

Commit

Permalink
Fixes #41: Function to check whether app is installed on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
baltpeter committed Mar 13, 2023
1 parent b7350fd commit a06f3ef
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 15 deletions.
22 changes: 11 additions & 11 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ A supported attribute for the `getDeviceAttribute()` function, depending on the

#### Defined in

[index.ts:327](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L327)
[index.ts:325](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L325)

___

Expand All @@ -77,7 +77,7 @@ The options for each attribute available through the `getDeviceAttribute()` func

#### Defined in

[index.ts:333](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L333)
[index.ts:331](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L331)

___

Expand All @@ -89,7 +89,7 @@ An ID of a known permission on iOS.

#### Defined in

[ios.ts:340](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L340)
[ios.ts:349](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L349)

___

Expand Down Expand Up @@ -120,7 +120,7 @@ Functions that are available for the platforms.
| `getPrefs` | (`appId`: `string`) => `Promise`<`Record`<`string`, `unknown`\> \| `undefined`\> | Get the preferences (`SharedPreferences` on Android, `NSUserDefaults` on iOS) of the app with the given app ID. Requires the `frida` capability on Android and iOS. |
| `installApp` | (`appPath`: `string`) => `Promise`<`void`\> | Install the app at the given path. **`Todo`** How to handle split APKs on Android (#4)? |
| `installCertificateAuthority` | (`path`: `string`) => `Promise`<`void`\> | Install the certificate authority with the given path as a trusted CA on the device. This allows you to intercept and modify traffic from apps on the device. On Android, this installs the CA as a system CA. As this is normally not possible on Android 10 and above, it overlays the `/system/etc/security/cacerts` directory with a tmpfs and installs the CA there. This means that the changes are not persistent across reboots. On iOS, the CA is installed permanently as a root certificate in the Certificate Trust Store. It persists across reboots.\ **Currently, you need to manually trust any CA at least once on the device, CAs can be added but not automatically marked as trusted (see: https://github.com/tweaselORG/appstraction/issues/44#issuecomment-1466151197).** Requires the `root` capability on Android, and the `ssh` capability on iOS. |
| `isAppInstalled` | `Platform` extends ``"android"`` ? (`appId`: `string`) => `Promise`<`boolean`\> : `never` | Check whether the app with the given app ID is installed. Currently only supported on Android. **`Param`** The app ID of the app to check. |
| `isAppInstalled` | (`appId`: `string`) => `Promise`<`boolean`\> | Check whether the app with the given app ID is installed. |
| `removeCertificateAuthority` | (`path`: `string`) => `Promise`<`void`\> | Remove the certificate authority with the given path from the trusted CAs on the device. On Android, this works for system CAs, including those pre-installed with the OS. As this is normally not possible on Android 10 and above, it overlays the `/system/etc/security/cacerts` directory with a tmpfs and removes the CA there. This means that the changes are not persistent across reboots. On iOS, this only works for CAs in the Certificate Trust Store. It does not work for pre-installed OS CAs. The changes are persistent across reboots. Requires the `root` capability on Android, and the `ssh` capability on iOS. |
| `resetDevice` | `Platform` extends ``"android"`` ? `RunTarget` extends ``"emulator"`` ? (`snapshotName`: `string`) => `Promise`<`void`\> : `never` : `never` | Reset the device to the specified snapshot (only available for emulators). **`Param`** The name of the snapshot to reset to. |
| `setAppBackgroundBatteryUsage` | `Platform` extends ``"android"`` ? (`appId`: `string`, `state`: ``"unrestricted"`` \| ``"optimized"`` \| ``"restricted"``) => `Promise`<`void`\> : `never` | Configure whether the app's background battery usage should be restricted. Currently only supported on Android. **`Param`** The app ID of the app to configure the background battery usage settings for. **`Param`** The state to set the background battery usage to. On Android, the possible values are: - `unrestricted`: "Allow battery usage in background without restrictions. May use more battery." - `optimized`: "Optimize based on your usage. Recommend for most apps." (default after installation) - `restricted`: "Restrict battery usage while in background. Apps may not work as expected. Notifications may be delayed." |
Expand Down Expand Up @@ -153,7 +153,7 @@ The options for the `platformApi()` function.

#### Defined in

[index.ts:265](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L265)
[index.ts:263](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L263)

___

Expand All @@ -172,7 +172,7 @@ Connection details for a proxy.

#### Defined in

[index.ts:341](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L341)
[index.ts:339](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L339)

___

Expand Down Expand Up @@ -202,7 +202,7 @@ The options for a specific platform/run target combination.

#### Defined in

[index.ts:292](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L292)
[index.ts:290](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L290)

___

Expand All @@ -220,7 +220,7 @@ A capability for the `platformApi()` function.

#### Defined in

[index.ts:320](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L320)
[index.ts:318](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L318)

___

Expand Down Expand Up @@ -262,7 +262,7 @@ Configuration string for WireGuard.

#### Defined in

[index.ts:348](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L348)
[index.ts:346](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L346)

## Variables

Expand All @@ -286,7 +286,7 @@ The IDs of known permissions on iOS.

#### Defined in

[ios.ts:323](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L323)
[ios.ts:332](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L332)

## Functions

Expand Down Expand Up @@ -366,4 +366,4 @@ The API object for the given platform and run target.

#### Defined in

[index.ts:357](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L357)
[index.ts:355](https://github.com/tweaselORG/appstraction/blob/main/src/index.ts#L355)
2 changes: 2 additions & 0 deletions examples/ios-device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { parseAppMeta, pause, platformApi } from '../src/index';
const appId = appMeta.id;
console.log('App:', appId, '@', appMeta.version);

console.log('Installed already?', await ios.isAppInstalled(appId));

await ios.installApp(appPath);
// First, grant all permissions.
await ios.setAppPermissions(appId);
Expand Down
4 changes: 1 addition & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,11 @@ export type PlatformApi<
/**
* Check whether the app with the given app ID is installed.
*
* Currently only supported on Android.
*
* @param appId The app ID of the app to check.
*
* @returns Whether the app is installed.
*/
isAppInstalled: Platform extends 'android' ? (appId: string) => Promise<boolean> : never;
isAppInstalled: (appId: string) => Promise<boolean>;
/**
* Install the app at the given path.
*
Expand Down
11 changes: 10 additions & 1 deletion src/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,16 @@ export const iosApi = <RunTarget extends SupportedRunTarget<'ios'>>(
},
clearStuckModals: asyncUnimplemented('clearStuckModals') as never,

isAppInstalled: asyncUnimplemented('isAppInstalled') as never,
isAppInstalled: async (appId) => {
const { stdout } = await execa('ideviceinstaller', ['-l', '-o', 'list_all']);
return (
stdout
.split('\n')
// The first line is the header.
.slice(1)
.some((l) => l.startsWith(`${appId},`))
);
},
// We're using `libimobiledevice` instead of `cfgutil` because the latter doesn't wait for the app to be fully
// installed before exiting.
installApp: async (ipaPath) => {
Expand Down

0 comments on commit a06f3ef

Please sign in to comment.