blob: 191ddd4f63929d24ada90fc7e54df134a3914da7 [file] [log] [blame]
//
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "Service/Sources/NSKeyedArchiver+EDOAdditions.h"
#import <objc/runtime.h>
#import "Service/Sources/EDOClientService.h"
#import "Service/Sources/EDOServiceError.h"
#import "Service/Sources/EDOServiceException.h"
NS_ASSUME_NONNULL_BEGIN
/** The delegate used for encoding eDO outgoing parameters. */
@interface EDOKeyedArchiverDelegate : NSObject <NSKeyedArchiverDelegate>
/** Initializes the object with the root object that is going to be encoded. */
- (instancetype)initWithRootObject:(id)rootObject NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
@implementation EDOKeyedArchiverDelegate {
/** The object that is encoded by the attached NSKeyedArchiver of this delegate. */
id _rootObject;
}
- (instancetype)initWithRootObject:(id)rootObject {
self = [super init];
if (self) {
_rootObject = rootObject;
}
return self;
}
#pragma mark - NSKeyedArchiverDelegate
- (nullable id)archiver:(NSKeyedArchiver *)archiver willEncodeObject:(id)object {
if (!EDOIsRemoteObject(object) &&
[object respondsToSelector:@selector(EDOCheckEncodingConformance:)]) {
NSError *error;
if (![object EDOCheckEncodingConformance:&error]) {
[self raiseEncodingConformanceError:error withEncodingObject:object];
}
}
return object;
}
#pragma mark - Private
/**
* Throws an exception for the failed NSCoding conformance check during the encoding procedure.
*
* @param error The error that contains the underlying reason that encoding will fail. The error
* reason must be included in the `EDOErrorEncodingFailureReasonKey` of the
* `userInfo`.
* @param object The object that will cause the encoding failure.
*/
- (void)raiseEncodingConformanceError:(NSError *)error withEncodingObject:(id)object {
NSString *reason = [NSString
stringWithFormat:
@"eDO fails to encode a parameter which is sent for remote invocation. Please check if "
@"the parameter fully conforming to NSCoding.\n\nThe parameter is: %@\nDetail: %@",
[_rootObject description], error.userInfo[EDOErrorEncodingFailureReasonKey]];
[[NSException exceptionWithName:EDOTypeEncodingException reason:reason userInfo:nil] raise];
}
/** @return Awalys @c NO because this class doesn't conform to NSCoding. */
- (BOOL)EDOCheckEncodingConformance:(NSError **)error {
return NO;
}
@end
@implementation NSKeyedArchiver (EDOAdditions)
+ (NSData *)edo_archivedDataWithObject:(id)object {
// In Xcode 10.0, we can use the newer APIs.
#if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400) || \
(defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 120000) || \
(defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED >= 120000) || \
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000)
if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) {
// Use instancesRespondToSelector check because it's possible that something loads this on a
// lower iOS version than what it was built with, in which case the availability macros alone
// fail to protect it.
if ([NSKeyedArchiver instancesRespondToSelector:@selector(initRequiringSecureCoding:)]) {
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:NO];
EDOKeyedArchiverDelegate *delegate =
[[EDOKeyedArchiverDelegate alloc] initWithRootObject:object];
archiver.delegate = delegate;
[archiver encodeObject:object forKey:NSKeyedArchiveRootObjectKey];
[archiver finishEncoding];
return archiver.encodedData;
}
}
#endif
return [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:nil];
}
@end
NS_ASSUME_NONNULL_END