Command Line Subcommands in Objective-C

Here’s the translation of the Go code to Objective-C, with explanations in Markdown format suitable for Hugo:

Our first program demonstrates how to create command-line subcommands, each with its own set of flags. This is similar to how tools like git have different subcommands (e.g., git commit, git push) with their own options.

#import <Foundation/Foundation.h>

@interface SubcommandParser : NSObject

@property (nonatomic, strong) NSString *subcommand;
@property (nonatomic, strong) NSMutableDictionary *flags;

- (instancetype)initWithSubcommand:(NSString *)subcommand;
- (void)addFlagWithName:(NSString *)name defaultValue:(id)defaultValue description:(NSString *)description;
- (void)parse:(NSArray *)arguments;
- (id)valueForFlag:(NSString *)flagName;
- (NSArray *)remainingArguments;

@end

@implementation SubcommandParser

- (instancetype)initWithSubcommand:(NSString *)subcommand {
    self = [super init];
    if (self) {
        _subcommand = subcommand;
        _flags = [NSMutableDictionary dictionary];
    }
    return self;
}

- (void)addFlagWithName:(NSString *)name defaultValue:(id)defaultValue description:(NSString *)description {
    self.flags[name] = @{@"value": defaultValue, @"description": description};
}

- (void)parse:(NSArray *)arguments {
    NSMutableArray *remaining = [NSMutableArray array];
    for (NSString *arg in arguments) {
        if ([arg hasPrefix:@"--"]) {
            NSArray *components = [arg componentsSeparatedByString:@"="];
            NSString *flagName = [components[0] substringFromIndex:2];
            id value = components.count > 1 ? components[1] : @YES;
            self.flags[flagName][@"value"] = value;
        } else {
            [remaining addObject:arg];
        }
    }
    self.flags[@"remaining"] = remaining;
}

- (id)valueForFlag:(NSString *)flagName {
    return self.flags[flagName][@"value"];
}

- (NSArray *)remainingArguments {
    return self.flags[@"remaining"];
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *args = [[NSProcessInfo processInfo] arguments];
        
        if (args.count < 2) {
            NSLog(@"expected 'foo' or 'bar' subcommands");
            return 1;
        }
        
        NSString *subcommand = args[1];
        
        if ([subcommand isEqualToString:@"foo"]) {
            SubcommandParser *fooCmd = [[SubcommandParser alloc] initWithSubcommand:@"foo"];
            [fooCmd addFlagWithName:@"enable" defaultValue:@NO description:@"enable"];
            [fooCmd addFlagWithName:@"name" defaultValue:@"" description:@"name"];
            [fooCmd parse:[args subarrayWithRange:NSMakeRange(2, args.count - 2)]];
            
            NSLog(@"subcommand 'foo'");
            NSLog(@"  enable: %@", [fooCmd valueForFlag:@"enable"]);
            NSLog(@"  name: %@", [fooCmd valueForFlag:@"name"]);
            NSLog(@"  tail: %@", [fooCmd remainingArguments]);
        } else if ([subcommand isEqualToString:@"bar"]) {
            SubcommandParser *barCmd = [[SubcommandParser alloc] initWithSubcommand:@"bar"];
            [barCmd addFlagWithName:@"level" defaultValue:@0 description:@"level"];
            [barCmd parse:[args subarrayWithRange:NSMakeRange(2, args.count - 2)]];
            
            NSLog(@"subcommand 'bar'");
            NSLog(@"  level: %@", [barCmd valueForFlag:@"level"]);
            NSLog(@"  tail: %@", [barCmd remainingArguments]);
        } else {
            NSLog(@"expected 'foo' or 'bar' subcommands");
            return 1;
        }
    }
    return 0;
}

To compile and run the program:

$ clang -framework Foundation command_line_subcommands.m -o command_line_subcommands

First, invoke the foo subcommand:

$ ./command_line_subcommands foo --enable --name=joe a1 a2
subcommand 'foo'
  enable: 1
  name: joe
  tail: (
    a1,
    a2
)

Now try bar:

$ ./command_line_subcommands bar --level=8 a1
subcommand 'bar'
  level: 8
  tail: (
    a1
)

But bar won’t accept foo’s flags:

$ ./command_line_subcommands bar --enable a1
subcommand 'bar'
  level: 0
  tail: (
    "--enable",
    a1
)

This example demonstrates how to implement command-line subcommands in Objective-C. We’ve created a SubcommandParser class to handle parsing of arguments for each subcommand. The main function checks which subcommand is invoked and processes the arguments accordingly.

Note that Objective-C doesn’t have built-in support for command-line argument parsing like Go’s flag package. In a real-world application, you might want to use a third-party library for more robust command-line argument parsing.