Throw and Throws are mechanisms for throwing and declaring exceptions in programming languages. The throw operator interrupts the normal execution of a function and passes the error object up the call stack. The throws keyword in a function signature warns the calling party about the possibility of an error, making the code predictable. According to Kotlin Documentation (2026), throw in Kotlin is an expression, not a statement, which allows using it inside when-blocks and the Elvis operator.
Key Takeaways
Throw is an operator that generates an exception at the point of program execution. When throw is encountered, the current execution flow is immediately interrupted and control is transferred to the nearest catch handler in the call stack. If no handler is found, the application crashes. Throw in mobile development is used to signal errors that cannot be handled at the current level of abstraction — for example, an invalid server response, network absence, or incorrect arguments.
Throws — a modifier in a function signature (primarily in Swift) that declares that the function may throw an error. This is part of the checked errors mechanism in Swift: the calling side is required to handle the error via do-catch, try?, try!, or mark its own function as throws for further propagation. In Kotlin and Java throws also exists, but Kotlin considers it redundant — all exceptions in Kotlin are unchecked, meaning they may not be handled without syntactic enforcement. According to Apple Swift Documentation (2026), throws in Swift is the only way to explicitly declare the possibility of an error in a function type, making API contracts transparent for the developer.
The difference between throw and throws is fundamental: throw is an action (throwing an exception at runtime), throws is a declaration (compile-time contract). A function without throws cannot use throw — the Swift compiler will produce an error. A function with throws may not use throw — it is allowed but meaningless. This separation makes throw/throws a powerful API design tool, where the error contract is visible in the function signature before its call.
In Swift, the throw operator accepts any type that implements the Error protocol. Most commonly this is an enum with cases for different kinds of errors. Swift does not support checked exceptions in Java style — instead, the function type is marked with throws, and handling is delegated to the calling side. This makes throw in Swift more flexible but also more responsibility for the developer.
Any type that conforms to the Error protocol can be thrown via throw. Most often developers use an enum with cases without associated values (for simple errors) or with associated values (to pass context). Swift does not require the error to be an enum — you can use a struct or class implementing Error, but enum is preferable thanks to exhaustive switch on the handler side. The compiler checks that all cases are handled in do-catch.
enum AuthError: Error {
case invalidCredentials
case tokenExpired
case accountLocked(remainingMinutes: Int)
}
func login(username: String, password: String) throws -> Session {
guard isValid(username) else {
throw AuthError.invalidCredentials
}
let response = try api.authenticate(username, password)
if response.isLocked {
throw AuthError.accountLocked(
remainingMinutes: response.lockDuration
)
}
return Session(token: response.token)
}
AuthError defines three scenarios: invalid credentials, expired token, and locked account with an associated value of remainingMinutes. The login function is declared as throws — the compiler requires calling it via try. Inside the function, throw is used in two places: for an invalid username and for a locked account. The associated value accountLocked allows passing concrete data to the user — how many minutes to wait before unlocking. This approach eliminates the need for separate API endpoints to check lock status.
In Kotlin throw is an expression of type Nothing, not a statement. This means throw can be used on the right side of an assignment, inside when-expressions, and the Elvis operator ?:. The Nothing type is a special subtype of all types in Kotlin, which allows using throw in places where a value of any type is required. The compiler understands that execution does not continue after throw and does not require a branch for this case.
Nothing is a unique type in Kotlin that is a subtype of all possible types. A function returning Nothing (e.g., TODO()) never completes normally — it either always throws an exception or enters an infinite loop. This makes throw a natural candidate for use in places where a value is required: Elvis operator, when without else, variable initialization. If throw stands in a when branch, the compiler understands that the branch leads to Nothing and does not require a return or else for that branch.
data class Config(val apiUrl: String, val timeoutSec: Int)
class ConfigParser {
fun parse(json: String): Config {
val obj = JSONObject(json)
val url = obj.optString("apiUrl")
?: throw IllegalArgumentException("apiUrl is required")
val timeout = obj.optInt("timeoutSec", 30)
return Config(url, timeout)
}
fun getErrorMessage(code: Int): String {
return when (code) {
404 -> "Not found"
500 -> "Server error"
else -> throw IllegalArgumentException("Unknown code: $code")
}
}
}
In the first example, throw is used in the Elvis operator ?:: if the apiUrl field is missing from the JSON, the throw expression immediately interrupts execution and throws an IllegalArgumentException. The Nothing type allows the compiler to infer the type of the right side as String (Elvis expects String, throw has type Nothing, Nothing is a subtype of String). In the second example, throw is used inside a when-expression: if the code does not match any known one, an exception is thrown. The compiler understands that code after throw is unreachable, so the return type of the function String is not violated.
In Swift throws is specified after the parameter list and before the return type arrow. A function with throws can only call other throws-functions inside do-catch or with try?. If a throws-function does not handle the error, it passes it to the calling side. Swift also supports rethrows — a modifier for higher-order functions that accept a throws closure and propagate its error. rethrows means that the function only throws an error if the passed closure threw one — the function itself does not generate an error.
func mapValues<T>(
_ array: [T],
transform: (T) throws -> U
) rethrows -> [U] {
var result = [U]()
for element in array {
result.append(try transform(element))
}
return result
}
// Using with throws closure
let parsed = try mapValues(jsonStrings) { str in
let data = Data(str.utf8)
return try JSONDecoder().decode(Item.self, from: data)
}
rethrows allows the mapValues function to be flexible: it accepts both throws-closures and regular ones. If a throws-closure is passed, the mapValues call requires try; if a regular one, try is not needed. This makes rethrows ideal for higher-order functions such as map, filter, reduce in the Swift standard library. Apple's recommendation: use rethrows for APIs that accept throws-closures and the only source of error is that closure. If the function can throw its own error, use throws.
Swift throws is closer to checked exceptions (like in Java) — thrown errors are declared in the signature. Kotlin and Dart use unchecked exceptions — throws in the signature is not required. The difference is fundamental: checked forces the developer to handle the error (safer but more verbose), unchecked gives freedom but increases the risk of forgetting to handle an error. Swift chose checked for throws, Kotlin chose unchecked for all exceptions. Both approaches have advantages: Swift is more reliable at the language level, Kotlin is more compact and convenient in functional transformation chains.
For structured error handling in mobile applications, it is recommended to create custom error types instead of using base Exception or Error. In Swift, an enum with the Error protocol is used for this; in Kotlin, a sealed class inheriting from Throwable (or from Exception); in Dart, a class inheriting from Exception. Custom types allow grouping errors by categories and passing associated data.
| Language | Error type | Feature |
|---|---|---|
| Swift | enum: Error { ... } | Associated values, exhaustive switch in catch |
| Kotlin | sealed class : Throwable() | Data class for errors with fields, when-expression |
| Dart | class implements Exception | Message field, on-clause in catch |
| Java | class extends Exception | Checked vs unchecked, mandatory throws in signature |
When designing custom errors, follow the rule: one error — one scenario. Do not combine different causes in one type with a String message flag — create separate cases/subclasses for each scenario. This will allow the calling side to handle each case via pattern matching (when/switch) rather than string comparison. In Swift this gives exhaustive checking — the compiler will warn if any enum case of NetworkError is not handled.
sealed class NetworkError(val message: String) : Throwable(message) {
data class Timeout(val durationMs: Long) :
NetworkError("Request timed out after ${durationMs}ms")
data class HttpError(val code: Int, val body: String?) :
NetworkError("HTTP $code")
data class NoConnection(val cause: IOException) :
NetworkError("No internet connection")
}
Sealed class NetworkError inherits from Throwable (the standard exception type in Kotlin). Each subclass is a data class with its own fields: Timeout contains the timeout duration in milliseconds, HttpError contains the code and response body, NoConnection contains the original IOException. This design allows handling each error via when with exhaustive matching (when you add a new subclass, the compiler will force updating all when-expressions).
Swift offers three ways to call throws-functions, each with its own safety contract. try is the standard way: requires do-catch or being inside a throws-function. try? converts the error to nil — the result becomes optional, on error it returns nil, the type changes from T to T?. try! forces execution without handling: if an error is thrown, the application crashes. Use try! only when absolutely certain that an error is impossible (for example, obviously valid data).
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!
// try? — optional result
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())
// try! — guaranteed success (only when certain)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
Config.self,
from: Config.defaultJSON
)
// try — standard handling
do {
let user = try fetchUser()
showUser(user)
} catch let error as NetworkError {
showRetryAlert(error.message)
}
In the example, try! is used for obviously valid JSON embedded in the app bundle — decoding error is impossible with a correct release. try? is used for reading a configuration file — if the file is missing or corrupted, the app uses default values instead of crashing. try in do-catch is used for network requests where an error is expected and requires user response. Recommendation: avoid try! in production code — use it only for constant data verified at build time.
Frequently Asked Questions
Throw is an operator that throws an error during program execution, interrupting the flow. Throws is a function signature modifier declaring that the function may throw an error. A function without throws cannot use throw. Throws is a compile-time contract, throw is a runtime action.
Kotlin follows the philosophy of unchecked exceptions: all exceptions may remain unhandled without syntactic enforcement. Kotlin developers believe that throws in Java leads to excessive try-catch blocks and ignoring checked exceptions via empty catch. Kotlin's Nothing type allows using throw as an expression, replacing throws with a more flexible approach.
try! is only acceptable when you are absolutely certain that an error is impossible: obviously valid JSON from bundle, constant data, correct URL schemes. In production code, try! is an exception, not a rule. try? is preferable for optional scenarios with a fallback value, try with do-catch for mandatory error handling.
Rethrows is a modifier for functions that accept throws-closures. A function with rethrows only throws an error if the passed closure threw one. This allows higher-order functions (map, filter) to work with both throws and non-throws closures without forcing try on the calling side.
Yes, inside catch you can use throw to propagate the error up the stack, wrapping it in a different type or adding context. This is called error chaining or rethrow. In Swift, simply another throw inside catch is enough; in Kotlin, throw inside a catch block. The finally block executes before control is passed further.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also