Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Lang Objc Dev

ASecurity

Foundational Objective-C patterns covering classes, protocols, categories, memory management (ARC/retain-release), blocks, GCD, and Foundation framework. Use when writing Objective-C code, working with Cocoa/Cocoa Touch APIs, bridging to Swift, or needing guidance on Apple platform development patterns. This is the entry point for Objective-C development.

8 stars
0 votes
0 copies
0 views
Added 2/8/2026
developmentgorubyswiftbashreactexpresstestinggitapidocumentation

Works with

api

Security Analysis

A100/100

Scanned 2/12/2026

$npx -y skills add aRustyDev/ai --skill lang-objc-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lang Objc Dev?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Lang Objc Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-lang-objc-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-lang-objc-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: lang-objc-dev
description: Foundational Objective-C patterns covering classes, protocols, categories, memory management (ARC/retain-release), blocks, GCD, and Foundation framework. Use when writing Objective-C code, working with Cocoa/Cocoa Touch APIs, bridging to Swift, or needing guidance on Apple platform development patterns. This is the entry point for Objective-C development.
---

# Objective-C Fundamentals

Foundational Objective-C patterns and core language features. This skill serves as both a reference for common patterns and an index to specialized Objective-C skills.

## Overview

```
┌─────────────────────────────────────────────────────────────────┐
│                  Objective-C Skill Hierarchy                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                  ┌─────────────────────┐                        │
│                  │   lang-objc-dev     │ ◄── You are here       │
│                  │   (foundation)      │                        │
│                  └──────────┬──────────┘                        │
│                             │                                   │
│            ┌────────────────┼────────────────┐                  │
│            │                │                │                  │
│            ▼                ▼                ▼                  │
│    ┌──────────────┐  ┌──────────┐   ┌──────────────┐          │
│    │   patterns   │  │ library  │   │    swift     │          │
│    │     -dev     │  │   -dev   │   │   -bridge    │          │
│    └──────────────┘  └──────────┘   └──────────────┘          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

**This skill covers:**
- Class declaration and implementation
- Protocols (formal and informal)
- Categories and extensions
- Memory management (ARC, retain/release cycles)
- Properties and accessors
- Blocks and closures
- Grand Central Dispatch (GCD) basics
- Foundation framework essentials
- Common Objective-C idioms

**This skill does NOT cover (see specialized skills):**
- Advanced design patterns → `lang-objc-patterns-dev`
- Framework/library development → `lang-objc-library-dev`
- Swift/Objective-C interoperability → `lang-swift-objc-bridge-dev`
- iOS/macOS UI development → platform-specific skills
- Core Data patterns → `ios-coredata-dev`

---

## Quick Reference

| Task | Syntax |
|------|--------|
| Declare class | `@interface ClassName : Superclass` |
| Define method | `- (ReturnType)methodName:(Type)param` |
| Class method | `+ (ReturnType)methodName` |
| Property | `@property (attributes) Type *name;` |
| Protocol | `@protocol ProtocolName` |
| Category | `@interface ClassName (CategoryName)` |
| Block type | `ReturnType (^blockName)(ParamTypes)` |
| Strong reference | `@property (strong) Type *obj;` |
| Weak reference | `@property (weak) Type *obj;` |
| Copy property | `@property (copy) NSString *str;` |

---

## Skill Routing

Use this table to find the right specialized skill:

| When you need to... | Use this skill |
|---------------------|----------------|
| Implement MVC, Singleton, Delegate patterns | `lang-objc-patterns-dev` |
| Build reusable frameworks, static libraries | `lang-objc-library-dev` |
| Bridge Objective-C code to Swift | `lang-swift-objc-bridge-dev` |
| Work with UIKit/AppKit | Platform-specific UI skills |
| Implement Core Data models | `ios-coredata-dev` |

---

## Classes and Objects

### Class Declaration

```objc
// Header file (.h)
#import <Foundation/Foundation.h>

@interface Person : NSObject

// Properties
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

// Instance methods
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (void)greet;
- (NSString *)description;

// Class methods
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age;

@end
```

### Class Implementation

```objc
// Implementation file (.m)
#import "Person.h"

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = name;
        _age = age;
    }
    return self;
}

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {
    return [[self alloc] initWithName:name age:age];
}

- (void)greet {
    NSLog(@"Hello, my name is %@", self.name);
}

- (NSString *)description {
    return [NSString stringWithFormat:@"Person(name: %@, age: %ld)",
            self.name, (long)self.age];
}

@end
```

### Creating Instances

```objc
// Using designated initializer
Person *person1 = [[Person alloc] initWithName:@"Alice" age:30];

// Using convenience class method
Person *person2 = [Person personWithName:@"Bob" age:25];

// Calling methods
[person1 greet];
NSLog(@"%@", person1.description);

// Accessing properties
NSString *name = person1.name;
person1.age = 31;
```

---

## Properties and Accessors

### Property Attributes

```objc
@interface User : NSObject

// Memory management
@property (strong) NSObject *strongRef;      // Retains object (default)
@property (weak) id<Delegate> delegate;      // Weak reference, no retain
@property (assign) NSInteger count;          // Simple assignment (scalars)
@property (copy) NSString *username;         // Creates copy on assignment

// Atomicity
@property (atomic) NSString *threadSafe;     // Thread-safe (default, slower)
@property (nonatomic) NSString *fast;        // Not thread-safe (faster)

// Read/write
@property (readonly) NSString *identifier;   // No setter generated
@property (readwrite) NSString *editable;    // Both getter/setter (default)

// Custom accessors
@property (getter=isEnabled) BOOL enabled;   // Custom getter name
@property (setter=setCustomValue:) id value; // Custom setter name

@end
```

### Custom Getters and Setters

```objc
@implementation User {
    NSString *_computedProperty;
}

// Custom getter
- (NSString *)computedProperty {
    if (!_computedProperty) {
        _computedProperty = [self calculateValue];
    }
    return _computedProperty;
}

// Custom setter with validation
- (void)setUsername:(NSString *)username {
    if (username.length > 0) {
        _username = [username copy];
    }
}

@end
```

### Property Synthesis

```objc
@implementation User

// Automatic synthesis (Xcode does this automatically)
// @synthesize username = _username;

// Manual backing variable name
@synthesize customName = _backingVariable;

// Dynamic property (implemented at runtime)
@dynamic runtimeProperty;

@end
```

---

## Protocols

### Defining Protocols

```objc
// Formal protocol
@protocol Drawable <NSObject>

@required  // Must be implemented
- (void)draw;
- (CGRect)bounds;

@optional  // Can be implemented
- (void)setColor:(UIColor *)color;
- (UIColor *)color;

@end
```

### Adopting Protocols

```objc
// Header: Adopt protocol
@interface Circle : NSObject <Drawable>
@property (nonatomic, assign) CGFloat radius;
@end

// Implementation
@implementation Circle

- (void)draw {
    // Drawing implementation
    NSLog(@"Drawing circle with radius %f", self.radius);
}

- (CGRect)bounds {
    return CGRectMake(0, 0, self.radius * 2, self.radius * 2);
}

// Optional method
- (void)setColor:(UIColor *)color {
    // Optional color implementation
}

@end
```

### Protocol Types and Conformance

```objc
// Variable can hold any object conforming to Drawable
id<Drawable> drawable = [[Circle alloc] init];

// Check protocol conformance
if ([object conformsToProtocol:@protocol(Drawable)]) {
    [(id<Drawable>)object draw];
}

// Check if optional method is implemented
if ([drawable respondsToSelector:@selector(setColor:)]) {
    [drawable setColor:[UIColor blueColor]];
}
```

### Multiple Protocol Adoption

```objc
@interface AdvancedShape : NSObject <Drawable, Serializable, Comparable>
@end
```

---

## Categories and Extensions

### Categories (Public)

```objc
// Header: NSString+Validation.h
@interface NSString (Validation)
- (BOOL)isValidEmail;
- (BOOL)isValidURL;
@end

// Implementation: NSString+Validation.m
@implementation NSString (Validation)

- (BOOL)isValidEmail {
    NSString *pattern = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
    return [predicate evaluateWithObject:self];
}

- (BOOL)isValidURL {
    return [NSURL URLWithString:self] != nil;
}

@end

// Usage
#import "NSString+Validation.h"
NSString *email = @"user@example.com";
if ([email isValidEmail]) {
    NSLog(@"Valid email");
}
```

### Class Extensions (Private)

```objc
// Class extension in .m file (private interface)
@interface Person ()
// Private properties
@property (nonatomic, strong) NSMutableArray *privateData;

// Private methods
- (void)internalMethod;
@end

@implementation Person

- (void)internalMethod {
    // Implementation only visible in this file
}

@end
```

### Categories with Properties

```objc
#import <objc/runtime.h>

@interface UIView (CustomTag)
@property (nonatomic, strong) NSString *customTag;
@end

@implementation UIView (CustomTag)

static char kCustomTagKey;

- (void)setCustomTag:(NSString *)customTag {
    objc_setAssociatedObject(self, &kCustomTagKey, customTag,
                            OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)customTag {
    return objc_getAssociatedObject(self, &kCustomTagKey);
}

@end
```

---

## Memory Management

### ARC (Automatic Reference Counting)

```objc
// Strong reference (default for objects)
@property (strong) NSString *name;  // Keeps object alive

// Weak reference (doesn't prevent deallocation)
@property (weak) id<Delegate> delegate;  // Becomes nil when deallocated

// Unretained (like weak but doesn't nil out)
@property (unsafe_unretained) id observer;  // Dangerous: can dangle

// Copy (for immutable/mutable pairs)
@property (copy) NSString *title;  // Always creates immutable copy
```

### Retain Cycles and Solutions

```objc
// PROBLEM: Retain cycle
@interface Parent : NSObject
@property (strong) Child *child;
@end

@interface Child : NSObject
@property (strong) Parent *parent;  // Creates cycle!
@end

// SOLUTION 1: Weak reference
@interface Child : NSObject
@property (weak) Parent *parent;  // Breaks cycle
@end

// SOLUTION 2: Block retain cycles
@interface MyClass : NSObject
@property (copy) void (^completion)(void);
@end

@implementation MyClass

- (void)setupCompletion {
    // WRONG: Captures self strongly
    self.completion = ^{
        [self doSomething];  // Retain cycle!
    };

    // CORRECT: Use weak-strong dance
    __weak typeof(self) weakSelf = self;
    self.completion = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf doSomething];
        }
    };
}

@end
```

### Manual Memory Management (Pre-ARC)

```objc
// Only relevant for legacy code or when ARC is disabled

// Ownership rules (retain, release, autorelease)
- (void)legacyMemoryManagement {
    // alloc/init creates object with retain count 1
    NSString *owned = [[NSString alloc] initWithString:@"owned"];

    // retain increases retain count
    [owned retain];

    // release decreases retain count
    [owned release];
    [owned release];  // Final release, object deallocated

    // autorelease defers release until later
    NSString *temp = [[[NSString alloc] init] autorelease];

    // copy creates new object with retain count 1
    NSString *copied = [owned copy];
    [copied release];
}

// dealloc method
- (void)dealloc {
    [_property release];
    [super dealloc];  // Required in MRC
}
```

---

## Blocks (Closures)

### Block Syntax

```objc
// Block type definition
typedef void (^CompletionBlock)(BOOL success, NSError *error);
typedef NSInteger (^MathBlock)(NSInteger a, NSInteger b);

// Block variable
CompletionBlock completion = ^(BOOL success, NSError *error) {
    if (success) {
        NSLog(@"Success!");
    } else {
        NSLog(@"Error: %@", error);
    }
};

// Calling block
completion(YES, nil);

// Block as parameter
- (void)performOperationWithCompletion:(CompletionBlock)completion {
    // ... do work
    completion(YES, nil);
}
```

### Block Captures

```objc
- (void)demonstrateCaptures {
    NSInteger count = 0;

    // Captures count by value
    void (^block1)(void) = ^{
        NSLog(@"Count: %ld", (long)count);  // Prints captured value
    };

    count = 10;
    block1();  // Still prints 0

    // Mutable capture with __block
    __block NSInteger mutableCount = 0;
    void (^block2)(void) = ^{
        mutableCount++;  // Can modify
        NSLog(@"Mutable count: %ld", (long)mutableCount);
    };

    block2();  // Prints 1
    NSLog(@"After block: %ld", (long)mutableCount);  // Prints 1
}
```

### Common Block Patterns

```objc
// Enumeration with blocks
NSArray *numbers = @[@1, @2, @3, @4, @5];
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
    NSLog(@"Index %lu: %@", (unsigned long)idx, num);
    if ([num integerValue] == 3) {
        *stop = YES;  // Stop enumeration
    }
}];

// Filtering with predicates
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSNumber *num, NSDictionary *bindings) {
    return [num integerValue] > 2;
}];
NSArray *filtered = [numbers filteredArrayUsingPredicate:predicate];

// Sorting with comparator
NSArray *sorted = [numbers sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
    return [a compare:b];
}];

// Completion handlers
- (void)fetchDataWithCompletion:(void (^)(NSData *data, NSError *error))completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Fetch data
        NSData *data = [self loadData];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(data, nil);
            }
        });
    });
}
```

---

## Grand Central Dispatch (GCD)

### Dispatch Queues

```objc
// Main queue (UI updates)
dispatch_async(dispatch_get_main_queue(), ^{
    self.label.text = @"Updated on main thread";
});

// Global concurrent queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    // Background work
    NSData *data = [self heavyComputation];

    // Update UI on main queue
    dispatch_async(dispatch_get_main_queue(), ^{
        [self updateUIWithData:data];
    });
});

// Custom serial queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    // Serial execution
});

// Custom concurrent queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
```

### Synchronous vs Asynchronous

```objc
// Asynchronous (doesn't block)
dispatch_async(queue, ^{
    NSLog(@"Async work");
});

// Synchronous (blocks until complete)
dispatch_sync(queue, ^{
    NSLog(@"Sync work");
});
// WARNING: Never call dispatch_sync on current queue (deadlock!)

// Dispatch after delay
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
               dispatch_get_main_queue(), ^{
    NSLog(@"Executed after 2 seconds");
});
```

### Dispatch Groups

```objc
// Wait for multiple operations
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// Add tasks to group
dispatch_group_async(group, queue, ^{
    NSLog(@"Task 1");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"Task 2");
});

// Notify when all complete
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"All tasks complete");
});

// Or wait synchronously
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
```

### Dispatch Barriers

```objc
// Ensure exclusive access in concurrent queue
dispatch_barrier_async(concurrentQueue, ^{
    // This executes exclusively (no other tasks run concurrently)
    [self.mutableArray addObject:object];
});
```

---

## Foundation Framework Essentials

### NSString

```objc
// Creating strings
NSString *literal = @"Hello";
NSString *formatted = [NSString stringWithFormat:@"Value: %d", 42];
NSString *fromFile = [NSString stringWithContentsOfFile:path
                                                encoding:NSUTF8StringEncoding
                                                   error:&error];

// String operations
NSString *upper = [str uppercaseString];
NSString *lower = [str lowercaseString];
NSRange range = [str rangeOfString:@"substring"];
BOOL contains = [str containsString:@"text"];
NSString *replaced = [str stringByReplacingOccurrencesOfString:@"old" withString:@"new"];

// Mutable strings
NSMutableString *mutable = [NSMutableString stringWithString:@"Hello"];
[mutable appendString:@" World"];
[mutable insertString:@"Beautiful " atIndex:6];
[mutable deleteCharactersInRange:NSMakeRange(0, 5)];
```

### NSArray and NSMutableArray

```objc
// Creating arrays
NSArray *array = @[@"one", @"two", @"three"];
NSArray *array2 = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];

// Array operations
NSUInteger count = array.count;
id firstObject = array.firstObject;
id lastObject = array.lastObject;
id objectAtIndex = array[1];  // Modern syntax
BOOL contains = [array containsObject:@"two"];
NSUInteger index = [array indexOfObject:@"two"];

// Iteration
for (NSString *item in array) {
    NSLog(@"%@", item);
}

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    NSLog(@"%lu: %@", (unsigned long)idx, obj);
}];

// Mutable arrays
NSMutableArray *mutable = [NSMutableArray arrayWithArray:array];
[mutable addObject:@"four"];
[mutable insertObject:@"zero" atIndex:0];
[mutable removeObject:@"two"];
[mutable removeObjectAtIndex:0];
```

### NSDictionary and NSMutableDictionary

```objc
// Creating dictionaries
NSDictionary *dict = @{
    @"name": @"Alice",
    @"age": @30,
    @"city": @"NYC"
};

// Dictionary operations
id value = dict[@"name"];  // Modern syntax
id value2 = [dict objectForKey:@"age"];
NSArray *keys = dict.allKeys;
NSArray *values = dict.allValues;
NSUInteger count = dict.count;

// Iteration
for (NSString *key in dict) {
    NSLog(@"%@: %@", key, dict[key]);
}

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    NSLog(@"%@: %@", key, obj);
}];

// Mutable dictionaries
NSMutableDictionary *mutable = [NSMutableDictionary dictionaryWithDictionary:dict];
mutable[@"email"] = @"alice@example.com";
[mutable setObject:@31 forKey:@"age"];
[mutable removeObjectForKey:@"city"];
```

### NSSet and NSMutableSet

```objc
// Creating sets (unordered, unique elements)
NSSet *set = [NSSet setWithObjects:@"a", @"b", @"c", nil];
NSSet *set2 = [NSSet setWithArray:array];

// Set operations
BOOL contains = [set containsObject:@"a"];
NSUInteger count = set.count;

// Set algebra
NSSet *union = [set setByAddingObjectsFromSet:set2];
NSMutableSet *mutable = [set mutableCopy];
[mutable intersectSet:set2];

// Iteration
for (id object in set) {
    NSLog(@"%@", object);
}
```

### NSNumber and Boxing

```objc
// Boxing primitives
NSNumber *intNumber = @42;
NSNumber *floatNumber = @3.14f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'A';

// Boxing expressions
NSNumber *result = @(1 + 2 * 3);

// Unboxing
NSInteger intValue = [intNumber integerValue];
float floatValue = [floatNumber floatValue];
BOOL boolValue = [boolNumber boolValue];

// Using in collections
NSArray *numbers = @[@1, @2, @3, @4, @5];
```

---

## Common Patterns

### Singleton Pattern

```objc
@interface NetworkManager : NSObject
+ (instancetype)sharedManager;
@end

@implementation NetworkManager

+ (instancetype)sharedManager {
    static NetworkManager *shared = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [[self alloc] init];
    });
    return shared;
}

@end

// Usage
NetworkManager *manager = [NetworkManager sharedManager];
```

### Delegate Pattern

```objc
// Define protocol
@protocol DataSourceDelegate <NSObject>
@required
- (NSInteger)numberOfItems;
@optional
- (void)didSelectItemAtIndex:(NSInteger)index;
@end

// Class with delegate
@interface DataSource : NSObject
@property (weak, nonatomic) id<DataSourceDelegate> delegate;
@end

@implementation DataSource

- (void)loadData {
    if ([self.delegate respondsToSelector:@selector(numberOfItems)]) {
        NSInteger count = [self.delegate numberOfItems];
        NSLog(@"Items: %ld", (long)count);
    }
}

- (void)selectItem:(NSInteger)index {
    if ([self.delegate respondsToSelector:@selector(didSelectItemAtIndex:)]) {
        [self.delegate didSelectItemAtIndex:index];
    }
}

@end
```

### KVO (Key-Value Observing)

```objc
// Add observer
[person addObserver:self
         forKeyPath:@"name"
            options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
            context:NULL];

// Observe changes
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"name"]) {
        NSString *oldValue = change[NSKeyValueChangeOldKey];
        NSString *newValue = change[NSKeyValueChangeNewKey];
        NSLog(@"Name changed from %@ to %@", oldValue, newValue);
    }
}

// Remove observer (important!)
- (void)dealloc {
    [person removeObserver:self forKeyPath:@"name"];
}
```

### Notifications

```objc
// Post notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdated"
                                                    object:self
                                                  userInfo:@{@"count": @5}];

// Observe notification
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleDataUpdated:)
                                             name:@"DataUpdated"
                                           object:nil];

- (void)handleDataUpdated:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NSNumber *count = userInfo[@"count"];
    NSLog(@"Data updated, count: %@", count);
}

// Remove observer (important!)
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
```

---

## Error Handling

### NSError Pattern

```objc
// Method that can fail
- (BOOL)loadDataFromFile:(NSString *)path error:(NSError **)error {
    NSData *data = [NSData dataWithContentsOfFile:path
                                          options:0
                                            error:error];
    if (!data) {
        return NO;  // error is populated by the framework
    }

    // Process data
    return YES;
}

// Creating custom errors
- (BOOL)validateUser:(User *)user error:(NSError **)error {
    if (user.name.length == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"com.example.validation"
                                         code:100
                                     userInfo:@{
                                         NSLocalizedDescriptionKey: @"Name is required",
                                         NSLocalizedFailureReasonErrorKey: @"User name cannot be empty"
                                     }];
        }
        return NO;
    }
    return YES;
}

// Using error methods
NSError *error = nil;
BOOL success = [self loadDataFromFile:@"data.txt" error:&error];
if (!success) {
    NSLog(@"Error: %@", error.localizedDescription);
    NSLog(@"Reason: %@", error.localizedFailureReason);
}
```

### Exception Handling

```objc
// Try-catch (use sparingly in Objective-C)
@try {
    NSArray *array = @[@1, @2, @3];
    id value = array[10];  // Out of bounds
}
@catch (NSException *exception) {
    NSLog(@"Exception: %@, reason: %@", exception.name, exception.reason);
}
@finally {
    NSLog(@"Cleanup code");
}

// Assertions
NSAssert(count > 0, @"Count must be positive");
NSParameterAssert(user != nil);
```

---

## Troubleshooting

### Unrecognized Selector

**Problem:** `-[ClassName selectorName:]: unrecognized selector sent to instance`

```objc
// Cause: Calling method that doesn't exist
[object methodThatDoesntExist];

// Fix 1: Check method exists
if ([object respondsToSelector:@selector(optionalMethod)]) {
    [object optionalMethod];
}

// Fix 2: Add missing method implementation
- (void)methodThatDoesntExist {
    // Implementation
}
```

### Memory Leaks and Retain Cycles

**Problem:** Objects not being deallocated

```objc
// Check for retain cycles with weak references
@property (weak) id<Delegate> delegate;  // Not strong!

// Use weak-strong dance in blocks
__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doWork];
    }
};

// Remove observers
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [self.observedObject removeObserver:self forKeyPath:@"property"];
}
```

### Nil Messaging

**Problem:** Sending messages to nil

```objc
// In Objective-C, sending messages to nil is safe (returns nil/0)
NSString *str = nil;
NSUInteger length = [str length];  // Returns 0, doesn't crash

// But can hide bugs
id result = [nil someMethod];  // Returns nil, might mask issue

// Check for nil when necessary
if (object) {
    [object doSomething];
}
```

### Property Attribute Mistakes

**Problem:** Incorrect memory management

```objc
// WRONG: Strong reference to delegate (retain cycle)
@property (strong) id<Delegate> delegate;

// CORRECT: Weak reference
@property (weak) id<Delegate> delegate;

// WRONG: Mutable string without copy
@property (strong) NSString *name;
// Caller could pass NSMutableString and mutate it

// CORRECT: Use copy for strings
@property (copy) NSString *name;
```

### Thread Safety Issues

**Problem:** Accessing shared state from multiple threads

```objc
// WRONG: Direct access from multiple threads
self.mutableArray = [NSMutableArray array];
dispatch_async(queue1, ^{ [self.mutableArray addObject:obj1]; });
dispatch_async(queue2, ^{ [self.mutableArray addObject:obj2]; });

// CORRECT: Synchronize access
@synchronized(self.mutableArray) {
    [self.mutableArray addObject:obj];
}

// BETTER: Use serial queue or barrier
dispatch_barrier_async(self.concurrentQueue, ^{
    [self.mutableArray addObject:obj];
});
```

---

## Module System

### Header and Implementation Files

```objc
// Header file (.h) - Public interface
#import <Foundation/Foundation.h>

// Forward declaration (avoid importing in header)
@class OtherClass;
@protocol SomeProtocol;

NS_ASSUME_NONNULL_BEGIN

@interface MyClass : NSObject

// Public API
@property (nonatomic, readonly) NSString *publicName;
- (instancetype)initWithName:(NSString *)name;
- (void)publicMethod;

@end

NS_ASSUME_NONNULL_END
```

```objc
// Implementation file (.m) - Private interface and implementation
#import "MyClass.h"
#import "OtherClass.h"  // Import here, not in header

// Private class extension
@interface MyClass ()
@property (nonatomic, readwrite) NSString *publicName;  // Make writable internally
@property (nonatomic, strong) NSMutableArray *privateData;
- (void)privateMethod;
@end

@implementation MyClass

- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        _publicName = [name copy];
        _privateData = [NSMutableArray array];
    }
    return self;
}

- (void)publicMethod {
    [self privateMethod];
}

- (void)privateMethod {
    // Private implementation
}

@end
```

### Import Types

```objc
// Framework import (system or SDK frameworks)
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

// Local header import
#import "MyClass.h"

// Umbrella header (for frameworks)
#import <MyFramework/MyFramework.h>

// Module import (modern, preferred)
@import Foundation;
@import UIKit;

// Conditional import
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif
```

### Visibility and Encapsulation

```objc
// Public: Declared in .h file
// In MyClass.h
- (void)publicMethod;

// Private: Declared in class extension in .m
@interface MyClass ()
- (void)privateMethod;
@end

// Protected: Use conventions (no language enforcement)
// By convention, prefix with underscore or document as internal
- (void)_internalMethod;  // Signals internal use

// Package visibility (via categories in separate header)
// MyClass+Internal.h - Only import where needed
@interface MyClass (Internal)
- (void)internalMethod;
@end
```

### Framework Structure

```
MyFramework.framework/
├── Headers/
│   ├── MyFramework.h           # Umbrella header
│   ├── PublicClass.h           # Public headers
│   └── AnotherPublicClass.h
├── PrivateHeaders/             # Private headers (not exposed)
│   └── InternalClass.h
├── Modules/
│   └── module.modulemap        # Module definition
└── MyFramework                 # Binary
```

```objc
// Umbrella header (MyFramework.h)
#import <MyFramework/PublicClass.h>
#import <MyFramework/AnotherPublicClass.h>
// Do NOT import private headers here
```

---

## Zero and Default Values

### Nil and NULL

```objc
// nil: Null pointer to Objective-C object
NSString *string = nil;

// NULL: Null pointer to C data (primitive pointers)
int *pointer = NULL;
void (*functionPointer)(void) = NULL;

// Nil: Null pointer to Objective-C class (rarely used)
Class cls = Nil;

// NSNull: Object representing null (for collections)
NSArray *array = @[@"one", [NSNull null], @"three"];
```

### Safe Nil Messaging

```objc
// Sending messages to nil is safe in Objective-C
NSString *string = nil;

// All return 0/nil/NO (doesn't crash)
NSUInteger length = [string length];         // Returns 0
NSString *upper = [string uppercaseString];  // Returns nil
BOOL empty = [string isEqualToString:@""];   // Returns NO

// Chaining with nil
NSArray *items = nil;
NSNumber *first = items.firstObject;  // nil
NSString *desc = [first stringValue]; // nil
```

### Instance Variable Defaults

```objc
@implementation MyClass {
    NSString *_string;      // Initialized to nil
    NSInteger _integer;     // Initialized to 0
    CGFloat _float;         // Initialized to 0.0
    BOOL _boolean;          // Initialized to NO
    id _object;             // Initialized to nil
}

// Verify defaults in init
- (instancetype)init {
    self = [super init];
    if (self) {
        NSAssert(_string == nil, @"Should be nil");
        NSAssert(_integer == 0, @"Should be 0");
        NSAssert(_boolean == NO, @"Should be NO");
    }
    return self;
}

@end
```

### Default/Empty Values by Type

| Type | Default Value | Empty/Zero Value |
|------|---------------|------------------|
| `id`, objects | `nil` | `nil` |
| `NSInteger` | `0` | `0` |
| `CGFloat`, `double` | `0.0` | `0.0` |
| `BOOL` | `NO` | `NO` |
| `NSString *` | `nil` | `@""` |
| `NSArray *` | `nil` | `@[]` |
| `NSDictionary *` | `nil` | `@{}` |
| `NSNumber *` | `nil` | `@0` |

### Handling Optional Values

```objc
// Check before use
if (string != nil && string.length > 0) {
    [self processString:string];
}

// Provide defaults
NSString *name = user.name ?: @"Unknown";

// NSNull checking (from JSON)
id value = json[@"key"];
if (value && value != [NSNull null]) {
    NSString *stringValue = (NSString *)value;
}

// Safe casting
NSString *string = [object isKindOfClass:[NSString class]] ? object : nil;
```

---

## Metaprogramming

Objective-C's runtime enables powerful metaprogramming capabilities. For cross-language comparison, see `patterns-metaprogramming-dev`.

### Runtime Introspection

```objc
#import <objc/runtime.h>

// Class introspection
Class cls = [MyClass class];
NSLog(@"Class name: %s", class_getName(cls));
NSLog(@"Superclass: %@", [cls superclass]);

// Check class and protocol conformance
if ([object isKindOfClass:[NSString class]]) {
    NSLog(@"Object is a string");
}
if ([object conformsToProtocol:@protocol(NSCopying)]) {
    NSLog(@"Object is copyable");
}

// Check method implementation
if ([object respondsToSelector:@selector(optionalMethod)]) {
    [object optionalMethod];
}
```

### Listing Methods and Properties

```objc
// List all methods
unsigned int methodCount;
Method *methods = class_copyMethodList([MyClass class], &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
    SEL selector = method_getName(methods[i]);
    NSLog(@"Method: %@", NSStringFromSelector(selector));
}
free(methods);

// List all properties
unsigned int propCount;
objc_property_t *properties = class_copyPropertyList([MyClass class], &propCount);
for (unsigned int i = 0; i < propCount; i++) {
    const char *name = property_getName(properties[i]);
    const char *attrs = property_getAttributes(properties[i]);
    NSLog(@"Property: %s, Attrs: %s", name, attrs);
}
free(properties);

// List protocols
unsigned int protocolCount;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList([MyClass class], &protocolCount);
for (unsigned int i = 0; i < protocolCount; i++) {
    NSLog(@"Protocol: %s", protocol_getName(protocols[i]));
}
free(protocols);
```

### Method Swizzling

```objc
#import <objc/runtime.h>

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewDidAppear:);
        SEL swizzledSelector = @selector(tracking_viewDidAppear:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector,
                                            method_getImplementation(swizzledMethod),
                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector,
                               method_getImplementation(originalMethod),
                               method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)tracking_viewDidAppear:(BOOL)animated {
    // This calls the original viewDidAppear (methods are swapped)
    [self tracking_viewDidAppear:animated];

    // Add tracking
    NSLog(@"View appeared: %@", NSStringFromClass([self class]));
}

@end
```

### Dynamic Method Resolution

```objc
@implementation DynamicClass

// Called when method not found
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel,
                       (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method called on %@", self);
}

@end
```

### Message Forwarding

```objc
// Called when no implementation found
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([self.helper respondsToSelector:aSelector]) {
        return self.helper;  // Forward to helper
    }
    return [super forwardingTargetForSelector:aSelector];
}

// Full forwarding (if forwardingTargetForSelector returns nil)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
    if (!sig) {
        sig = [self.helper methodSignatureForSelector:aSelector];
    }
    return sig;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([self.helper respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self.helper];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
```

---

## Serialization

For cross-language serialization patterns and comparison, see `patterns-serialization-dev`.

### NSCoding (Archive/Unarchive)

```objc
@interface User : NSObject <NSSecureCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSDate *createdAt;
@end

@implementation User

// Required for NSSecureCoding
+ (BOOL)supportsSecureCoding {
    return YES;
}

// Encode object
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeInteger:self.age forKey:@"age"];
    [coder encodeObject:self.createdAt forKey:@"createdAt"];
}

// Decode object
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _name = [coder decodeObjectOfClass:[NSString class] forKey:@"name"];
        _age = [coder decodeIntegerForKey:@"age"];
        _createdAt = [coder decodeObjectOfClass:[NSDate class] forKey:@"createdAt"];
    }
    return self;
}

@end

// Archive to data
User *user = [[User alloc] init];
user.name = @"Alice";
user.age = 30;

NSError *error;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:user
                                     requiringSecureCoding:YES
                                                     error:&error];

// Unarchive from data
User *restored = [NSKeyedUnarchiver unarchivedObjectOfClass:[User class]
                                                   fromData:data
                                                      error:&error];
```

### JSON Serialization

```objc
// Dictionary to JSON
NSDictionary *dict = @{
    @"name": @"Alice",
    @"age": @30,
    @"email": @"alice@example.com"
};

NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                             encoding:NSUTF8StringEncoding];

// JSON to Dictionary
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *parsed = [NSJSONSerialization JSONObjectWithData:data
                                                       options:0
                                                         error:&error];

// Handle NSNull in JSON
id value = parsed[@"nullable_field"];
if (value && value != [NSNull null]) {
    // Use value
}
```

### Manual JSON Mapping

```objc
@interface User : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSDate *createdAt;

+ (instancetype)userFromDictionary:(NSDictionary *)dict;
- (NSDictionary *)toDictionary;
@end

@implementation User

+ (instancetype)userFromDictionary:(NSDictionary *)dict {
    User *user = [[User alloc] init];
    user.name = dict[@"name"];
    user.age = [dict[@"age"] integerValue];

    // Parse date
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
    user.createdAt = [formatter dateFromString:dict[@"created_at"]];

    return user;
}

- (NSDictionary *)toDictionary {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

    return @{
        @"name": self.name ?: [NSNull null],
        @"age": @(self.age),
        @"created_at": self.createdAt ? [formatter stringFromDate:self.createdAt] : [NSNull null]
    };
}

@end
```

### Property Lists

```objc
// Save to plist file
NSDictionary *config = @{
    @"version": @"1.0.0",
    @"settings": @{
        @"darkMode": @YES,
        @"fontSize": @14
    }
};

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]
                  stringByAppendingPathComponent:@"config.plist"];
[config writeToFile:path atomically:YES];

// Load from plist
NSDictionary *loaded = [NSDictionary dictionaryWithContentsOfFile:path];

// UserDefaults (plist-based)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"value" forKey:@"key"];
[defaults setBool:YES forKey:@"boolKey"];
[defaults synchronize];

NSString *value = [defaults stringForKey:@"key"];
BOOL boolValue = [defaults boolForKey:@"boolKey"];
```

---

## Build and Dependencies

### Xcode Project Structure

```
MyProject/
├── MyProject.xcodeproj/      # Xcode project file
├── MyProject/                # Main target source
│   ├── AppDelegate.h/m
│   ├── Models/
│   ├── Views/
│   ├── Controllers/
│   └── Info.plist
├── MyProjectTests/           # Unit test target
│   └── MyProjectTests.m
├── MyProjectUITests/         # UI test target
│   └── MyProjectUITests.m
└── Podfile                   # CocoaPods config (if used)
```

### CocoaPods

```ruby
# Podfile
platform :ios, '13.0'
use_frameworks!

target 'MyApp' do
  pod 'AFNetworking', '~> 4.0'
  pod 'Masonry'
  pod 'SDWebImage'

  target 'MyAppTests' do
    inherit! :search_paths
    pod 'OCMock'
  end
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
    end
  end
end
```

```bash
# CocoaPods commands
pod init                  # Create Podfile
pod install              # Install dependencies
pod update               # Update dependencies
pod outdated             # Check for updates
pod deintegrate          # Remove CocoaPods
```

### Swift Package Manager (SPM)

```swift
// Package.swift (for library development)
// swift-tools-version:5.5
import PackageDescription

let package = Package(
    name: "MyLibrary",
    platforms: [.iOS(.v13), .macOS(.v10_15)],
    products: [
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
    ],
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0"),
    ],
    targets: [
        .target(name: "MyLibrary", dependencies: ["Alamofire"]),
        .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
    ]
)
```

In Xcode: File → Add Packages → Enter repository URL

### Carthage

```
# Cartfile
github "Alamofire/Alamofire" ~> 5.0
github "ReactiveX/RxSwift" ~> 6.0
```

```bash
# Carthage commands
carthage update --platform iOS          # Build frameworks
carthage bootstrap --platform iOS       # Build from resolved versions
carthage outdated                       # Check for updates
```

### Build Settings

```
// Common build settings (Build Settings tab)

// Architectures
ARCHS = arm64
VALID_ARCHS = arm64

// Deployment
IPHONEOS_DEPLOYMENT_TARGET = 13.0
TARGETED_DEVICE_FAMILY = 1,2  // iPhone and iPad

// Code Signing
CODE_SIGN_IDENTITY = Apple Development
DEVELOPMENT_TEAM = XXXXXXXXXX

// Compilation
GCC_WARN_UNINITIALIZED_AUTOS = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_ENABLE_OBJC_ARC = YES

// Linking
OTHER_LDFLAGS = -ObjC  // For static libraries with categories
```

---

## Testing

### XCTest Basics

```objc
// MyClassTests.m
#import <XCTest/XCTest.h>
#import "MyClass.h"

@interface MyClassTests : XCTestCase
@property (nonatomic, strong) MyClass *sut;  // System Under Test
@end

@implementation MyClassTests

- (void)setUp {
    [super setUp];
    self.sut = [[MyClass alloc] init];
}

- (void)tearDown {
    self.sut = nil;
    [super tearDown];
}

- (void)testExample {
    // Arrange
    NSString *input = @"test";

    // Act
    NSString *result = [self.sut processInput:input];

    // Assert
    XCTAssertNotNil(result);
    XCTAssertEqualObjects(result, @"expected");
}

@end
```

### XCTest Assertions

```objc
// Boolean assertions
XCTAssertTrue(condition);
XCTAssertFalse(condition);

// Nil assertions
XCTAssertNil(object);
XCTAssertNotNil(object);

// Equality assertions
XCTAssertEqual(actual, expected);           // Primitive equality
XCTAssertNotEqual(actual, notExpected);
XCTAssertEqualObjects(actual, expected);    // Object equality (isEqual:)
XCTAssertNotEqualObjects(actual, notExpected);

// Numeric assertions
XCTAssertEqualWithAccuracy(3.14, 3.14159, 0.01);
XCTAssertGreaterThan(5, 3);
XCTAssertLessThanOrEqual(3, 5);

// Exception assertions
XCTAssertThrows([object methodThatThrows]);
XCTAssertNoThrow([object safeMethod]);
XCTAssertThrowsSpecific([object method], NSInvalidArgumentException);

// Custom failure
XCTFail(@"Test failed for reason: %@", reason);
```

### Async Testing

```objc
// Using expectations
- (void)testAsyncOperation {
    XCTestExpectation *expectation = [self expectationWithDescription:@"Async completed"];

    [self.sut performAsyncOperationWithCompletion:^(NSData *data, NSError *error) {
        XCTAssertNotNil(data);
        XCTAssertNil(error);
        [expectation fulfill];
    }];

    [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
        if (error) {
            XCTFail(@"Timeout: %@", error);
        }
    }];
}

// Multiple expectations
- (void)testMultipleAsync {
    XCTestExpectation *exp1 = [self expectationWithDescription:@"First"];
    XCTestExpectation *exp2 = [self expectationWithDescription:@"Second"];

    [self.service fetchFirst:^{ [exp1 fulfill]; }];
    [self.service fetchSecond:^{ [exp2 fulfill]; }];

    [self waitForExpectations:@[exp1, exp2] timeout:10.0];
}

// Inverted expectation (should NOT be called)
- (void)testCallbackNotCalled {
    XCTestExpectation *expectation = [self expectationWithDescription:@"Should not call"];
    expectation.inverted = YES;

    [self.sut methodThatShouldNotCallback:^{
        [expectation fulfill];  // This would fail the test
    }];

    [self waitForExpectationsWithTimeout:1.0 handler:nil];
}
```

### Mocking with OCMock

```objc
#import <OCMock/OCMock.h>

- (void)testWithMock {
    // Create mock
    id mockService = OCMClassMock([NetworkService class]);

    // Stub method
    OCMStub([mockService fetchDataWithCompletion:OCMOCK_ANY])
        .andDo(^(NSInvocation *invocation) {
            void (^completion)(NSData *, NSError *);
            [invocation getArgument:&completion atIndex:2];
            completion([@"mock data" dataUsingEncoding:NSUTF8StringEncoding], nil);
        });

    // Inject mock
    self.sut.networkService = mockService;

    // Test
    [self.sut loadData];

    // Verify
    OCMVerify([mockService fetchDataWithCompletion:OCMOCK_ANY]);
}

// Partial mock
- (void)testPartialMock {
    id partialMock = OCMPartialMock(self.sut);
    OCMStub([partialMock expensiveOperation]).andReturn(@"cached");

    NSString *result = [self.sut methodThatCallsExpensiveOperation];
    XCTAssertEqualObjects(result, @"cached");
}

// Protocol mock
- (void)testProtocolMock {
    id mockDelegate = OCMProtocolMock(@protocol(MyDelegate));
    self.sut.delegate = mockDelegate;

    [self.sut triggerDelegate];

    OCMVerify([mockDelegate didComplete:OCMOCK_ANY]);
}
```

### Test Doubles

```objc
// Stub: Replace with canned response
@interface StubNetworkService : NetworkService
@property (nonatomic, strong) NSData *stubbedData;
@end

@implementation StubNetworkService
- (void)fetchWithCompletion:(void (^)(NSData *))completion {
    completion(self.stubbedData);
}
@end

// Spy: Record calls
@interface SpyLogger : Logger
@property (nonatomic, strong) NSMutableArray *loggedMessages;
@end

@implementation SpyLogger
- (instancetype)init {
    self = [super init];
    if (self) { _loggedMessages = [NSMutableArray array]; }
    return self;
}
- (void)log:(NSString *)message {
    [self.loggedMessages addObject:message];
}
@end

// Fake: Working implementation
@interface FakeUserDefaults : NSUserDefaults
@property (nonatomic, strong) NSMutableDictionary *storage;
@end

@implementation FakeUserDefaults
- (void)setObject:(id)value forKey:(NSString *)key {
    self.storage[key] = value;
}
- (id)objectForKey:(NSString *)key {
    return self.storage[key];
}
@end
```

---

## Cross-Cutting Patterns

For cross-language comparison and translation patterns, see:

- `patterns-concurrency-dev` - GCD, NSOperation, threading
- `patterns-serialization-dev` - NSCoding, JSON, property lists
- `patterns-metaprogramming-dev` - Runtime, swizzling, introspection

---

## References

- [Apple Developer Documentation](https://developer.apple.com/documentation/)
- [Objective-C Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/)
- [Memory Management Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/)
- [Blocks Programming Topics](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/)
- [Concurrency Programming Guide](https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/)
- Specialized skills: `lang-objc-patterns-dev`, `lang-objc-library-dev`, `lang-swift-objc-bridge-dev`

Attribution

aRustyDevaRustyDev
View sourceSee grades on GitHubMore from aRustyDev →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

286712 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10341 votes
View all in development →