Skip to content

Strategy ​

Strategy puts interchangeable algorithms behind one interface. The feature chooses an algorithm at runtime without scattering the choice through its rendering code.

When it helps in iOS ​

A news feed may sort by newest or by reading priority. The feed screen should be able to switch when the user changes a preference, while keeping the rest of its loading and display logic stable.

Example: two feed ordering policies ​

swift
import Foundation

struct FeedStory {
    let title: String
    let publishedAt: Date
    let priority: Int
}

protocol FeedOrdering {
    func order(_ stories: [FeedStory]) -> [FeedStory]
}

struct NewestFirst: FeedOrdering {
    func order(_ stories: [FeedStory]) -> [FeedStory] {
        stories.sorted { $0.publishedAt > $1.publishedAt }
    }
}

struct PriorityFirst: FeedOrdering {
    func order(_ stories: [FeedStory]) -> [FeedStory] {
        stories.sorted {
            if $0.priority == $1.priority {
                return $0.publishedAt > $1.publishedAt
            }
            return $0.priority > $1.priority
        }
    }
}

struct FeedPresenter {
    var ordering: any FeedOrdering

    func visibleStories(from stories: [FeedStory]) -> [FeedStory] {
        ordering.order(stories)
    }
}

var presenter = FeedPresenter(ordering: NewestFirst())
presenter.ordering = PriorityFirst() // User changed the sort option.

Both strategies accept and return the same model. The presenter can switch behavior without knowing how either ordering works.

How to use it in a real app ​

Map the user's sort selection to a strategy in the view model, then recompute the visible list when the selection changes. For a paginated feed, sorting only one loaded page is misleading; have the server apply the same ordering across all pages or fetch the full set before sorting.

Watch for: If the alternatives are one-line closures, a closure may be enough. Named strategies help when algorithms need parameters, independent tests, or reuse across screens.

Trace the switch ​

The presenter holds one FeedOrdering strategy. A preference change swaps the strategy, and the next visibleStories call uses the new algorithm. No view rendering code needs to branch on sort mode. A stable tie-breaker makes lists less likely to jump unexpectedly.

If sorting happens on the server, the strategy should build request parameters rather than reorder a single page locally. Test both algorithms with equal priorities and equal dates. Keep algorithm selection in one view model or composition root so two screens do not silently disagree about what “top” means.

Separate policy from presentation ​

FeedOrdering defines one operation over stories. NewestFirst compares publication dates, while PriorityFirst compares priority and uses date as a tie-breaker. FeedPresenter stores the chosen strategy through the protocol, so changing the user preference replaces an algorithm rather than adding sorting branches throughout the view. Both algorithms return the same model type, letting the UI render stories the same way.

From example to an iOS feed ​

A sort picker maps its selection to a strategy, and the view model recomputes visible stories when that selection or the loaded data changes. Persist the selected sort mode if users expect it to survive relaunch. For server-paginated feeds, local sorting of only the current page is incorrect; have the strategy generate API sort parameters and let the server order the full result set. Make ties deterministic with a stable secondary key such as story ID.

Failure modes and verification ​

Test old and new stories with equal priorities, empty input, and whether changing strategy leaves source data untouched. If a strategy needs network access or mutable state, document its lifetime and thread isolation. Named strategy types help when policy grows or is shared; a single closure or enum switch is simpler for two trivial one-line cases. Unlike State, Strategy selection does not imply a lifecycle transition.

Next: Observer →

Released under the MIT License.