Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
142: C Interop in Swift
I've got a bit of a legacy situation. I'm working with a small C library that handles some low-level hardware telemetry—nothing fancy, just a few structs and functions—and I need to pull that data into a Swift app. Now, Swift is great, but it doesn't magically know how to talk to a .c file just because they're in the same folder. Let's figure out how to bridge the gap together.
Getting the compiler to see the C code
I'll start by creating a simple C header. Let's call it Telemetry.h. I've got a struct for a sensor reading and a function to simulate a reading coming from the hardware.
// Telemetry.h
typedef struct {
int sensor_id;
float value;
} SensorReading;
SensorReading get_latest_reading(int id);
And the implementation in Telemetry.c:
// Telemetry.c #include "Telemetry.h" SensorReading get_latest_reading(int id) { // In a real world, this would hit a register. // Here, we'll just fake it. SensorReading r = { .sensor_id = id, .value = 42.0f }; return r; }Now, if I go into my Swift file and try to call
get_latest_reading(id: 1), the compiler screams at me.Cannot find 'get_latest_reading' in scope. This is the first hurdle. Swift needs a "map" to understand what the C world is offering. In a standard Xcode project, this is where the Bridging Header comes in. I'll create a file calledYourProject-Bridging-Header.hand just add#import "Telemetry.h". Once I tell the build settings where that header lives, suddenly, the Swift compiler stops complaining. It's like the lights just turned on.The "Type Soup" problem
Now that the function is visible, I try to use it. Simple enough, right?
let reading = get_latest_reading(1) print("Sensor \(reading.sensor_id) is at \(reading.value)")Surprisingly, this works perfectly. Swift imported the C struct
SensorReadingas a Swift struct. But here's where it gets messy. What if the C library doesn't return a value, but instead returns a pointer to a value? This is how most real C libraries actually work to avoid copying large structs on the stack.Let's change the C function to return a pointer:
// Telemetry.h updated SensorReading* get_reading_ptr(int id);I try to call this in Swift, and now I'm staring at a type I've never seen before:
UnsafeMutablePointer<SensorReading>. I'll be honest, the word "Unsafe" in the type name is a bit jarring. It's Swift's way of saying, "I'm letting you do this, but if you crash the app by accessing garbage memory, it's on you."If I try to access
reading.valuenow, it fails becausereadingis a pointer, not the struct itself. I need to "dereference" it. In Swift, we use thepointeeproperty for this.let ptr = get_reading_ptr(1) if let reading = ptr?.pointee { print("The value is \(reading.value)") }Strings and the dreaded char*
The real headache starts with strings. C strings are just pointers to characters ending in a null terminator. Swift strings are complex, UTF-8 encoded objects. They don't speak the same language.
Let's add a function to our C library that returns the name of the sensor:
// Telemetry.h const char* get_sensor_name(int id);When I call
get_sensor_name(1)in Swift, I get back anUnsafePointer<Int8>. That's a C string. If I try to print it, I just get the memory address. Not helpful. I need to convert this "C-style string" into a "Swift-style string".I'll try the
String(cString:)initializer:let cName = get_sensor_name(1) let swiftName = String(cString: cName) print("Sensor name is \(swiftName)")That works, but there's a catch.
String(cString:)copies the data from the C memory into a new Swift-managed string. That's usually what you want. But if the C library expects you to free that memory afterward (which happens a lot), you'll have a memory leak. I've spent way too many hours hunting down leaks because I forgot that the C side of the bridge doesn't have Automatic Reference Counting (ARC). You have to manually manage what you allocate in C, even if you're calling it from Swift.
📋 Practical Task
Implement a C-to-Swift Memory Buffer Reader
You are given a small C library that simulates a hardware buffer. The library provides a function that returns a pointer to a buffer of integers and the size of that buffer.
The C Code:
// BufferLib.h
typedef struct {
int* data;
int length;
} DataBuffer;
DataBuffer get_hardware_buffer();
void release_buffer(DataBuffer buffer);
Your Goal: Write a Swift function called fetchHardwareData() that:
- Calls
get_hardware_buffer()to retrieve theDataBufferstruct. - Converts the C array (the
UnsafeMutablePointer<Int32>) into a native Swift[Int]array. - Calls
release_buffer()to prevent a memory leak. - Returns the Swift array.
Hint: Look into the UnsafeBufferPointer initializer that takes a base address and a count to make the conversion to a Swift array easier.
There are no comments for now.