
Learning Swift from a C# Perspective
String Literals & Initialization #
1. Core Concepts #
- Concept Explanation: The
Stringtype in Swift is a fast, Unicode-compliant text processing tool. Its syntax is designed to be lightweight, similar to C, but the underlying implementation is very modern. Unlike Objective-C’sNSString, Swift’sStringis a struct (Value Type), not a class. - Key Syntax:
""(Double quotes),"""(Multiline strings),String()(Initialization),isEmpty - Official Note:
Swift’s
Stringtype is bridged with Foundation’sNSStringclass. If you importFoundation, you can accessNSStringmethods on aStringwithout casting.
2. Example Analysis #
Source Documentation Code:
let someString = "Some string literal value"
// Multiline String Literals
let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
// Initializing an empty string
var emptyString = "" // Empty string literal
var anotherEmptyString = String() // Initializer syntax
if emptyString.isEmpty {
print("Nothing to see here")
}Logic Explanation:
Swift uses double quotes to define single-line strings. Multiline strings are wrapped in three double quotes """, and the opening and closing quotes must be on their own lines. Indentation rules are determined by the position of the closing """, keeping the code layout clean. The isEmpty property is an efficient way to check if a string is empty.
3. C# Developer Perspective #
Concept Mapping: C# string also uses double quotes. Swift’s multiline strings are similar to Raw String Literals ("""...""") introduced in C# 11 or the older Verbatim String (@"...").
C# Comparison Code:
// C#
string someString = "Some string literal value";
// C# 11 Raw String Literal (Similar to Swift's multiline handling)
string quotation = """
The White Rabbit put on his spectacles.
"Begin at the beginning," the King said.
""";
// Empty string check
string emptyString = "";
bool isEmpty = string.IsNullOrEmpty(emptyString); // Or emptyString.Length == 0Key Differences Analysis:
- Syntax: Swift’s multiline string indentation handling is very smart; it automatically ignores whitespace aligning with the closing
""". Perfect support for this in C# was only recently achieved with Raw String Literals in C# 11. - Behavior: Swift recommends using the
isEmptyproperty, whereas C# developers are accustomed tostring.IsNullOrEmpty()or checkingLength == 0.
Mutability & Value Types #
1. Core Concepts #
- Concept Explanation: This is one of the biggest differences between Swift and many other languages. Swift’s
Stringis a Value Type, not a Reference Type. Furthermore, whether a string is mutable depends entirely on whether it is declared as a variable (var) or a constant (let). - Key Syntax:
var(Mutable),let(Immutable),struct(Value Type)
2. Example Analysis #
Source Documentation Code:
var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"
let constantString = "Highlander"
// constantString += " and another Highlander"
// Compile error: a constant string cannot be modifiedLogic Explanation:
When a String is passed to a function or assigned to another variable, a copy actually occurs. Although the Swift compiler performs copy-on-write optimization (meaning copying only happens when content is actually modified), semantically, you possess an independent copy of that string.
3. C# Developer Perspective #
Concept Mapping: C#’s string is a Reference Type, but it is Immutable. To modify a string, C# typically creates a new object or uses StringBuilder.
C# Comparison Code:
// C#
string str = "Horse";
str += " and carriage"; // Actually creates a new string object and reassigns it
// C# does not have a direct equivalent to 'let' for constants preventing reassignment-induced modification,
// unless using const (compile-time) or readonly (fields only).Key Differences Analysis:
- Syntax: Swift directly supports string modification (concatenation) using
var, similar to an easy-to-use version of C#’sStringBuilder, but syntactically looks like standard string operations. - Behavior: This is the biggest trap. In C#, a string variable holds a reference; in Swift,
Stringis a Struct.- C#:
string a = "hi"; string b = a;-> a and b point to the same memory on the heap. - Swift:
var a = "hi"; var b = a; b += "!"-> Modifying b does not affect a, because a copy occurred upon assignment (logically).
- C#:
Concatenation & Interpolation #
1. Core Concepts #
- Concept Explanation: Swift provides intuitive operators for string concatenation and powerful string interpolation, allowing variables or expressions to be embedded within strings.
- Key Syntax:
+,+=,append(),\(expression)
2. Example Analysis #
Source Documentation Code:
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
var instruction = "look over"
instruction += string2
// String Interpolation
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"Logic Explanation:
String interpolation uses the backslash and parentheses syntax \(...). This is more intuitive and strongly typed than traditional format strings.
3. C# Developer Perspective #
Concept Mapping: Equivalent to String Concatenation and String Interpolation in C#.
C# Comparison Code:
// C#
int multiplier = 3;
// C# uses the $ sign and curly braces
string message = $"{multiplier} times 2.5 is {multiplier * 2.5}";Key Differences Analysis:
- Syntax: Swift uses
\(), while C# uses{}(combined with$). - Behavior: Both compile down to efficient string construction calls. Note that Swift’s interpolation parentheses cannot contain unescaped backslashes or line breaks.
Unicode, Characters & Counting #
1. Core Concepts #
- Concept Explanation: Swift’s
Stringis built upon Unicode Scalar Values. Most notably, it supports Extended Grapheme Clusters. A “human-readable character” (likeéor 🇹🇼) might be composed of one or more Unicode scalars, but in Swift, they are treated as a singleCharacter. - Key Syntax:
Character,countproperty,\u{n}
2. Example Analysis #
Source Documentation Code:
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e followed by acute accent
// Both are considered the same character in Swift
var word = "cafe"
word += "\u{301}" // Add acute accent
print("the number of characters in \(word) is \(word.count)")
// Output "the number of characters in café is 4" (Visually it's still one word, length remains 4)Logic Explanation:
This is the most powerful yet complex part of Swift string handling. The count property returns “how many characters a human sees,” not how many bytes or 16-bit units are used underneath. Because it needs to calculate grapheme cluster boundaries, accessing count may require iterating through the entire string and is not an O(1) operation.
3. C# Developer Perspective #
Concept Mapping: C#’s char is a 16-bit unit (UTF-16 code unit). string.Length in C# returns the number of 16-bit units, not the actual character count.
C# Comparison Code:
// C#
string word = "cafe";
word += "\u0301"; // Add combining accent
Console.WriteLine(word.Length);
// Output 5 (Because 'e' and the accent are two separate chars)Key Differences Analysis:
- Syntax: No major difference, but Swift Unicode escapes use
\u{...}allowing variable length hex, whereas C# uses\uXXXXor\UXXXXXXXX. - Behavior: Extremely Important!
- Emoji Handling: An Emoji (e.g., 🐶) usually has a
string.Lengthof 2 (Surrogate Pair) in C#; in Swift,.countis 1. - Performance: C#’s
Lengthis O(1); Swift’scountrequires iteration, making it O(n). Be mindful of performance when callingcountfrequently within loops.
- Emoji Handling: An Emoji (e.g., 🐶) usually has a
Accessing and Modifying Strings #
1. Core Concepts #
- Concept Explanation: Due to the Unicode complexity mentioned above (characters having variable lengths), Swift does not allow integer indexing (e.g.,
str[0]) to access strings. You must useString.Index. - Key Syntax:
startIndex,endIndex,index(before:),index(after:),index(_:offsetBy:)
2. Example Analysis #
Source Documentation Code:
let greeting = "Guten Tag!"
// Access first character
greeting[greeting.startIndex] // G
// Access specific position (e.g., the 7th character)
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a
// Insert
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// Remove
welcome.remove(at: welcome.index(before: welcome.endIndex))Logic Explanation:
You cannot write greeting[7] directly. You must first calculate the String.Index object representing “the 7th grapheme cluster” and then use it to retrieve the value. This prevents random access from slicing a multi-byte character (like cutting an Emoji in half).
3. C# Developer Perspective #
Concept Mapping: C# allows integer indexing str[i], but this accesses a char (UTF-16 code unit), which is not necessarily a complete character.
C# Comparison Code:
// C#
string greeting = "Guten Tag!";
char c = greeting[7]; // Directly use integer index 'a'
// Insert and Remove (C# string is immutable, creates new string)
string welcome = "hello";
welcome = welcome.Insert(welcome.Length, "!");
welcome = welcome.Remove(welcome.Length - 1);Key Differences Analysis:
- Syntax: Swift’s indexing syntax is very verbose (
index(_:offsetBy:)). This forces developers to be aware of the cost of string traversal. - Behavior: In C#, you are used to
for (int i=0; i<str.Length; i++), but in Swift, you should preferfor char in stringor high-order functions. If you need frequent random access, consider converting theStringto anArray(Array(str)), but note that this loses some Unicode handling features and increases memory consumption.
Substrings #
1. Core Concepts #
- Concept Explanation: When you slice a Swift string, the returned type is not
String, butSubstring. ASubstringshares memory with the original string, which is a performance optimization. - Key Syntax:
prefix(_:),[range],Substringtype,String(substring)
2. Example Analysis #
Source Documentation Code:
let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning type is Substring, it reuses greeting's memory
// If storing long-term, convert back to String
let newString = String(beginning)Logic Explanation:
While Substring is efficient, it is not suitable for long-term storage. As long as a Substring exists, the memory for the entire original String cannot be released.
3. C# Developer Perspective #
Concept Mapping: Traditionally, C#’s Substring() allocates new memory and copies the string. However, in newer C# versions (Core/Standard), the concept of Span<char> or ReadOnlySpan<char> is very similar to Swift’s Substring—they are “views” of the original memory.
C# Comparison Code:
// C# (Traditional)
string greeting = "Hello, world!";
int idx = greeting.IndexOf(',');
string beginning = greeting.Substring(0, idx); // Allocates new memory
// C# (High-performance Span)
ReadOnlySpan<char> span = greeting.AsSpan();
ReadOnlySpan<char> slice = span.Slice(0, idx); // Zero allocation, similar to Swift SubstringKey Differences Analysis:
- Behavior: Swift enforces a type distinction between
StringandSubstring, preventing accidental long-term retention of references to large strings. C#’sSubstringdirectly returns astring(causing a copy), unless you explicitly useSpan.
Comparing Strings #
1. Core Concepts #
- Concept Explanation: Swift string comparison uses “Canonical Equivalence”. This means if two strings look the same, they are equal, even if the underlying Unicode composition differs.
- Key Syntax:
==,!=,hasPrefix(),hasSuffix()
2. Example Analysis #
Source Documentation Code:
// "é" (Single scalar) vs "e" + "́" (Combined scalar)
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion {
print("These two strings are considered equal")
}Official Note:
Swift’s string and character comparisons are not locale-sensitive.
3. C# Developer Perspective #
Concept Mapping: C#’s == is based on UTF-16 code unit content comparison (ordinal) and does not perform Unicode normalization.
C# Comparison Code:
// C#
string s1 = "\u00E9";
string s2 = "e\u0301";
Console.WriteLine(s1 == s2); // False! Because underlying char sequences differ
// To achieve Swift's effect, normalization is required
Console.WriteLine(s1.Normalize() == s2.Normalize()); // TrueKey Differences Analysis:
- Behavior: This is another potential source of bugs. Swift’s
==is smarter (but slower) as it handles Unicode normalization automatically. C#’s==is faster but strictly compares thecharsequence. If you need similar comparison logic in C#, you must manually callString.Normalize().