Wanting to understand how Java/C# interfaces are implemented in objective-c, I created this example based on the one I found here.

1. Create the protocol.

Interfaces in objective-c are called protocols. Here’s a protocol for printing.

Printing.h

#import <Foundation/Foundation.h>

@protocol Printing
-(void) print;
@end

@interface Printing : NSObject

@end

The protocol or interface (not be be confused with the other interface in this file) defines a single method – print.

2. Implement the protocol.

We’re going to implement this protocol two ways. One with a fraction and another with a complex number.

Fraction.h

#import <Foundation/NSObject.h>
#import "Printing.h"

@interface Fraction: NSObject <Printing> 

@property (nonatomic) int numerator;
@property (nonatomic) int denominator;

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;

@end

To say that we implement the protocol we put the protocol in after the objective-c interface definition.

@interface Fraction: NSObject <Printing> 

Then we do the actual implementation in the .m file.

Fraction.m

#import "Fraction.h"
#import <stdio.h>

@implementation Fraction

@synthesize numerator = _numerator;
@synthesize denominator = _denominator;

-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];
    
    if ( self ) {
        self.numerator = n;
        self.denominator = d;
   }
    
    return self;
}

-(void) print {
    printf( "%i/%i", self.numerator, self.denominator );
}

@end

Complex.h

#import <Foundation/NSObject.h>
#import "Printing.h"

@interface Complex: NSObject <Printing> 

@property (nonatomic) double real;
@property (nonatomic) double imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i;

@end

Complex.m

#import "Complex.h"
#import <stdio.h>

@implementation Complex

@synthesize real = _real;
@synthesize imaginary = _imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i {
    self = [super init];
    
    if ( self ) {
        self.real = r;
        self.imaginary = i;
    }
    
    return self;
}

-(void) print {
    printf( "%_f + %_fi", self.real, self.imaginary );
}

@end

3. Test.

Then to use the protocol as a fraction or a complex number we do the following:

SpikeProtocolTests.m

#import "SpikeProtocolTests.h"
#import "Fraction.h"
#import "Complex.h"

@implementation SpikeProtocolTests

- (void)testExample
{
    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
  
    id <Printing> printable;
    
    // print it
    printable = frac;
    printf( "The fraction is: " );
    [printable print];
    printf( "\n" );
    
    // print complex
    printable = comp;
    printf( "The complex number is: " );
    [printable print];
    printf( "\n" );
}

@end

The magic pretty much happens here:

    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
  
    id <Printing> printable;

We define our objects (which implement the interface) and a variable id that implements the interface. The reason this object is of type id is so we can implement multiple interfaces (id is a reserved object type in objective-c meaning any object).

Then to switch implementations we just change implementations:

 printable = frac;
 printable = comp;

The output should look something like this:

For instructions on how to see the unit test output in Xcode look here.