Kotlin Swift Control Flow Statements

Master conditional, iterative, and jump statements for cross-platform mobile development

Introduction to Control Flow in Mobile Development

Control flow statements govern how your application executes code--determining which paths are taken, which operations repeat, and how your app responds to different conditions and user interactions. Whether you are building an Android app in Kotlin or an iOS application in Swift, understanding control flow patterns is essential for creating responsive, efficient mobile experiences.

Kotlin and Swift share many conceptual foundations that make cross-platform development achievable. Both languages offer modern features including type inference, optional safety, and functional programming capabilities that influence how control flow is implemented.

For teams evaluating cross-platform approaches, understanding these language fundamentals helps inform decisions between native development with Kotlin and Swift versus frameworks like React Native or Flutter.

Why Control Flow Mastery Matters

Modern mobile applications require sophisticated state management and decision-making capabilities. A social media feed must decide whether to load more content based on scroll position; a navigation app must continuously evaluate traffic conditions and recalculate routes; an e-commerce app must validate form inputs and manage shopping cart states. These scenarios all rely on robust control flow implementations.

Consider a real-world example: when implementing an authentication flow, you need to handle numerous states including initial loading, credential validation, network error recovery, and session management. Kotlin's when expressions and Swift's switch statements provide type-safe ways to handle these state transitions, reducing bugs and improving code maintainability. Both languages enforce exhaustiveness checking, ensuring you cannot accidentally miss a critical state that could lead to crashes in production applications.

For mobile developers working with platform-specific native modules or bridging code between React Native and native layers, understanding these control flow constructs accelerates development and reduces debugging time. When you encounter a Kotlin module handling device sensor data or a Swift module managing camera permissions, the control flow patterns translate directly between platforms.

Conditional Statements: If, When, and Switch

Kotlin's When Statement

Kotlin replaced the traditional switch statement with a more powerful when construct that eliminates many limitations found in languages like Java. The when statement can accept any object as its argument, not just primitives or enums, and supports complex matching conditions including ranges, type checks, and custom predicates.

The when expression in Kotlin returns a value, enabling elegant functional-style code where conditional logic can be directly assigned to variables or returned from functions. This capability is particularly valuable in Android development when working with reactive frameworks like Kotlin Flow or coroutines, where you often need to transform state based on conditions.

fun handleAction(action: Action) {
 when (action) {
 is Action.Load -> loadContent()
 is Action.Refresh -> refreshContent()
 is Action.Error -> showError(action.message)
 is Action.Navigation -> navigateTo(action.destination)
 else -> handleUnknownAction(action)
 }
}

When Expression vs Statement

One of Kotlin's most powerful features is the ability to use when as an expression that returns a value. When used this way, the value of the first matching branch becomes the value of the overall expression, and an else branch becomes mandatory unless the compiler can verify exhaustiveness through sealed classes or enums.

val result = when (status) {
 Status.Success -> "Completed"
 Status.Loading -> "In progress"
 Status.Error -> "Failed"
 else -> "Unknown"
}

When used as a statement, when performs actions without requiring exhaustiveness coverage. This distinction matters when working with nullable types, enums, or sealed classes where the compiler can determine whether all cases are handled. The Kotlin official documentation recommends using expressions over statements where possible to reduce mutable variable usage and make code flow more apparent.

Advanced When Features

Kotlin's when supports several advanced features that make it more versatile than traditional switch statements. You can group multiple conditions into a single branch using commas, check whether values fall within ranges using in or !in operators, perform type checks using is or !is keywords, and even capture the subject in a local variable for use within the branch body.

Guard conditions allow including multiple conditions for branches by adding if clauses after the primary condition. This feature enables complex control flow scenarios without nesting additional if statements.

when (user) {
 is Admin -> grantFullAccess()
 is PremiumUser if user.subscriptionActive -> grantPremiumFeatures()
 is RegularUser -> grantBasicAccess()
 else -> showUpgradePrompt()
}

Swift's Switch Statement

Swift's switch statement received a significant overhaul from Objective-C, introducing powerful pattern matching capabilities. Swift's switch is an expression that can return values, supports value binding, where clauses for additional conditions, and compound cases that share the same body.

The language enforces exhaustiveness, requiring you to handle all possible values or include a default case. The @unknown default clause explicitly handles future cases that might be added to enum definitions.

func handleAction(_ action: Action) {
 switch action {
 case .load:
 loadContent()
 case .refresh:
 refreshContent()
 case .error(let message):
 showError(message)
 case .navigation(let destination):
 navigate(to: destination)
 @unknown default:
 handleUnknownAction(action)
 }
}

Pattern Matching and Value Binding

Swift's switch statement excels at pattern matching across multiple dimensions. You can match against tuples, check type patterns, evaluate conditions with where clauses, and use interval matching for ranges. The where clause enables conditional pattern matching, allowing you to add additional constraints to your cases beyond the pattern itself. Combined with value binding using let or var, this creates expressive matching logic that would require multiple nested if statements in other languages.

switch response {
 case let (statusCode, _) where statusCode >= 200 && statusCode < 300:
 handleSuccess()
 case (400...499, let message):
 handleClientError(message)
 case (_, let error):
 handleError(error)
 @unknown default:
 handleUnknownResponse()
}

Compound Cases and Fallthrough

Swift supports compound cases that match multiple patterns in a single case clause, separated by commas. Critically, Swift's switch does not fallthrough between cases by default, but you can explicitly opt into fallthrough behavior using the fallthrough keyword when needed for specific use cases.

When vs Switch: Key Differences
FeatureKotlin WhenSwift Switch
Argument TypeAny object supportedPattern matching based
ExhaustivenessRequires else branchRequires default case
Range MatchingUses 'in' keywordInterval syntax
Type CheckingSmart casts with 'is'Value binding patterns
FallthroughNot supportedExplicit with fallthrough keyword

Iteration Statements: Loops in Kotlin and Swift

Kotlin Iteration Patterns

Kotlin's for loop works exclusively with iterables, eliminating the traditional C-style three-part for loop. This design encourages cleaner, more expressive iteration patterns that are easier to reason about and less prone to off-by-one errors.

// Iterating through a range
for (i in 1..10) {
 processItem(i)
}

// Iterating with index
for ((index, item) in items.withIndex()) {
 handleItem(index, item)
}

// Functional operations with coroutines
items.filter { it.isValid }
 .map { transform(it) }
 .forEach { process(it) }

Kotlin ranges use intuitive operators like .. for closed ranges and ..< for open-ended ranges. You can iterate in reverse with downTo and control step size with the step keyword. For mobile developers working with data processing pipelines or UI state transformations, these functional operations integrate seamlessly with Kotlin coroutines for asynchronous processing.

The Kotlin standard library provides extensive functional operations including forEach, map, filter, flatMap, reduce, and groupBy. These work seamlessly with coroutines for asynchronous data processing, making them essential tools for mobile developers handling network responses, user data, and UI state transformations.

Swift Iteration Patterns

Swift offers both traditional for-in loops similar to Kotlin's approach and C-style for loops for cases requiring more control. The for-in loop supports pattern matching, conditional filtering with where clauses, and zip operations for iterating over multiple collections simultaneously.

// Basic iteration
for item in items {
 process(item)
}

// Iterating with where clause
for item in items where item.isValid {
 process(item)
}

// Enumerated iteration
for (index, item) in items.enumerated() {
 handle(index, item)
}

Swift uses the closed range operator ... and half-open range operator ..<, providing similar iteration capabilities with slightly different syntax conventions. The where clause enables conditional filtering directly in the loop declaration, keeping iteration logic concise and readable.

Both languages support while and do-while loops (called repeat-while in Swift) for cases where the number of iterations depends on a condition evaluated during execution. In mobile development, these loops commonly power game loops, polling mechanisms, and retry logic.

For developers transitioning from Java to Kotlin, understanding how Kotlin differs from Java in iteration patterns helps accelerate the learning curve.

Jump Expressions: Break, Return, and Control Transfer

Labeled Breaks for Nested Loops

Both Kotlin and Swift support labeled break statements that allow breaking out of nested loops. This is crucial when processing multidimensional data structures or implementing search algorithms in mobile applications.

outerLoop@ for (row in rows) {
 innerLoop@ for (cell in row) {
 if (cell.value == target) {
 break@outerLoop
 }
 }
}
outerLoop: for row in rows {
 innerLoop: for cell in row {
 if cell.value == target {
 break outerLoop
 }
 }
}

Without labels, the break statement only exits the innermost loop, which can lead to inefficient processing when you need to exit early from nested iterations. Labeled breaks provide precise control over which loop to exit, improving both code clarity and runtime performance.

Continue Statements

The continue statement skips to the next iteration in both languages. Labeled continue similarly allows skipping to an outer loop's next iteration when working with nested structures. This pattern is useful for filtering during iteration without building intermediate collections.

Return Variations

Both languages support local and non-local returns from functions. Kotlin's return@label and Swift's labeled return syntax enable precise control over function execution flow. These constructs are particularly valuable when working with collection processing functions, where you might need to return early based on a condition discovered during iteration.

If Expressions and Conditional Logic

Kotlin If as Expression

In Kotlin, if functions as both a statement and an expression, eliminating the need for a ternary operator. When used as an expression, the last expression in each branch becomes the returned value.

val statusText = if (isLoading) {
 "Loading..."
} else if (hasError) {
 "Error occurred"
} else {
 "Ready"
}

Swift If-Let and Guard Statements

Swift introduces if let syntax for optional binding, combining unwrapping and condition checking. The guard statement provides early exit semantics, particularly useful for validating preconditions at function entry.

func processItem(_ item: Item?) {
 guard let validItem = item, validItem.isReady else {
 return
 }
 // validItem is available throughout the rest of the function
 updateUI(with: validItem)
}

Best Practices for Cross-Platform Control Flow

Choosing the Right Construct

When migrating logic between platforms or bridging native modules, recognize that Kotlin's when provides more expression-oriented flexibility, while Swift's switch excels at pattern matching but requires explicit handling of all cases. Prefer using expressions over statements where the result of the conditional logic will be used.

Maintaining Readability

Prioritize clarity over cleverness when writing control flow code. Use meaningful variable and label names that describe what your control flow achieves. Document complex conditions with comments explaining the business logic they implement.

Exhaustiveness and Safety

Both languages provide exhaustiveness checking. Kotlin's when requires an else branch when not all cases are covered, while Swift's switch can use @unknown default for truly exhaustive enums. Embrace these compiler warnings--they represent valuable safety nets that prevent runtime crashes in production mobile applications.

Common Pitfalls and How to Avoid Them

Missing Default Cases

One common error when transitioning between languages is forgetting the required default or else case. Swift's switch always requires a default case when not all enum cases are handled. Always explicitly include default handling even when you believe all cases are covered--this future-proofs your code against enum evolution.

Break Statement Scope Confusion

Developers accustomed to C-style switch fall-through may initially find Kotlin's lack of implicit fallthrough surprising. Each case must explicitly continue to the next or use -> to execute and exit. Swift similarly requires explicit fallthrough using the fallthrough keyword when needed.

Control Flow in Mobile State Management

Mobile applications require sophisticated state management, and control flow statements are the building blocks of state machines that drive your UI. Whether implementing Redux-style state updates in Kotlin with when or building SwiftUI state logic with switch statements, the patterns are fundamentally similar. A well-designed state management system uses exhaustive control flow to ensure every possible state transition is handled explicitly, preventing silent failures that lead to confusing UI bugs.

When combining control flow with error handling, Kotlin's try expression and Swift's do-catch syntax integrate seamlessly. Use when or switch statements to categorize errors and determine appropriate recovery strategies--whether showing a user-friendly message, retrying an operation, or gracefully degrading functionality.

For teams building cross-platform mobile applications, understanding these control flow fundamentals helps bridge the gap between native Kotlin/Swift code and framework-based implementations.

Frequently Asked Questions

Ready to Build Cross-Platform Mobile Apps?

Master Kotlin and Swift to build native-quality applications for both Android and iOS.

Sources

  1. LogRocket: Comparing control flow statements in Kotlin and Swift - Comprehensive comparison of when vs switch, exhaustiveness, and fallthrough behavior
  2. Kotlin Documentation: Conditions and loops - Official Kotlin control flow reference including when expressions and guard conditions
  3. Swift Documentation: Control Flow - Official Swift language guide covering switch, if, guard, and loops