Swift is a great language and often used to build iOS and macOS applications. This post is designed to help programmers with C++ experience get started programming in Swift with side-by-side C++ and Swift code for the following topics:

  1. Fundamental Types
    a. Int
    b. Double
    c. Boolean
    d. String
    e. Tuple
  2. Print To Console
  3. Operators
    a. Assignment
    b. Arithmetic
    c. Comparison
    d. Optionals
    e. Force Unwrap Optional
    f. Boolean Logic
  4. Strings
    a. String Literals
    b. Multiline Strings
    c. String Interpolation
    d. Substrings
  5. Arrays
  6. Sets
  7. Dictionaries
  8. Control Flow
    a. If Else
    b. If Let
    c. For In
    d. While
    e. Switch
  9. Functions
  10. Closures
  11. Structs
  12. Classes
  13. Error Handling
  14. Enums
  15. Protocols
  16. Extensions
  17. Generics

Note: to keep the examples concise in this post, the C++ examples assume the standard std namespace is used and relevant libraries like iostream, optional, array, vector, set, map, assert, and tuple are included.

The Swift examples in this post only assume Foundation is imported.

Fundamental Types

Swift fundamental types are similar to C++ and other languages.

Int

// C++
1
// Swift
1
Side-by-side available only in wider browser windows.

Double

// C++
1.0
// Swift
1.0
Side-by-side available only in wider browser windows.

Boolean

// C++
true
false
// Swift
true
false
Side-by-side available only in wider browser windows.

String

// C++
"Swift tutorial"
// Swift
"Swift tutorial"
Side-by-side available only in wider browser windows.

Tuple

// C++
tuple{1, true};
// Swift
(1, true)
Side-by-side available only in wider browser windows.
// C++
cout << "Swift examples";
cout << endl;
// Swift
print("Swift examples")
Side-by-side available only in wider browser windows.

Operators

Assignment

In Swift, var is used to define a variable that allows mutation. The type annotation in Swift is often optional. The Swift example below could be written with the type annotation as var count: Int = 0.

// C++
int count = 0
count = 1
// Swift
var count = 0
count = 1
Side-by-side available only in wider browser windows.

Alternatively, let is used in Swift to define a variable that does not allow mutation. Swift let is similar in some ways, but not equivalent to, const in C++:

// C++
const int phones = 5;

// Attempting mutation
// causes an error
// phones = 3;
// Swift
let phones = 5

// Attempting mutation
// causes an error
//phones = 3
Side-by-side available only in wider browser windows.

Read the following article for a more detailed overview of the differences between let and var in Swift, and when to use each:

let vs var in Swift
Learn what is the difference between let and var in Swift, and when to use var and let variables.

Arithmetic

Swift and C++ share arithmetic syntax.

// C++
1 + 2;
1 - 2;
1 * 2.0;
1 / 2.0;

int count = 2;
count += 1;
count -= 1;
count *= 2;
count /= 2;
count %= 4;
// Swift
1 + 2
1 - 2
1 * 2.0
1 / 2.0

var count = 2
count += 1
count -= 1
count *= 2
count /= 2
count %= 4
Side-by-side available only in wider browser windows.

Comparison

// C++
int count = 3;
count == 2; // false
count != 2; // true
count > 1; // true
count < 1;  // false
count >= 2; // true
count <= 2; // false
// Swift
var count = 3
count == 2 // false
count != 2 // true
count > 1 // true
count < 1  // false
count >= 2 // true
count <= 2 // false
Side-by-side available only in wider browser windows.

Optionals

In Swift, optionals are a first class citizen of the language expressed with ?. In C++, optionals are a distinct type. The ?? operator in Swift is very similar to the value_or(_) optional function in C++.

// C++
optional<int> count;
*count // NULL

count.emplace(3);
count // optional<int>

count.value_or(1); // 3

count.reset();
count.value_or(1); // 1
// Swift
var count: Int?
count // nil

count = 3 
count // optional(3)

count ?? 1 // 3 

count = nil
count ?? 1 // 1
Side-by-side available only in wider browser windows.

Force Unwrap Optional

The ! is used to forcefully unwrap an optional in Swift, meaning the optional value is forcefully read and an error is thrown if the optional does not have a value. In C++, the optional function value() has similar behavior.

// C++
optional count;

// bad_optional_access
count.value();

count.emplace(1);
count.value(); // 1
// Swift
var count: Int?

// Unexpectedly found nil
count!

count = 1
count! // Optional(1)
Side-by-side available only in wider browser windows.

Boolean Logic

Swift and C++ share boolean logic syntax.

// C++
bool value = false;
bool check = true;

// not
!value;

// and
value && check;

// or
value || check;

// compound
!value && (count == 1);
// Swift
let value = false
let check = true

// not
!value

// and
value && check

// or
value || check 

// compound
!value && (count == 1)
Side-by-side available only in wider browser windows.

Strings

String literals in Swift and C++ are similar, but string manipulation and substring indexes are very different.

String Literals

// C++
string kind = "Advanced";
string topic = "Swift";
// Swift
let kind = "Advanced"
let topic = "Swift"
Side-by-side available only in wider browser windows.

Multiline Strings

// C++
string description =
  "Learn Swift,\n"
  "for iOS and macOS!";
// Swift
let description = """
	Learn Swift,
	for iOS and macOS!
"""
Side-by-side available only in wider browser windows.

String Interpolation

Swift's implementation of string interpolation uses \() in string literals to interpolate values and initialize a string dynamically. There is no direct equivalent in C++, with format in C++20 having the closest functionality.

// C++
string domain = "advswift";
string tld = "com";
string url = format(
    "{}.{}", domain, tld
);
// Swift
let domain = "advswift"
let tld = "com"
let url = "\(domain).\(tld)"
Side-by-side available only in wider browser windows.

Substrings

// C++
string colors = "OYRGBIV";
colors.substr(0, 2); // "OY"
colors.substr(5); // "IV"

colors.substr(2, 3); // "RGB"
// Swift
let colors = "OYRGBIV"
colors.prefix(2) // "OY"
colors.suffix(2) // "IV"

let start = colors.index(
    colors.startIndex, 
    offsetBy: 2
)
let end = colors.index(
    colors.endIndex, 
    offsetBy: -3
)
colors[start...end] // "RGB"
Side-by-side available only in wider browser windows.

Arrays

// C++
vector<string> devices = {
    string("iPhone"), "iPad"
};
devices[1]; // "iPad"

// Insert
devices.push_back("iMac");

devices.insert(
    devices.begin(), 
    "Newton"
);

// Remove
devices.erase(
    devices.begin(), 
    devices.begin()+2
);

devices.pop_back();

// Contains
find(
    devices.begin(), 
    devices.end(), 
    "iCar"
) != devices.end();
// Swift
var devices = [
    "iPhone", "iPad"
]
devices[1] // "iPad"

// Insert
devices.append("iMac")

devices.insert("Newton", at: 0)




// Remove
devices.removeFirst(2)

devices.removeLast()

devices.remove(at: 2)


// Contains
devices.contains("iCar")
Side-by-side available only in wider browser windows.

Sets

// C++
set<string> devices = {
    string("iPhone"), "iPad"
};

// Insert
devices.insert("iMac");

// Remove
devices.erase("Newton");

// Contains
devices.find("iCar") != devices.end();
// Swift
var devices = Set([
    "iPhone", "iPad"
])

// Insert
devices.insert("iMac")

// Remove
devices.remove("Newton")

// Contains
devices.contains("iCar")
Side-by-side available only in wider browser windows.

Dictionaries

// C++
map<string,int> clothes = {
    {"pants", 2},
    {"shirts", 4}
};

// Access
clothes["pants"]; // 2
clothes["shoes"]; // NULL

clothes["shirts"] = 5;
clothes["socks"] = 2;

// Remove
clothes.erase("pants");
// Swift
var clothes = [
	"pants": 2, 
	"shirts": 4
]

// Access
clothes["pants"] // Optional(2)
clothes["shoes"] // nil

clothes["shirts"] = 5
clothes["socks"] = 2

// Remove
clothes["pants"] = nil
Side-by-side available only in wider browser windows.

Control Flow

If Else

// C++
bool swift = true;
bool objc = false;

if (swift) { /* ... */ }

if (swift) {
	/* ... */
} else if (objc) {
	/* ... */
} else {
	/* ... */
}
// Swift
var swift = true
var objc = false

if swift { /* ... */ }

if swift {
	/* ... */
} else if objc {
	/* ... */
} else {
	/* ... */
}
Side-by-side available only in wider browser windows.

If Let

// C++
optional<string> name{"Swift"};

if (auto result = name) {
    // branch taken if
    // name is not NULL
}
// Swift
var name: String? = "Swift"

if let name = name {
    // branch taken if
    // name is not nil
}
Side-by-side available only in wider browser windows.

For In

// C++
vector<int> values = {1,2,3,4,5};

for (auto & element : values) {
	/* ... */
}

for (int i = 0; i < 5; i++) {
	/* ... */
}

auto it = values.begin();
for (it; it != values.begin()+3; ++it) {
	// 1, 2, 3
}

auto it = values.end() - 3;
for (it; it != values.end(); ++it) {
	// 3, 4, 5
}
// Swift
let values = [1,2,3,4,5]

for value in values {
	/* ... */
}

for value in 0..<5 {
	/* ... */
}

for value in values[...2] {
    // 1, 2, 3
}


for value in values[2...] {
    // 3, 4, 5
}
Side-by-side available only in wider browser windows.

While

// C++
bool proceed = true;
while (proceed) {
	/* ... */
	proceed = false;
}
// Swift
var proceed = true
while proceed {
	/* ... */
	proceed = false
}
Side-by-side available only in wider browser windows.

Switch

The Swift switch statement can be used to branch based on the value of a type, including the tuple type. Each case inside of the switch statement contains the match pattern inside of (). The keyword break is used to exit the switch statement and the keyword default is used to match all other cases.

C++ has a switch keyword. Unlike Swift, the C++ switch statement requires cases to be constants or literals so there is no direct equivalent example.

// Swift
let outcome = (true, false)

switch outcome {
case (true, true):
	/* ... */
	break
case (false, false):
	/* ... */
	break
default:
    /* ... */
    break
}

Functions

In Swift the func keyword is used to specify a function definition, arguments are specified inside of (), and the -> syntax is used to specify the return type. In C++, a return type is specified first and no explicit keyword like func is used.

Swift also has slightly different argument definition syntax. Where Swift declares a function verify with a String parameter name like verify(name: String) -> Bool, C++ declares the same function like bool verify(string name).

// C++
void greet() {
	cout << "Hello" >> endl;
}

bool verify(string name) {
	return name.length() > 1;
}
// Swift
func greet() {
    print("Hello")
}

func verify(name: String) -> Bool {
    return name.count > 1
}
Side-by-side available only in wider browser windows.

A key difference between Swift and C++ are named function arguments. All Swift function arguments are required to be named in a function call except where:

  1. _ is used, like in cut(_ str: String, len: Int = 10), removing the argument name from the function call
  2. a default value is specified, like = 10 in cut(_ str: String, len: Int = 10), making the argument optional
  3. a different name is declared, like of in firstWord(of str: String), changing the required name in the function call to the declared name

The Swift functions below would be called like this:

  1. cut("hi ho") or cut("hi ho", len: 10)
  2. firstWord(of: "hi ho")

The C++ equivalents would be called like this:

  1. cut("hi ho") or cut("hi ho", 10)
  2. firstWord("hi ho")
// C++
string cut(string str, int len = 10) {
	if (str.length() > len) {
		return str.substr(0, len-3) + "...";
	} else {
		return str;
	}
}
// Swift
func cut(_ str: String, len: Int = 10) -> String {
	if str.count > len {
		return str.prefix(len-3) + "..."
	} else {
		return str
	}
}
Side-by-side available only in wider browser windows.
// C++
optional<string> firstWord(string str) {
	if(!str.empty()) {
		return str.substr(0, str.find(" "));
	} else {
		return nullopt;
	}
}
// Swift
func firstWord(of str: String) -> String? {
	if let substr = str.split(separator: " ").first {
		return String(substr)
	}
	else {
		return nil
	}
}
Side-by-side available only in wider browser windows.

Closures

Closures in Swift follow function conventions except that closures and closure arguments are often unnamed. In other words, a function definition func sort(a: Int, b: Int) -> Bool { ... } as a closure would be { (a, b) -> Bool in ... }. The in keyword denotes the scope in which the arguments a, b are defined.

Using {} defines a closure. Using () defines a closure type. For example, the closure { (a, b) -> Bool in ... } has a closure type of (Int, Int) -> Bool. Closure types are often defined as function arguments for callback and completion handlers.

Although capitalized, the Void keyword in a closure type definition has the same meaning, no return value, as the void keyword in C++.

// C++
vector<string> codes = {
    "c4", "a3", "b1", "b2"
};

vector<string> filteredCodes;
copy_if(
	codes.begin(), 
	codes.end(), 
	std::back_inserter(filteredCodes), 
	[](string code) {
		return code.substr(0, 1) == "b";
	}
);

// Swift
var codes = [
    "c4", "a3", "b1", "b2"
]

_ = codes.filter({ item -> Bool in
	return item.prefix(1) == "b"
})
Side-by-side available only in wider browser windows.
// C++
void task(function<void()> callback) {
    /* ... */
	callback();
}

void task(function<void(vector<int>)> completion) {
    /* ... */
    completion({1,2});
}
// Swift
func task(callback: (() -> Void)) {
	/* ... */
	callback()
}

func task(completion: (([Int]) -> Void)) {
	/* ... */
	completion([1,2])
}

Side-by-side available only in wider browser windows.

Note: You may encounter a shorthand syntax using $ when looking at Swift closure examples. Using $ followed by a N number like $0 is shorthand for the Nth argument in a closure. Further, the return type and return keyword can be omitted in some cases.

Consider the following Swift example:

// Swift
_ = codes.sorted(by: { (a, b) -> Bool in
    return a < b
})

The Swift shorthand may look like:

// Swift
_ = codes.sorted(by: { 
   $0 < $1 
})

Structs

Like C++, a struct in Swift can contain variables and functions. Unlike C++, a struct in Swift is copied on write. This means modifying a struct copies the struct and does not modify the original reference.

// C++
struct Context {
	string source;
    int iterations;

	// Custom Initializer
    Context2(string source): 
        source(source), 
        iterations(0) {};


	// Methods
	string description() {
		return source + " " + to_string(iterations);
	}

	void increment() {
		iterations += 1;
	}
};
// Swift
struct Context {
	var source: String
	var iterations: Int

    // Custom initializer
	init(source: String) {
		self.source = source
		self.iterations = 0
	}

	// Methods
	func description() -> String {
		return "\(self.source): \(self.iterations)"
	}

	// mutating is required 
	// for methods that modify 
	// the struct's variables
	mutating func increment() {
		self.iterations += 1
	}
}
Side-by-side available only in wider browser windows.
// C++
Context context = {
	.source = "web",
	.iterations = 1
};

cout << context.description();
cout << endl;
context.increment();
// Swift
var context = Context(
    source: "web", 
    iterations: 5
)

print(context.description())
context.increment()
Side-by-side available only in wider browser windows.

The following example better illustrates Swift's copy-on-write mechanics:

// Swift
struct Context {
    var iterations: Int
}

var context = Context(iterations: 1)
var ref = context

ref.iterations = 2

// The original struct reference 
// context is not modified
ref.iterations != context.iterations

Classes

The class syntax in Swift is similar to a struct, using the class keyword instead of the struct keyword. Unlike a struct, a Swift class is not copied-on-write.

// C++
class Context {
public:
	string source;
	int iterations;

	Context(string source) {
		this->source = source;
		this->iterations = 0;
	}

	string description() const {
		return source + " " + to_string(iterations);
	}

    void increment() {
		iterations += 1;
    }
};
// Swift
class Context {
	var source: String
	var iterations: Int


	init(source: String) {
		self.source = source
		self.iterations = 0
	}

	func description() -> String {
		return "\(self.source): \(self.iterations)"
	}

	func increment() {
		self.iterations += 1
	}
}
Side-by-side available only in wider browser windows.
// C++
Context context = Context("web", 5);
cout << context.description();
cout << endl;
context.increment();
// Swift
var context = Context(source: "web")
print(context.description())
context.increment()
Side-by-side available only in wider browser windows.

Error Handling

Swift uses a do catch syntax for error handling similar to try catch in C++. However, Swift's error definition and function annotation are different. Swift errors are defined as enums conforming to the Error protocol. A Swift function that can throw an error is annotated with throws in the function definition, and the function call is annotated with try inside of a do clause or function annotated with throws.

// C++
class programerror: public exception {
  // Error Interface
} ProgramError;
// Swift
enum ProgramError: Error {
    case failed
    case terminated
}
Side-by-side available only in wider browser windows.
// C++
try {
	task([]() {});
}
catch (exception & e) {
    // Handle error
}
// Swift
func task() throws { 
    /* ... */ 
}

do {
    try task(completion: {});
}
catch ProgramError.failed {
    // Handle specific error
}
catch (let e) {
    // Handle error
}
Side-by-side available only in wider browser windows.

To learn more about creating custom errors in Swift, read:

Custom Errors in Swift
Learn how to create, extend, throw, and handle custom error types in Swift.

Enums

An enum in Swift is similar to an enum in C++ with a notable difference, Swift enum cases can contain associated values. In the following example, the case stopped(Error) contains an associated value of type Error. In the switch statement, case .stopped(let error) maps the associated value to the variable error in the case clause.

There is no direct equivalent in C++ for associated enum values.

// Swift
enum Status {
	case initial
	case complete
	case inProgress
	case stopped(Error)
}

var status = Status.initial
status = Status.stopped(
    ProgramError.failed
)

switch status {
	case .initial:
		break
	case .complete:
		break
	case .inProgress:
		break
	case .stopped(let error):
		// Handle error
		break
}

Protocols

A protocol in Swift can be applied to any type, whereas in C++ abstract classes can only be applied to classes. This enables Swift developers to use protocols as types, driving powerful use cases (see CustomStringConvertible in the Extensions section).

// C++
class Tracker {
    public:
        optional<string> currentTask;

        virtual void started(string task);
        virtual void completed(string task);
};
// Swift
protocol Tracker {
	var currentTask: String? { get }

	func started(task: String)
	func completed(task: String)
}
Side-by-side available only in wider browser windows.

Extensions

A powerful feature of Swift not available in C++ is Extensions. Extensions allow for methods and computed properties to be added to any type, including protocol types. Define an extension using the extension keyword followed by the type name.

// Swift
extension String {
	var quoted: String {
		return "'\(self)'"
	}
}

// Only String type was extended
print("Swift".quoted) // "Swift"

// Any Type can be extended in Swift, 
// including CustomStringConvertible 
// which print uses to convert types 
// to a string
extension CustomStringConvertible {
	var quoted: String {
		return "'\(self)'"
	}
}

print("Swift".quoted) // "Swift"
print([1,2].quoted) // "[1,2]"

Generics

Generics in Swift are specified inside of <> between the function name and arguments. One or more generic types are specified in the format name: type. The name is then used in the argument definition as a generic type.

In the following example, T: CustomStringConvertible means a generic type T conforms to CustomStringConvertible and the function quote expects a single argument of type T.

// C++
template <typename T>
string quote(T x) {
	return "'" + string(x) + "'";
}

cout << quote("Hi") << endl;

// Unlike Swift, this fails 
// without a specific cast
cout << quote(vector<int>{1,2}) 
cout << endl;
// Swift
func quote<T: CustomStringConvertible>
(_ x: T) -> String {
	return "'\(x)'"
}

print(quote("Hi")) // "Hi"

// Extending protocols in Swift
// is powerful!
print(quote([1,2])) // "[1,2]"
Side-by-side available only in wider browser windows.

Swift Tutorial For C++ Engineers

That's it! By mapping Swift concepts to C++ equivalents, you can quickly learn the fundamentals of Swift and start building iOS and macOS applications.