mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-15 17:53:31 +01:00
[AltServer] Supports “remove app” requests
Improves support for removing apps
This commit is contained in:
@@ -39,7 +39,9 @@ typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError)
|
|||||||
ALTServerErrorInvalidAnisetteData = 13,
|
ALTServerErrorInvalidAnisetteData = 13,
|
||||||
ALTServerErrorPluginNotFound = 14,
|
ALTServerErrorPluginNotFound = 14,
|
||||||
|
|
||||||
ALTServerErrorProfileNotFound = 15
|
ALTServerErrorProfileNotFound = 15,
|
||||||
|
|
||||||
|
ALTServerErrorAppDeletionFailed = 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdenti
|
|||||||
|
|
||||||
case ALTServerErrorProfileNotFound:
|
case ALTServerErrorProfileNotFound:
|
||||||
return [self profileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not find profile", "")];
|
return [self profileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not find profile", "")];
|
||||||
|
|
||||||
|
case ALTServerErrorAppDeletionFailed:
|
||||||
|
return NSLocalizedString(@"An error occured while removing the app.", @"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public enum ServerRequest: Decodable
|
|||||||
case beginInstallation(BeginInstallationRequest)
|
case beginInstallation(BeginInstallationRequest)
|
||||||
case installProvisioningProfiles(InstallProvisioningProfilesRequest)
|
case installProvisioningProfiles(InstallProvisioningProfilesRequest)
|
||||||
case removeProvisioningProfiles(RemoveProvisioningProfilesRequest)
|
case removeProvisioningProfiles(RemoveProvisioningProfilesRequest)
|
||||||
|
case removeApp(RemoveAppRequest)
|
||||||
case unknown(identifier: String, version: Int)
|
case unknown(identifier: String, version: Int)
|
||||||
|
|
||||||
var identifier: String {
|
var identifier: String {
|
||||||
@@ -34,6 +35,7 @@ public enum ServerRequest: Decodable
|
|||||||
case .beginInstallation(let request): return request.identifier
|
case .beginInstallation(let request): return request.identifier
|
||||||
case .installProvisioningProfiles(let request): return request.identifier
|
case .installProvisioningProfiles(let request): return request.identifier
|
||||||
case .removeProvisioningProfiles(let request): return request.identifier
|
case .removeProvisioningProfiles(let request): return request.identifier
|
||||||
|
case .removeApp(let request): return request.identifier
|
||||||
case .unknown(let identifier, _): return identifier
|
case .unknown(let identifier, _): return identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,7 @@ public enum ServerRequest: Decodable
|
|||||||
case .beginInstallation(let request): return request.version
|
case .beginInstallation(let request): return request.version
|
||||||
case .installProvisioningProfiles(let request): return request.version
|
case .installProvisioningProfiles(let request): return request.version
|
||||||
case .removeProvisioningProfiles(let request): return request.version
|
case .removeProvisioningProfiles(let request): return request.version
|
||||||
|
case .removeApp(let request): return request.version
|
||||||
case .unknown(_, let version): return version
|
case .unknown(_, let version): return version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +88,10 @@ public enum ServerRequest: Decodable
|
|||||||
let request = try RemoveProvisioningProfilesRequest(from: decoder)
|
let request = try RemoveProvisioningProfilesRequest(from: decoder)
|
||||||
self = .removeProvisioningProfiles(request)
|
self = .removeProvisioningProfiles(request)
|
||||||
|
|
||||||
|
case "RemoveAppRequest":
|
||||||
|
let request = try RemoveAppRequest(from: decoder)
|
||||||
|
self = .removeApp(request)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
self = .unknown(identifier: identifier, version: version)
|
self = .unknown(identifier: identifier, version: version)
|
||||||
}
|
}
|
||||||
@@ -97,6 +104,7 @@ public enum ServerResponse: Decodable
|
|||||||
case installationProgress(InstallationProgressResponse)
|
case installationProgress(InstallationProgressResponse)
|
||||||
case installProvisioningProfiles(InstallProvisioningProfilesResponse)
|
case installProvisioningProfiles(InstallProvisioningProfilesResponse)
|
||||||
case removeProvisioningProfiles(RemoveProvisioningProfilesResponse)
|
case removeProvisioningProfiles(RemoveProvisioningProfilesResponse)
|
||||||
|
case removeApp(RemoveAppResponse)
|
||||||
case error(ErrorResponse)
|
case error(ErrorResponse)
|
||||||
case unknown(identifier: String, version: Int)
|
case unknown(identifier: String, version: Int)
|
||||||
|
|
||||||
@@ -107,6 +115,7 @@ public enum ServerResponse: Decodable
|
|||||||
case .installationProgress(let response): return response.identifier
|
case .installationProgress(let response): return response.identifier
|
||||||
case .installProvisioningProfiles(let response): return response.identifier
|
case .installProvisioningProfiles(let response): return response.identifier
|
||||||
case .removeProvisioningProfiles(let response): return response.identifier
|
case .removeProvisioningProfiles(let response): return response.identifier
|
||||||
|
case .removeApp(let response): return response.identifier
|
||||||
case .error(let response): return response.identifier
|
case .error(let response): return response.identifier
|
||||||
case .unknown(let identifier, _): return identifier
|
case .unknown(let identifier, _): return identifier
|
||||||
}
|
}
|
||||||
@@ -119,6 +128,7 @@ public enum ServerResponse: Decodable
|
|||||||
case .installationProgress(let response): return response.version
|
case .installationProgress(let response): return response.version
|
||||||
case .installProvisioningProfiles(let response): return response.version
|
case .installProvisioningProfiles(let response): return response.version
|
||||||
case .removeProvisioningProfiles(let response): return response.version
|
case .removeProvisioningProfiles(let response): return response.version
|
||||||
|
case .removeApp(let response): return response.version
|
||||||
case .error(let response): return response.version
|
case .error(let response): return response.version
|
||||||
case .unknown(_, let version): return version
|
case .unknown(_, let version): return version
|
||||||
}
|
}
|
||||||
@@ -155,6 +165,10 @@ public enum ServerResponse: Decodable
|
|||||||
let response = try RemoveProvisioningProfilesResponse(from: decoder)
|
let response = try RemoveProvisioningProfilesResponse(from: decoder)
|
||||||
self = .removeProvisioningProfiles(response)
|
self = .removeProvisioningProfiles(response)
|
||||||
|
|
||||||
|
case "RemoveAppResponse":
|
||||||
|
let response = try RemoveAppResponse(from: decoder)
|
||||||
|
self = .removeApp(response)
|
||||||
|
|
||||||
case "ErrorResponse":
|
case "ErrorResponse":
|
||||||
let response = try ErrorResponse(from: decoder)
|
let response = try ErrorResponse(from: decoder)
|
||||||
self = .error(response)
|
self = .error(response)
|
||||||
@@ -379,3 +393,28 @@ public struct RemoveProvisioningProfilesResponse: ServerMessageProtocol
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct RemoveAppRequest: ServerMessageProtocol
|
||||||
|
{
|
||||||
|
public var version = 1
|
||||||
|
public var identifier = "RemoveAppRequest"
|
||||||
|
|
||||||
|
public var udid: String
|
||||||
|
public var bundleIdentifier: String
|
||||||
|
|
||||||
|
public init(udid: String, bundleIdentifier: String)
|
||||||
|
{
|
||||||
|
self.udid = udid
|
||||||
|
self.bundleIdentifier = bundleIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RemoveAppResponse: ServerMessageProtocol
|
||||||
|
{
|
||||||
|
public var version = 1
|
||||||
|
public var identifier = "RemoveAppResponse"
|
||||||
|
|
||||||
|
public init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -273,6 +273,9 @@ private extension ConnectionManager
|
|||||||
case .success(.removeProvisioningProfiles(let request)):
|
case .success(.removeProvisioningProfiles(let request)):
|
||||||
self.handleRemoveProvisioningProfilesRequest(request, for: connection)
|
self.handleRemoveProvisioningProfilesRequest(request, for: connection)
|
||||||
|
|
||||||
|
case .success(.removeApp(let request)):
|
||||||
|
self.handleRemoveAppRequest(request, for: connection)
|
||||||
|
|
||||||
case .success(.unknown):
|
case .success(.unknown):
|
||||||
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
||||||
connection.send(response, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
@@ -485,7 +488,31 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
let response = RemoveProvisioningProfilesResponse()
|
let response = RemoveProvisioningProfilesResponse()
|
||||||
connection.send(response, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent remove profiles error response to \(connection) with result:", result)
|
print("Sent remove profiles success response to \(connection) with result:", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: ClientConnection)
|
||||||
|
{
|
||||||
|
ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in
|
||||||
|
if let error = error, !success
|
||||||
|
{
|
||||||
|
print("Failed to remove app \(request.bundleIdentifier):", error)
|
||||||
|
|
||||||
|
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||||
|
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||||
|
print("Sent remove a[[ error response with result:", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print("Removed app:", request.bundleIdentifier)
|
||||||
|
|
||||||
|
let response = RemoveAppResponse()
|
||||||
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
|
print("Sent remove app success response to \(connection) with result:", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification
|
|||||||
|
|
||||||
/* App Installation */
|
/* App Installation */
|
||||||
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet<NSString *> *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
- (void)removeProvisioningProfilesForBundleIdentifiers:(NSSet<NSString *> *)bundleIdentifiers fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <libimobiledevice/misagent.h>
|
#include <libimobiledevice/misagent.h>
|
||||||
|
|
||||||
void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid);
|
void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid);
|
||||||
|
void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid);
|
||||||
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data);
|
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data);
|
||||||
|
|
||||||
NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification = @"ALTDeviceManagerDeviceDidConnectNotification";
|
NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification = @"ALTDeviceManagerDeviceDidConnectNotification";
|
||||||
@@ -28,6 +29,8 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
|||||||
@interface ALTDeviceManager ()
|
@interface ALTDeviceManager ()
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, void (^)(NSError *)> *installationCompletionHandlers;
|
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, void (^)(NSError *)> *installationCompletionHandlers;
|
||||||
|
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, void (^)(NSError *)> *deletionCompletionHandlers;
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, NSProgress *> *installationProgress;
|
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, NSProgress *> *installationProgress;
|
||||||
@property (nonatomic, readonly) dispatch_queue_t installationQueue;
|
@property (nonatomic, readonly) dispatch_queue_t installationQueue;
|
||||||
|
|
||||||
@@ -54,8 +57,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
|||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
_installationCompletionHandlers = [NSMutableDictionary dictionary];
|
_installationCompletionHandlers = [NSMutableDictionary dictionary];
|
||||||
_installationProgress = [NSMutableDictionary dictionary];
|
_deletionCompletionHandlers = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
_installationProgress = [NSMutableDictionary dictionary];
|
||||||
_installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL);
|
_installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
_cachedDevices = [NSMutableSet set];
|
_cachedDevices = [NSMutableSet set];
|
||||||
@@ -498,6 +502,87 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)removeAppForBundleIdentifier:(NSString *)bundleIdentifier fromDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler
|
||||||
|
{
|
||||||
|
__block idevice_t device = NULL;
|
||||||
|
__block lockdownd_client_t client = NULL;
|
||||||
|
__block instproxy_client_t ipc = NULL;
|
||||||
|
__block lockdownd_service_descriptor_t service = NULL;
|
||||||
|
|
||||||
|
void (^finish)(NSError *error) = ^(NSError *e) {
|
||||||
|
__block NSError *error = e;
|
||||||
|
|
||||||
|
lockdownd_service_descriptor_free(service);
|
||||||
|
instproxy_client_free(ipc);
|
||||||
|
lockdownd_client_free(client);
|
||||||
|
idevice_free(device);
|
||||||
|
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
completionHandler(NO, error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completionHandler(YES, nil);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Find Device */
|
||||||
|
if (idevice_new(&device, udid.UTF8String) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Device */
|
||||||
|
if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Installation Proxy */
|
||||||
|
if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL)
|
||||||
|
{
|
||||||
|
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instproxy_client_new(device, service, &ipc) != INSTPROXY_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service)
|
||||||
|
{
|
||||||
|
lockdownd_service_descriptor_free(service);
|
||||||
|
service = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUUID *UUID = [NSUUID UUID];
|
||||||
|
__block char *uuidString = (char *)malloc(UUID.UUIDString.length + 1);
|
||||||
|
strncpy(uuidString, (const char *)UUID.UUIDString.UTF8String, UUID.UUIDString.length);
|
||||||
|
uuidString[UUID.UUIDString.length] = '\0';
|
||||||
|
|
||||||
|
self.deletionCompletionHandlers[UUID] = ^(NSError *error) {
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
NSString *localizedFailure = [NSString stringWithFormat:NSLocalizedString(@"Could not remove “%@”.", @""), bundleIdentifier];
|
||||||
|
|
||||||
|
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
|
||||||
|
userInfo[NSLocalizedFailureErrorKey] = localizedFailure;
|
||||||
|
|
||||||
|
NSError *localizedError = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
|
||||||
|
finish(localizedError);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finish(nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(uuidString);
|
||||||
|
};
|
||||||
|
|
||||||
|
instproxy_uninstall(ipc, bundleIdentifier.UTF8String, NULL, ALTDeviceManagerUpdateAppDeletionStatus, uuidString);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Provisioning Profiles -
|
#pragma mark - Provisioning Profiles -
|
||||||
|
|
||||||
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
|
- (void)installProvisioningProfiles:(NSSet<ALTProvisioningProfile *> *)provisioningProfiles toDeviceWithUDID:(NSString *)udid activeProvisioningProfiles:(nullable NSSet<NSString *> *)activeProvisioningProfiles completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
|
||||||
@@ -1117,6 +1202,43 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ALTDeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void *uuid)
|
||||||
|
{
|
||||||
|
NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:[NSString stringWithUTF8String:(const char *)uuid]];
|
||||||
|
|
||||||
|
char *statusName = NULL;
|
||||||
|
instproxy_status_get_name(status, &statusName);
|
||||||
|
|
||||||
|
char *errorName = NULL;
|
||||||
|
char *errorDescription = NULL;
|
||||||
|
uint64_t code = 0;
|
||||||
|
instproxy_status_get_error(status, &errorName, &errorDescription, &code);
|
||||||
|
|
||||||
|
if ([@(statusName) isEqualToString:@"Complete"] || code != 0 || errorName != NULL)
|
||||||
|
{
|
||||||
|
void (^completionHandler)(NSError *) = ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID];
|
||||||
|
if (completionHandler != nil)
|
||||||
|
{
|
||||||
|
if (code != 0 || errorName != NULL)
|
||||||
|
{
|
||||||
|
NSLog(@"Error removing app. %@ (%@). %@", @(code), @(errorName ?: ""), @(errorDescription ?: ""));
|
||||||
|
|
||||||
|
NSError *underlyingError = [NSError errorWithDomain:AltServerInstallationErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: @(errorDescription ?: "")}];
|
||||||
|
NSError *error = [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorAppDeletionFailed userInfo:@{NSUnderlyingErrorKey: underlyingError}];
|
||||||
|
|
||||||
|
completionHandler(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog(@"Finished removing app!");
|
||||||
|
completionHandler(nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALTDeviceManager.sharedManager.deletionCompletionHandlers[UUID] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data)
|
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data)
|
||||||
{
|
{
|
||||||
ALTDevice * (^deviceForUDID)(NSString *, NSArray<ALTDevice *> *) = ^ALTDevice *(NSString *udid, NSArray<ALTDevice *> *devices) {
|
ALTDevice * (^deviceForUDID)(NSString *, NSArray<ALTDevice *> *) = ^ALTDevice *(NSString *udid, NSArray<ALTDevice *> *devices) {
|
||||||
|
|||||||
Reference in New Issue
Block a user