SwiftJuniorMCQ

What is the difference between [Obj], [Obj?], [Obj]?, and [Obj?]? in Swift?

Test your knowledge:

Explanation & Code

These four declarations differ in two dimensions: whether the array itself is optional, and whether the elements inside are optional.

Jump to: Plain Array · Array of Optionals · Optional Array · Optional Array of Optionals · Quick Decision Guide


The 4 Variations:

var items: [Obj]    // Array NOT optional, elements NOT nil
var items: [Obj?]   // Array NOT optional, elements CAN be nil
var items: [Obj]?   // Array IS optional, elements NOT nil
var items: [Obj?]?  // Array IS optional, elements CAN be nil
DeclarationArray can be nil?Elements can be nil?
[Obj]NoNo
[Obj?]NoYes
[Obj]?YesNo
[Obj?]?YesYes

1. [Obj] — Plain Array

The safest and most common. Array always exists, all elements are real objects.

var items: [String] = []

items = ["Apple", "Banana"]   // OK
items = nil                   // ERROR - cannot be nil
items.append(nil)             // ERROR - elements cannot be nil

// No unwrapping needed
for item in items {
    print(item.uppercased())  // always safe
}

2. [Obj?] — Array of Optionals

Array always exists, but individual elements might be nil (e.g., gaps in data).

var items: [String?] = []

items = ["Apple", nil, "Cherry"]  // nil elements OK
items = nil                        // ERROR - array itself cannot be nil

// Must unwrap each element
for item in items {
    if let item = item {
        print(item.uppercased())
    } else {
        print("empty slot")
    }
}

Real-world use case: A list of optional user inputs, or API data where some fields may be missing.


3. [Obj]? — Optional Array

The array itself might not exist yet, but if it does, all elements are real.

var items: [String]? = nil

items = ["Apple", "Banana"]  // OK
items = nil                   // OK

// Must unwrap the array first
if let items = items {
    for item in items {
        print(item.uppercased())  // always a String here
    }
}

// Or use optional chaining
let count = items?.count ?? 0

Real-world use case: A list that hasn't been loaded yet (e.g., waiting for an API response).


4. [Obj?]? — Optional Array of Optionals

The most flexible (and most complex). Both the array and its elements can be nil.

var items: [String?]? = nil

items = ["Apple", nil, "Cherry"]  // OK
items = nil                        // OK

// Must unwrap array AND each element
if let items = items {
    for item in items {
        print(item ?? "empty")
    }
}

Real-world use case: An optional dataset where rows themselves can have missing values.


Quick Decision Guide

Do you need the array to possibly not exist?
  |
  +-- YES → [Obj]?  or  [Obj?]?
  +-- NO  → [Obj]   or  [Obj?]
              |
              Do you need elements to possibly be nil?
                |
                +-- YES → [Obj?]  or  [Obj?]?
                +-- NO  → [Obj]   or  [Obj]?

Rule of thumb: Start with [Obj] and only add ? when you have a clear reason to.

Rate your understanding:

Ready to practice more Swift?

Test yourself with our interactive quiz mode or browse all curated questions for this topic.