← Back to all projects

LEARN IOS IN C DEEP DIVE

Learn iOS Development in Pure C: From Zero to Metal

Goal: Deeply understand the iOS platform by building applications from first principles in pure C, bypassing modern abstractions like Swift, SwiftUI, and even Objective-C as much as humanly possible.


Why Learn iOS in C?

Modern iOS development is a high-level affair. Swift and SwiftUI provide immense power and safety, but they abstract away the machinery that makes it all work. Writing an iOS app in C is an act of software archaeology. It’s about peeling back the layers to see the raw, C-based foundations that still underpin the entire operating system.

This path is not for building the next big App Store hit. It’s for gaining an unparalleled, fundamental understanding of the platform.

After completing these projects, you will:

  • Understand the C-based APIs (Core Foundation, Core Graphics) that everything else is built on.
  • Manually manage an application’s lifecycle and event loop.
  • Render UI components from scratch, pixel by pixel.
  • Interface directly with the Objective-C runtime from C.
  • Truly understand memory management without the safety net of ARC.

Core Concept Analysis

The iOS Stack (From a C Perspective)

┌──────────────────────────────────────────────────┐
│                 Swift / SwiftUI / UIKit            │  ← We will AVOID this layer
│           (High-Level, Object-Oriented)            │
└──────────────────────────────────────────────────┘
                            │
                            ▼
┌──────────────────────────────────────────────────┐
│           Objective-C / Foundation / AppKit        │  ← We will BRIDGE this layer
│            (The original, object-oriented API)     │
└──────────────────────────────────────────────────┘
                            │
                            ▼
┌──────────────────────────────────────────────────┐
│              Core Foundation / Core Graphics       │  ← We will LIVE in this layer
│      (The C-based, procedural foundation)        │
└──────────────────────────────────────────────────┘
                            │
                            ▼
┌──────────────────────────────────────────────────┐
│                  XNU Kernel / Mach / BSD           │  ← The deep magic
│                 (The heart of the OS)              │
└──────────────────────────────────────────────────┘

Fundamental Concepts

  1. The Objective-C Runtime Bridge: You cannot escape the Objective-C runtime entirely. The main function of an iOS app must hand control over to an object-oriented framework. We will use C functions like objc_msgSend to create the minimal objects required (a UIWindow, a UIView) to get a drawing surface.
  2. Core Foundation (CF): The C-based equivalent of the Foundation framework.
    • Toll-Free Bridging: CFString is interchangeable with NSString, CFArray with NSArray, etc. This is the key that makes interoperability possible.
    • Manual Memory Management: You are responsible for CFRetain() and CFRelease() for every CF object you create. This is like manual memory management on steroids.
    • Run Loops: The CFRunLoop is the event processing heart of your application.
  3. Core Graphics (CG) / Quartz 2D: The low-level C API for all 2D drawing. You will use it to draw everything: lines, shapes, text, images. You’ll get a CGContextRef and issue drawing commands to it.
  4. The App Bundle and Toolchain: An iOS app is a directory with a specific structure (Info.plist, executable, resources). You’ll learn how to use clang to cross-compile your C code for the ARM64 architecture of an iPhone and package it correctly.
  5. Manual UI and Event Handling: Forget Interface Builder. You will define UI element bounds with CGRect. You will handle touches by processing coordinates passed to low-level event handlers you register yourself.

Project List

The following 10 projects will guide you from a console message to a fully interactive C-based iOS application.


Project 1: “Hello, Console!” iOS App

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: Objective-C, C++
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: iOS App Lifecycle / Toolchain
  • Software or Tool: Xcode, Clang
  • Main Book: “Mac OS X and iOS Internals: To the Apple’s Core” by Jonathan Levin

What you’ll build: The simplest possible iOS application: a main function in C that links against the necessary frameworks and prints a “Hello, World!” message to the Xcode debug console before exiting.

Why it teaches pure C on iOS: It forces you to understand the absolute minimum required to create a running iOS process. You’ll learn about the main entry point, linking against system frameworks, and the basic app bundle structure, all without a single line of Objective-C.

Core challenges you’ll face:

  • Creating a C main function → maps to understanding the app’s entry point before UIApplicationMain is called
  • Linking against Foundation/UIKit → maps to telling the compiler which system libraries you need, even if you don’t use their headers
  • Configuring Xcode for a C-only project → maps to stripping away all the default Swift/Objective-C boilerplate
  • Using printf and NSLog → maps to understanding basic output and the underlying system logging

Key Concepts:

  • Application Entry Point: “Mac OS X and iOS Internals” Ch. 4 - Levin
  • Linking and Frameworks: Apple’s “Linker and Libraries Guide” (Archived)
  • CoreFoundation Logging: CFShow function documentation

Difficulty: Intermediate (conceptually, not in lines of code) Time estimate: A few hours Prerequisites: Basic C programming, Xcode installed.

Real world outcome: When you run the app in the Xcode simulator, it will launch to a black screen, and in the Xcode console output pane, you will see:

Hello from pure C on iOS!

The app will then terminate.

Implementation Hints:

  1. Create a new, empty Xcode project. Remove any default AppDelegate or SceneDelegate files.
  2. Create a single main.c file.
  3. Your main function is the entry point: int main(int argc, char * argv[]).
  4. To print to the console, you can use standard printf, but it’s better to use a Core Foundation method. CFStringRef is the C equivalent of NSString.
  5. Create a CFStringRef: CFStringRef myString = CFSTR("Hello from pure C on iOS!");
  6. Use CFShow(myString) to print the string’s description to the console. This is the C-level way to log objects.
  7. Crucially, you’ll need to link your app against the CoreFoundation.framework. In Xcode’s build settings, you’ll find “Linked Frameworks and Libraries”.
  8. At the end of main, simply return 0;. The app will launch, print, and exit.

Learning milestones:

  1. App compiles and links → You understand how to connect your C code to Apple’s frameworks.
  2. Message appears in the console → You have successfully executed code in an iOS environment.
  3. App launches and exits cleanly → You understand the most basic application lifecycle.

Project 2: Core Foundation Fundamentals

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Objective-C
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Data Structures / Manual Memory Management
  • Software or Tool: Core Foundation framework
  • Main Book: “C Interfaces and Implementations” by David R. Hanson (for C design patterns)

What you’ll build: A console-based iOS app that creates, manipulates, and properly cleans up various Core Foundation objects: CFString, CFNumber, CFArray, and CFDictionary.

Why it teaches pure C on iOS: Core Foundation is the C-based “standard library” you’ll be using. This project forces you to master its data structures and, most importantly, its manual memory management model (The “Get/Create Rule”).

Core challenges you’ll face:

  • Creating CF objects → maps to using CFStringCreateWith..., CFArrayCreate..., etc.
  • Manual memory management → maps to knowing when to use CFRetain and CFRelease
  • Understanding the “Create Rule” → maps to if the function name contains “Create” or “Copy”, you own the object and must release it
  • Using toll-free bridging → maps to casting a CFStringRef to an NSString* to use Objective-C methods on it if needed

Key Concepts:

  • Core Foundation Design Concepts: Apple’s “Core Foundation Design Concepts” guide (Archived)
  • Memory Management: Apple’s “Memory Management Programming Guide for Core Foundation” (Archived)
  • Ownership Policy: The “Create Rule” and “Get Rule” sections of the memory management guide.

Difficulty: Intermediate Time estimate: 1 day Prerequisites: Project 1, solid understanding of C pointers.

Real world outcome: Your app will print a structured description of the data you’ve created to the Xcode console, for example:

--- CFArray ---
(
    "Hello, CFString!",
    42,
    {
        key1 = "value1";
        key2 = 1337;
    }
)
--- All objects released successfully ---

Implementation Hints:

  1. Follow the “Create Rule”:
    • CFStringRef str = CFStringCreateWithCString(...) -> You OWN this. You must call CFRelease(str) later.
    • CFStringRef str = CFArrayGetValueAtIndex(...) -> You do NOT own this. Do not release it. If you need to keep it, you must CFRetain it.
  2. Build a nested structure. For example, create a CFString and a CFNumber, put them in a CFArray. Create a CFDictionary and put that array into the dictionary.
  3. Use CFShow() to print the contents of your top-level object. It will recursively print the description of all contained objects.
  4. Work backward from your most complex object, releasing everything you created. If your memory management is correct, you will have no leaks. You can check this with Xcode’s memory debugger.
  5. Try to intentionally leak an object (e.g., create it but don’t release it) and see if you can detect it with the tools.

Learning milestones:

  1. Create and log a CFDictionary → You can work with complex, nested C data structures.
  2. Run the app without memory leaks → You have understood and correctly applied the Core Foundation ownership model.
  3. Use CFRetain correctly → You know how to keep a reference to an object you don’t own.

Project 3: “Hello, Window!” - The Bridge to Graphics

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: Objective-C (the easy way)
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Objective-C Runtime / iOS App Lifecycle
  • Software or Tool: Objective-C Runtime Library (<objc/runtime.h>)
  • Main Book: “Cocoa Programming for Mac OS X” by Aaron Hillegass (for the underlying concepts)

What you’ll build: A pure C application that displays a blank, black window on the iOS simulator. This is the “Hello, World!” of graphics and the most critical conceptual leap.

Why it teaches pure C on iOS: You cannot create a window without interacting with the Objective-C-based UIKit. This project forces you to learn how to speak “Objective-C” from pure C by using the low-level runtime functions. You will be sending messages to objects you’ve created from C.

Core challenges you’ll face:

  • Calling Objective-C from C → maps to using objc_msgSend to invoke methods
  • Finding classes and selectors → maps to using objc_getClass and sel_getUid
  • Instantiating objects → maps to the alloc and init message sequence
  • Setting up a Run Loop → maps to calling CFRunLoopRun() to start the event loop and prevent your app from exiting

Key Concepts:

  • Objective-C Runtime Reference: Apple’s objc/runtime.h header documentation.
  • Messaging: The concept of objc_msgSend(receiver, selector, ...args).
  • Run Loops: Apple’s “Run Loop Management” guide (Archived).

Difficulty: Advanced Time estimate: 1-2 days Prerequisites: Project 2, comfort with function pointers in C.

Real world outcome: The app launches and displays a persistent, empty, black window in the iOS simulator. It will not exit until you manually stop it.

Implementation Hints:

This is a pseudo-code sequence of the runtime calls you’ll need to make. Your job is to translate this into real C.

// All Objective-C method calls must be done through objc_msgSend.
// You will need to cast objc_msgSend to the correct function pointer type
// to avoid compiler warnings and ensure correct argument passing.

// Get references to the classes we need.
Class UIWindow = objc_getClass("UIWindow");
Class UIScreen = objc_getClass("UIScreen");

// Get the main screen: [UIScreen mainScreen]
id mainScreen = objc_msgSend(UIScreen, sel_getUid("mainScreen"));

// Get the screen bounds: [[UIScreen mainScreen] bounds]
CGRect screenBounds;
objc_msgSend_stret(&screenBounds, mainScreen, sel_getUid("bounds")); // Use _stret for struct returns

// Create a window: [[UIWindow alloc] initWithFrame:screenBounds]
id window = objc_msgSend(UIWindow, sel_getUid("alloc"));
window = objc_msgSend(window, sel_getUid("initWithFrame:"), screenBounds);

// Make it visible: [window makeKeyAndVisible]
objc_msgSend(window, sel_getUid("makeKeyAndVisible"));

// This is the most important part! Start the event loop.
// Without this, your program will exit and you'll never see the window.
CFRunLoopRun();

// The code will be "stuck" on CFRunLoopRun(), processing events,
// until the app is terminated.

Questions to guide you:

  1. Why do you need to cast objc_msgSend for different methods? (Hint: return types and argument types).
  2. What is a “selector”? How does sel_getUid("initWithFrame:") work?
  3. What is a “Run Loop” and why does the program not just exit after the last line of main?

Learning milestones:

  1. A window appears → You have successfully bridged the gap between C and the Objective-C world.
  2. The app doesn’t exit → You understand the fundamental role of the Run Loop.
  3. You can change the window’s background color → You can send messages with arguments to modify object properties.

Project 4: The Core Graphics Drawing Pad

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: 2D Graphics / Event Handling
  • Software or Tool: Core Graphics (<CoreGraphics/CoreGraphics.h>), Objective-C Runtime
  • Main Book: “Computer Graphics from Scratch” by Gabriel Gambetta (for graphics theory)

What you’ll build: A simple drawing application. When you tap the screen, a red circle appears at that location. This is your first custom-rendered UI.

Why it teaches pure C on iOS: It forces you to learn the Core Graphics API, which is the foundation of all 2D drawing on iOS. You’ll also have to implement your own “view” and its drawing and touch-handling logic from scratch.

Core challenges you’ll face:

  • Creating a custom UIView subclass in C → maps to using objc_allocateClassPair to dynamically create a new class
  • Implementing a method in C → maps to using class_addMethod to add a C function as an Objective-C method implementation
  • Overriding drawRect: → maps to implementing your own drawRect: method in C to get a CGContextRef
  • Handling touch events → maps to implementing touchesBegan: to get touch coordinates

Key Concepts:

  • Core Graphics Drawing: Apple’s “Quartz 2D Programming Guide” (Archived), especially the sections on Paths and Color.
  • Dynamic Subclassing: objc_allocateClassPair, class_addMethod, objc_registerClassPair.
  • Method Encodings: Understanding the type signature strings for class_addMethod (e.g., "v@:").

Difficulty: Advanced Time estimate: 2-3 days Prerequisites: Project 3.

Real world outcome: An app with a white background. When you tap anywhere on the screen, a red circle is drawn and persists at that location. You can add multiple circles.

Implementation Hints:

  1. Create a custom class: You can’t just use UIView because you need to provide your own drawing code.
    • Class MyViewClass = objc_allocateClassPair(objc_getClass("UIView"), "MyView", 0);
  2. Implement drawRect: in C: Write a C function void myDrawRect(id self, SEL _cmd, CGRect rect).
    • Inside, get the graphics context: CGContextRef ctx = UIGraphicsGetCurrentContext();
    • Set the fill color: CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor); (you’ll need to get [UIColor redColor] via objc_msgSend).
    • Draw your shapes (e.g., circles at stored locations): CGContextFillEllipseInRect(ctx, circleRect);
  3. Add your method to the class: class_addMethod(MyViewClass, sel_getUid("drawRect:"), (IMP)myDrawRect, "v@:{CGRect={CGPoint=dd}{CGSize=dd}}");
  4. Implement touchesBegan: in C: Write a C function to handle touches, get the coordinates, store them in an array, and then call [self setNeedsDisplay]; (via objc_msgSend) to trigger a redraw.
  5. Register your class: objc_registerClassPair(MyViewClass);
  6. Use it: In your main function from Project 3, instead of adding a plain UIView, allocate and init an instance of MyView.

This is the most complex part of the entire learning journey. Mastering this means you can build any UI you want.

Learning milestones:

  1. A static shape appears → You have successfully implemented a custom drawRect: and obtained a graphics context.
  2. Tapping the screen draws a circle → You have successfully handled touch events and can trigger a redraw.
  3. Multiple taps create multiple circles → You are managing state (the circle locations) within your C implementation.

Project 5: A Pure C UI Button

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: UI Implementation / State Management
  • Software or Tool: Core Graphics, Core Text
  • Main Book: “Designing Interfaces” by Jenifer Tidwell (for UI theory)

What you’ll build: A reusable “button” component, rendered entirely in C. It will be a rectangle with text inside. When you press it, it will change color, and when you release, it will change back and print a message to the console.

Why it teaches pure C on iOS: It moves from simple drawing to creating interactive, stateful UI components from absolute scratch. You will be re-implementing a tiny piece of UIButton.

Core challenges you’ll face:

  • Drawing text → maps to using Core Text or -[NSString drawInRect:] for simple text
  • Managing button state → maps to using a C struct to hold properties like isPressed
  • Hit testing → maps to checking if a touch coordinate from touchesBegan: is inside the button’s CGRect
  • Handling different touch phases → maps to implementing touchesBegan, touchesEnded, and touchesCancelled to control the button’s visual state

Key Concepts:

  • Core Text: For advanced text layout, use Apple’s “Core Text Programming Guide”. For simple text, bridge to NSString and use its drawing methods.
  • State Machines: Your button is a simple state machine (up, down). Your C code will manage this state.
  • Geometry: Using CGRectContainsPoint to check if a touch is inside your button’s bounds.

Difficulty: Advanced Time estimate: 2-3 days Prerequisites: Project 4.

Real world outcome: An app displays a rectangle with “Click Me” drawn inside. Tapping and holding the rectangle turns it dark gray. Releasing the tap restores its color and prints “Button Clicked!” to the Xcode console.

Implementation Hints:

  1. In your MyView class from Project 4, define a CGRect for your button’s frame.
  2. Store a bool isPressed state variable, perhaps in a struct associated with your view.
  3. In your drawRect: C function:
    • Check the isPressed flag. Set the fill color to light gray or dark gray accordingly.
    • Draw the button’s rectangle shape: CGContextFillRect(ctx, buttonRect);
    • Draw the text. The “easiest” way is to use the Objective-C runtime to call -[NSString drawInRect:withAttributes:]. This requires creating an NSString and an NSDictionary of attributes (font, color) from C.
  4. In your touchesBegan: C function:
    • Get the touch location.
    • Use CGRectContainsPoint(buttonRect, touchLocation) for hit testing.
    • If it’s a hit, set isPressed = true; and call setNeedsDisplay.
  5. Implement touchesEnded::
    • If isPressed was true, set isPressed = false;, call setNeedsDisplay, and print your message.

Learning milestones:

  1. A button with text appears → You can combine shape and text rendering.
  2. The button’s state changes visually on press → You can manage state and connect it to the render cycle.
  3. The button performs an action on release → You have created a complete, interactive UI control.

Project 6: A Pure C Text Field

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Text Input / Keyboard Handling
  • Software or Tool: Core Text, UIKeyInput protocol
  • Main Book: “Advanced C and C++ Compiling” by Milan Stevanovic

What you’ll build: A simple text input field. When tapped, it will “become active,” show a blinking cursor, and bring up the iOS keyboard. As you type, the characters will appear on the screen.

Why it teaches pure C on iOS: This is brutally difficult but teaches an immense amount about the text input system. You have to formally adopt an Objective-C protocol (UIKeyInput) from C, manage text state, and render it with a cursor.

Core challenges you’ll face:

  • Becoming First Responder → maps to implementing canBecomeFirstResponder (must return true) and calling becomeFirstResponder on your view
  • Adopting a Protocol in C → maps to dynamically adding all required methods of the UIKeyInput protocol to your custom C view class
  • Handling keyboard input → maps to implementing insertText: and deleteBackward
  • Rendering a blinking cursor → maps to using a CFRunLoopTimer to toggle a boolean and trigger redraws

Key Concepts:

  • Input and The Responder Chain: Apple’s “Event Handling Guide for iOS” (Archived).
  • Adopting Protocols: class_addProtocol and adding the required methods.
  • Core Text Line Layout: Using CTLineCreateWithAttributedString and CTLineDraw to render your text.

Difficulty: Expert Time estimate: 1 week Prerequisites: Project 5.

Real world outcome: An app with a rectangle. Tapping it shows the iOS keyboard and a blinking vertical bar. Typing on the keyboard adds text to the rectangle. The backspace key works.

Implementation Hints:

  1. Your custom view class needs to adopt the UIKeyInput protocol. You do this by adding the required methods like hasText, insertText:, deleteBackward.
  2. You also need to implement canBecomeFirstResponder to return true.
  3. When your view is tapped (in touchesBegan), call becomeFirstResponder on it via objc_msgSend.
  4. Store the entered text in a CFMutableStringRef.
  5. In your insertText: C implementation, append the new character to your string. In deleteBackward, remove the last character. Call setNeedsDisplay after any change.
  6. Set up a repeating CFRunLoopTimer (or NSTimer via Obj-C runtime) that fires every 0.5 seconds. The timer’s callback will flip a cursorVisible boolean and call setNeedsDisplay.
  7. In drawRect:, draw the text from your CFMutableStringRef using Core Text. Then, if cursorVisible is true, calculate the position at the end of the text and draw a vertical line for the cursor.

Learning milestones:

  1. The keyboard appears → You have mastered the responder chain.
  2. Typing appears on screen → You have correctly adopted UIKeyInput and are managing text state.
  3. A cursor blinks at the correct position → You have integrated timer-based drawing with your text layout.

Project 7: Run Loop, Timers, and Animation

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Concurrency / Event Loops
  • Software or Tool: Core Foundation (CFRunLoop)
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk (for general event loop concepts)

What you’ll build: A simple animation. A circle will move from the left side of the screen to the right. This will not use Core Animation, but will be driven by a CFRunLoopTimer.

Why it teaches pure C on iOS: It solidifies your understanding of the run loop as the heart of your application. Instead of just starting it and forgetting it, you will now actively add your own event sources (a timer) to it to drive application logic and rendering.

Core challenges you’ll face:

  • Creating a CFRunLoopTimer → maps to using CFRunLoopTimerCreate to define a repeating event
  • Adding a source to the run loop → maps to using CFRunLoopAddTimer to schedule your timer
  • Driving animation from a timer → maps to the timer callback updating the circle’s X-coordinate and triggering a redraw
  • Passing context to a C callback → maps to using the info pointer in the CFRunLoopTimerContext to give your callback access to your application’s state

Key Concepts:

  • Run Loop Sources: Apple’s “Run Loop Management” documentation.
  • Timers: The CFRunLoopTimer section of the Run Loop documentation.
  • Animation Loops: The fundamental concept of updating state and redrawing on a fixed time step.

Difficulty: Intermediate Time estimate: 1 day Prerequisites: Project 4.

Real world outcome: An app launches showing a circle on the left edge of the screen. The circle smoothly animates across the screen to the right edge and stops.

Implementation Hints:

  1. In your application state, store the circle’s CGRect.
  2. Create a CFRunLoopTimerContext struct. Its most important field is info, a void* pointer. Point this to your application’s main state struct (the one holding the circle’s rect).
  3. Create a C callback function for the timer: void timerCallback(CFRunLoopTimerRef timer, void *info).
  4. Inside the callback:
    • Cast info back to a pointer to your state struct.
    • Increment the origin.x of your circle’s rect.
    • If the circle reaches the edge, invalidate the timer using CFRunLoopTimerInvalidate to stop it.
    • Trigger a redraw of your view using setNeedsDisplay.
  5. Create the timer with CFRunLoopTimerCreate, passing your callback and context. A good interval is 1.0/60.0 for a 60 FPS animation.
  6. Get the current run loop with CFRunLoopGetCurrent() and add the timer with CFRunLoopAddTimer.

Learning milestones:

  1. A timer callback fires repeatedly → You can schedule your own events on the main run loop.
  2. A circle moves across the screen → You can create animation by changing state over time.
  3. The animation stops at the edge → You can manage the lifecycle of your timer from within its own callback.

Project 8: Reading and Writing Files

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Filesystem API / Sandboxing
  • Software or Tool: Standard C I/O (stdio.h), POSIX I/O
  • Main Book: “The C Programming Language” by Kernighan & Ritchie (K&R)

What you’ll build: A bare-bones note-taking app. It will feature your C-based text field and two C-based buttons: “Save” and “Load”. Tapping “Save” writes the content of the text field to a file, and “Load” reads it back.

Why it teaches pure C on iOS: It connects your C-based UI to the underlying file system. You’ll learn about the iOS app sandbox and how to find your app’s Documents directory to persist data using standard, portable C file APIs.

Core challenges you’ll face:

  • Finding the app’s sandbox directories → maps to using Objective-C NSSearchPathForDirectoriesInDomains to find the Documents directory path
  • Constructing file paths → maps to C string manipulation to append a filename to the directory path
  • Using standard C file I/O → maps to fopen, fwrite, fread, fclose in a mobile environment
  • Converting between CFString and C strings → maps to using CFStringGetCString to get a representation you can write to a file

Key Concepts:

  • iOS App Sandbox: Understanding that your app can only write to specific directories.
  • Standard C I/O: The functions in <stdio.h>.
  • String Encodings: CFStringGetCString requires you to specify an encoding, like kCFStringEncodingUTF8.

Difficulty: Intermediate Time estimate: 1-2 days

  • Prerequisites: Project 6.

Real world outcome: You can type text into the text field, press “Save”, terminate the app, relaunch it, press “Load”, and your text will reappear in the text field.

Implementation Hints:

  1. To get the Documents directory, you’ll need to bridge to Objective-C:
    • Use NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES). This returns an NSArray.
    • Get the first object from the array, which is the path string.
    • You’ll need to perform all of this using objc_msgSend and runtime calls.
  2. Convert the Objective-C NSString path to a C string (const char*).
  3. Append your filename (e.g., /notes.txt) to the path.
  4. Save Action:
    • Convert your CFMutableStringRef from the text field into a C string using CFStringGetCString.
    • Use fopen(path, "w") to open the file for writing.
    • Use fwrite to write the C string bytes.
    • fclose the file.
  5. Load Action:
    • fopen(path, "r").
    • Use fread to read the bytes into a C buffer.
    • Create a new CFStringRef from the C buffer with CFStringCreateWithCString.
    • Set this new string as the content for your text field and trigger a redraw.

Learning milestones:

  1. Successfully write a file → You can navigate the sandbox and perform file I/O.
  2. Data persists between launches → You have achieved state persistence.
  3. Load data back into the UI → You can round-trip data from your UI to disk and back.

Project 9: A Simple Network Client

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Networking API / Asynchronous Programming
  • Software or Tool: CFNetwork framework
  • Main Book: “TCP/IP Sockets in C” by Donahoo & Calvert

What you’ll build: An app that fetches the current time from a world time API (e.g., http://worldtimeapi.org/api/ip) and displays it on screen.

Why it teaches pure C on iOS: It introduces you to C-level asynchronous networking with CFNetwork. Unlike synchronous curl, CFNetwork is run loop-based, which is the proper, non-blocking way to handle networking in a UI application.

Core challenges you’ll face:

  • Creating a CFHTTP request → maps to CFHTTPMessageCreateRequest
  • Creating a read stream from the request → maps to CFReadStreamCreateForHTTPRequest
  • Scheduling the stream on the run loop → maps to CFReadStreamScheduleWithRunLoop
  • Handling stream events via callbacks → maps to setting callbacks for events like kCFStreamEventHasBytesAvailable and kCFStreamEventEndEncountered

Key Concepts:

  • CFNetwork Programming Guide: Apple’s documentation on CFNetwork.
  • Asynchronous I/O: Understanding that network requests are not instant and must not block the main UI thread.
  • Callback-based Programming: A common pattern in C for handling asynchronous events.

Difficulty: Advanced Time estimate: 2-3 days Prerequisites: Project 7, basic understanding of HTTP.

Real world outcome: The app launches, shows “Fetching time…”, then after a moment, displays the current date and time fetched from the internet, rendered with Core Graphics.

Implementation Hints:

  1. Create the URL object: CFURLRef url = CFURLCreateWithCString(...);
  2. Create the HTTP request message: CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), url, kCFHTTPVersion1_1);
  3. Create a read stream from this request: CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
  4. Set up your client context and callbacks using CFStreamClientContext. The context’s info pointer is key for accessing your application state.
  5. Register your callbacks: CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, myStreamCallback, &context);
  6. Schedule the stream on the run loop: CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  7. Open the stream to start it: CFReadStreamOpen(stream);
  8. In your myStreamCallback function:
    • Use a switch on the event type.
    • For kCFStreamEventHasBytesAvailable, read the data using CFReadStreamRead into a buffer. Append this to a CFMutableDataRef.
    • For kCFStreamEventEndEncountered, the download is complete. Parse the data (it will be JSON), extract the time, update your UI state, and trigger a redraw. Clean up the stream.

Learning milestones:

  1. Callback fires with data → You have successfully scheduled an async operation on the run loop.
  2. HTTP response body is fully received → You can handle chunked data and know when the operation is complete.
  3. Time is displayed in the UI → You have successfully parsed data from a network call and updated the UI.

Project 10: Build a Mach-O Parser

  • File: LEARN_IOS_IN_C_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: Python, Go
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Binary Formats / Linker & Loader
  • Software or Tool: <mach-o/loader.h> header
  • Main Book: “Mac OS X and iOS Internals: To the Apple’s Core” by Jonathan Levin

What you’ll build: A command-line tool (for macOS) that reads the executable from one of your previous iOS projects and prints out its structure: the Mach-O header, load commands, sections, and symbols.

Why it teaches pure C on iOS: It makes you understand the final output of your compiler and linker. You’ll see how your C code and the frameworks you link against are represented in the binary file that actually runs on the iPhone.

Core challenges you’ll face:

  • Parsing the mach_header_64 → maps to understanding the file’s magic number, CPU type, and load command layout
  • Iterating through load commands → maps to a variable-length list of commands that tell the loader how to map the file into memory
  • Interpreting different load commands → maps to distinguishing LC_SEGMENT_64 (memory segments) from LC_LOAD_DYLIB (dynamic libraries) and LC_SYMTAB (symbol table)
  • Reading the symbol table → maps to finding the names of your C functions inside the binary

Key Concepts:

  • Mach-O File Format: The mach-o/loader.h header file is the primary specification.
  • Dynamic Linker (dyld): Understanding that load commands are instructions for the dynamic linker.
  • Symbol Tables: The data structures that map names (like _main) to addresses.

Difficulty: Advanced Time estimate: 1 week Prerequisites: Strong C, understanding of file I/O and structs.

Real world outcome: A command-line tool you can run on your Mac:

$ ./macho_parser /path/to/YourCApp.app/YourCApp

--- Mach-O Header ---
Magic: MH_MAGIC_64
CPU Type: ARM64
Load Commands: 15

--- Load Commands ---
[0] Command: LC_SEGMENT_64
    Segment: __PAGEZERO, Size: 4 GB
[1] Command: LC_SEGMENT_64
    Segment: __TEXT
    Sections:
      __text: Address=0x100003f30, Size=560 bytes
      __stubs: ...
[2] Command: LC_LOAD_DYLIB
    Path: /usr/lib/libSystem.B.dylib
[3] Command: LC_LOAD_DYLIB
    Path: /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
...
--- Symbols ---
_main
_myDrawRect
...

Implementation Hints:

  1. Open the executable file using fopen and read it into a buffer, or mmap it into memory.
  2. Cast the start of the buffer to a struct mach_header_64*. Check the magic number.
  3. The load commands immediately follow the header. You can loop ncmds times. The start of the commands is buffer + sizeof(struct mach_header_64).
  4. In each iteration, cast the current position to a struct load_command*. Read the cmd and cmdsize fields.
  5. Use a switch on lc->cmd to handle different command types. For LC_SEGMENT_64, you’ll then need to iterate through its section_64 structs.
  6. The cmdsize tells you how many bytes to advance your pointer to get to the next load command.
  7. The LC_SYMTAB command gives you offsets to the symbol table and string table. You can use these to read the names of all functions and symbols.

Learning milestones:

  1. Header is parsed correctly → You can identify a Mach-O file and its basic properties.
  2. All load commands are iterated → You understand the structure of the file’s metadata.
  3. Dynamic library dependencies are listed → You can see exactly which frameworks your app links against.
  4. Your C function names are found in the symbol table → You have connected your source code to its binary representation.

Summary

Project Difficulty Time Main Concept Taught
1. Hello, Console! Intermediate Hours App Entry & Linking
2. Core Foundation Intermediate 1 Day Manual Memory Management
3. Hello, Window! Advanced 1-2 Days Objective-C Runtime Bridge
4. Drawing Pad Advanced 2-3 Days Core Graphics & Custom Views
5. C UI Button Advanced 2-3 Days Stateful UI from Scratch
6. C Text Field Expert 1 Week Keyboard Input & Protocols
7. Run Loop & Timers Intermediate 1 Day Event-Driven Animation
8. File I/O Intermediate 1-2 Days Filesystem & Sandboxing
9. Network Client Advanced 2-3 Days Asynchronous Networking
10. Mach-O Parser Advanced 1 Week Binary File Formats