0

So despite my having about zero experience in Xcode, and iOS development, I was asked to try to update some config string in an Xcode project that was an app deployed to the iOS marketplace some time ago. In theory it all should just work, but of course it doesn't.

When I build it complains about a class not conforming to protocol NSCopying and it has an option to add a stub to fix. enter image description here

But that just adds something to the end of the file and i don't know what it is wanting to be in this stub to get it to stop failing.

- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    code
}

And the code part is added there by default, which you probably already know.

I'd appreciate any advise on what this is expecting. I suspect there may be other factors for why the build succeeds but the app crashes and points to this line. I don't know when the app was made, what version it was supposed to be targeting or what. I just have an xcode project in the last known state. here is the original ParseClientConfiguration class:

#import "ParseClientConfiguration.h"
#import "ParseClientConfiguration_Private.h"

#import "PFAssert.h"
#import "PFApplication.h"
#import "PFCommandRunningConstants.h"
#import "PFFileManager.h"
#import "PFHash.h"
#import "PFObjectUtilities.h"

NSString *const _ParseDefaultServerURLString = @"https://api.somewhere.com/1";

@implementation ParseClientConfiguration

///--------------------------------------
#pragma mark - Init
///--------------------------------------

+ (instancetype)emptyConfiguration {
    return [[super alloc] initEmpty];
}

- (instancetype)initEmpty {
    self = [super init];
    if (!self) return nil;

    _networkRetryAttempts = PFCommandRunningDefaultMaxAttemptsCount;
    _URLSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    _server = [_ParseDefaultServerURLString copy];

    return self;
}

- (instancetype)initWithBlock:(void (^)(id<ParseMutableClientConfiguration>))configurationBlock {
    self = [self initEmpty];
    if (!self) return nil;

    configurationBlock(self);

    PFParameterAssert(self.applicationId.length, @"`applicationId` should not be nil.");

    return self;
}

+ (instancetype)configurationWithBlock:(void (^)(id<ParseMutableClientConfiguration>))configurationBlock {
    return [[self alloc] initWithBlock:configurationBlock];
}

///--------------------------------------
#pragma mark - Properties
///--------------------------------------

- (void)setApplicationId:(NSString *)applicationId {
    PFParameterAssert(applicationId.length, @"'applicationId' should not be nil.");
    _applicationId = [applicationId copy];
}

- (void)setClientKey:(NSString *)clientKey {
    _clientKey = [clientKey copy];
}

- (void)setServer:(NSString *)server {
    PFParameterAssert(server.length, @"Server should not be `nil`.");
    PFParameterAssert([NSURL URLWithString:server], @"Server should be a valid URL.");
    _server = [server copy];
}

- (void)setApplicationGroupIdentifier:(NSString *)applicationGroupIdentifier {
    PFParameterAssert(applicationGroupIdentifier == nil ||
                      [PFFileManager isApplicationGroupContainerReachableForGroupIdentifier:applicationGroupIdentifier],
                      @"ApplicationGroupContainer is unreachable. Please double check your Xcode project settings.");

    _applicationGroupIdentifier = [applicationGroupIdentifier copy];
}

- (void)setContainingApplicationBundleIdentifier:(NSString *)containingApplicationBundleIdentifier {
    PFParameterAssert([PFApplication currentApplication].extensionEnvironment,
                      @"'containingApplicationBundleIdentifier' cannot be set in non-extension environment");
    PFParameterAssert(containingApplicationBundleIdentifier.length,
                      @"'containingApplicationBundleIdentifier' should not be nil.");

    _containingApplicationBundleIdentifier = containingApplicationBundleIdentifier;
}

- (void)_resetDataSharingIdentifiers {
    _applicationGroupIdentifier = nil;
    _containingApplicationBundleIdentifier = nil;
}

///--------------------------------------
#pragma mark - NSObject
///--------------------------------------

- (NSUInteger)hash {
    return PFIntegerPairHash(self.applicationId.hash, self.clientKey.hash);
}

- (BOOL)isEqual:(id)object {
    if (![object isKindOfClass:[ParseClientConfiguration class]]) {
        return NO;
    }

    ParseClientConfiguration *other = object;
    return ([PFObjectUtilities isObject:self.applicationId equalToObject:other.applicationId] &&
            [PFObjectUtilities isObject:self.clientKey equalToObject:other.clientKey] &&
            [self.server isEqualToString:other.server] &&
            self.fileUploadController == other.fileUploadController &&
            self.localDatastoreEnabled == other.localDatastoreEnabled &&
            [PFObjectUtilities isObject:self.applicationGroupIdentifier equalToObject:other.applicationGroupIdentifier] &&
            [PFObjectUtilities isObject:self.containingApplicationBundleIdentifier equalToObject:other.containingApplicationBundleIdentifier] &&
            [PFObjectUtilities isObject:self.URLSessionConfiguration equalToObject:other.URLSessionConfiguration] &&
            self.networkRetryAttempts == other.networkRetryAttempts);
}

///--------------------------------------
#pragma mark - NSCopying
///--------------------------------------

- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    code
}

@end

Thanks for looking. By this point I pretty much expect to hand it back to them and say look, hire an iOS developer. But if I can get it working that would be interesting. I'm kind of curious what this app did.

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
Kai Qing
  • 18,793
  • 5
  • 39
  • 57
  • see this; https://stackoverflow.com/questions/4089238/implementing-nscopying – john elemans Mar 01 '21 at 18:18
  • @johnelemans Thanks for the link. I have seen it but I think I am more or less confused by the concept of the class properties since the one I am dealing with doesn't define them the way the example does. I don't work with swift, or objective c so its relatively foreign to me. I am just hesitant to radically change the structure of the code since they claim it was working before handing the source to me. I was just updating an api end point string in the config, which doesnt require knowledge of the language to do. I do have the option of returning the request and saying I can't do it. No harm – Kai Qing Mar 01 '21 at 20:32

1 Answers1

1

First of all, NSCopying protocol (if you're closer to Java than Objective-C, read 'protocol' as 'interface' instead) is exactly what it says in the name: requires an object that conforms (read 'implements') it - ParseClientConfiguration - to have a specific method copyWithZone:. Typically, here you would create another instance of this same object - ParseClientConfiguration - and copy all the properties' values from 'self' (aka 'this') to this newly created instance and return it at the end of this func. So it would look like this:

- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    ParseClientConfiguration *copy = [ParseClientConfiguration allocWithZone:zone];
    copy.property1 = self.property1;
    copy.property2 = self.property2;
    //...
    
    return copy;
}

But frankly, this might not even be the problem why the app crashes. Xcode highlights you in red the line with @implementation ParseClientConfiguration cause smth went wrong with this class in a runtime in general, while the warning highlighted in yellow by compiler (notice the diff) just warns you that potentially you should have done it and there is a risk that this might cause the app to behave in other way than expected.

To find the line that really must have caused a crash try adding AllExceptions breakpoint in Breakpoint Navigator and run again. Chances are, you'll see a different line highlighted in red this time! enter image description here

Dharman
  • 30,962
  • 25
  • 85
  • 135
roxanneM
  • 853
  • 1
  • 7
  • 12
  • Thanks. I'll see if I can adjust and confirm from your suggestions. – Kai Qing Mar 01 '21 at 18:44
  • I added the exception breakpoint and more or less it looks like it is pointing back to this class. [ParseClientConfiguration copyWithZone:]: unrecognized selector sent to instance 0x600002100190 - clearly that thing has something to do with all of this but I am fairly limited to what I can do to help them here. If it doesn't just work, I think it's best to just hand it back to them. Thanks – Kai Qing Mar 01 '21 at 19:22