Search code examples
iosobjective-cnetwork-programmingencapsulation

How to maintain proper encapsulation for singleton created with data


I have a singleton networking class as well as a singleton object that needs to persist throughout my app. The singleton is initialized based on data retrieved from a web call, so right now my code works, and I have the following in my singleton networking class:

- (void)initializeObjectWithSuccess:(void (^)(BOOL))success
                                 failure:(void (^)(NSError *error))failure {
[self.HTTPClient postPath:[NSString stringWithFormat:@"users/%@/", [CPUser sharedUser].name parameters:[self createParameters] success:^(AFHTTPRequestOperation *operation, id responseObject) {
    id json = [NSJSONSerialization JSONObjectWithData:responseObject
                                              options:NSJSONReadingAllowFragments
                                                error:nil];

    [[CPList sharedList] setIdentifier:json[@"id"]];
    [[CPList sharedList] setImages:json[@"images"]];
    if (success) {
        success(YES);
    }

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    failure(error);
}];
}

I don't know how to initialize all the properties I need on my singleton CPList without setting them within this method, however I know that this is not proper encapsulation because the CPRequestManager Class should know nothing about the CPList Class


Solution

  • If your issue is that you don't want this class to know the name of CPList and the detail that it's a singleton and that it can access it with +[CPList sharedInstance] then you can just pass in an object that conforms to a protocol. This basically moves the knowledge of the singleton somewhere else

    - (void)initializeObjectWithList:(id<CPList>)list
                             success:(void (^)(BOOL))success
                             failure:(void (^)(NSError *error))failure;
    {
      [self.HTTPClient postPath:[NSString stringWithFormat:@"users/%@/", [CPUser sharedUser].name parameters:[self createParameters] success:^(AFHTTPRequestOperation *operation, id responseObject) {
          id json = [NSJSONSerialization JSONObjectWithData:responseObject
                                                    options:NSJSONReadingAllowFragments
                                                      error:nil];
    
    
          [list setIdentifier:json[@"id"]];
          [list setImages:json[@"images"]];
          if (success) {
              success(YES);
          }
    
      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          failure(error);
      }];
    }
    

    Or you could remove all knowledge that there is a "list" and just have this method return the actual data and then the caller can set it on the list

    - (void)initializeObjectWithSuccess:(void (^)(NSString *ID, NSArray *images))success
                                failure:(void (^)(NSError *error))failure;
    {
      [self.HTTPClient postPath:[NSString stringWithFormat:@"users/%@/", [CPUser sharedUser].name parameters:[self createParameters] success:^(AFHTTPRequestOperation *operation, id responseObject) {
          id json = [NSJSONSerialization JSONObjectWithData:responseObject
                                                    options:NSJSONReadingAllowFragments
                                                      error:nil];
    
          if (success) {
              success(json[@"id"], json[@"images"]);
          }
    
      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          failure(error);
      }];
    }
    

    Without any further context it's hard to suggest structural changes but here's two potential refactoring that might get you thinking about what you coudld do