Skip to main content

Swift File-private Access

In Swift, controlling access to your code is an essential part of creating well-structured applications. Among Swift's access control levels, file-private offers a unique scoping option that helps you organize your code more effectively.

What is file-private Access?

file-private access restricts the use of a class, structure, function, property, or other code element to within the source file where it is defined. This means that any entity marked as file-private can be accessed by any code within the same file, but not from code in other files.

This level of access provides encapsulation at the file level, allowing you to hide implementation details from other parts of your codebase while still enabling different entities within the same file to work together.

Syntax

To declare a code element with file-private access, simply use the fileprivate access modifier:

swift
fileprivate class SomeClass {
// Class implementation
}

fileprivate var someVariable = 10

fileprivate func someFunction() {
// Function implementation
}

When to Use file-private Access

You should consider using file-private access when:

  1. You have helper code that should only be used within a specific file
  2. You want to share functionality between multiple types in the same file but hide it from the rest of your codebase
  3. You need to implement utility functions or properties that support a public API but shouldn't be exposed directly

file-private vs. private Access

It's important to understand the difference between file-private and private:

  • private: Restricts access to the enclosing declaration (class, struct, etc.)
  • fileprivate: Restricts access to the current file

Let's see this difference in action:

swift
// In FileA.swift
class Container {
private var privateValue = 10
fileprivate var filePrivateValue = 20
}

// Still in FileA.swift
class Accessor {
func accessValues() {
let container = Container()
// Error: 'privateValue' is inaccessible due to 'private' protection level
// print(container.privateValue)

// Works fine, because we're in the same file
print(container.filePrivateValue)
}
}

// In FileB.swift
class ExternalAccessor {
func tryAccess() {
let container = Container()
// Error: 'filePrivateValue' is inaccessible due to 'fileprivate' protection level
// print(container.filePrivateValue)
}
}

Practical Examples

Example 1: Helper Classes Within a File

swift
// NetworkManager.swift

// Public interface that other files can use
public class NetworkManager {
public func fetchData(from url: URL, completion: @escaping (Data?) -> Void) {
let session = URLSessionProvider().session
let task = session.dataTask(with: url) { data, response, error in
completion(data)
}
task.resume()
}
}

// Helper class that's only needed within this file
fileprivate class URLSessionProvider {
let session: URLSession

init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 30
self.session = URLSession(configuration: configuration)
}
}

In this example, the URLSessionProvider class is an implementation detail that shouldn't be exposed to other files. By marking it as fileprivate, we ensure it's only accessible within the NetworkManager.swift file.

Example 2: Extensions in the Same File

swift
// UserModel.swift

public struct User {
public let id: String
public let name: String
public let email: String
fileprivate let passwordHash: String

public init(id: String, name: String, email: String, passwordHash: String) {
self.id = id
self.name = name
self.email = email
self.passwordHash = passwordHash
}
}

// Extension in the same file can access fileprivate members
extension User {
public func authenticate(with password: String) -> Bool {
// Can access passwordHash because we're in the same file
return hashPassword(password) == self.passwordHash
}

fileprivate func hashPassword(_ password: String) -> String {
// Simple hash for demonstration (use better hashing in real apps)
return password.reversed().joined()
}
}

Here, the passwordHash property and hashPassword function are implementation details that shouldn't be accessible from outside the file, but they need to be shared between the User struct and its extension.

Example 3: File-level Constants and Utilities

swift
// ThemeManager.swift

// Constants used throughout this file but nowhere else
fileprivate let defaultAnimationDuration = 0.3
fileprivate let defaultCornerRadius = 8.0

public class ThemeManager {
public static let shared = ThemeManager()

private init() {}

public func applyRoundedCorners(to view: UIView) {
view.layer.cornerRadius = defaultCornerRadius
view.clipsToBounds = true
}

public func animateColorChange(view: UIView, to color: UIColor) {
UIView.animate(withDuration: defaultAnimationDuration) {
view.backgroundColor = color
}
}
}

// Another class in the same file can access the fileprivate constants
fileprivate class ThemeAnimator {
func performCustomAnimation(on view: UIView) {
UIView.animate(withDuration: defaultAnimationDuration * 2) {
// Animation code here
}
}
}

In this example, the constants and the ThemeAnimator class are only relevant within the theme implementation file and shouldn't be accessible elsewhere.

Best Practices

  1. Start restrictive: Begin with the most restrictive access level (private) and only broaden the access as needed
  2. Use fileprivate sparingly: If you find yourself using many fileprivate declarations in a single file, consider if your file is doing too much and could be split into multiple files
  3. Document the purpose: Add comments to explain why a particular element is marked as fileprivate to help other developers understand your design decisions
  4. Consider alternatives: Before using fileprivate, consider if a nested type with private access might be a cleaner solution

Summary

The file-private access level in Swift provides a useful middle ground between private and internal access, allowing you to share implementation details within a single file while hiding them from the rest of your codebase. This helps you create cleaner, more modular code with appropriate encapsulation.

By using file-private access strategically, you can:

  • Hide implementation details at the file level
  • Share functionality between types in the same file
  • Create supporting code that's not exposed to the rest of your application

Exercises

  1. Take an existing class in your project and identify properties or methods that could benefit from being marked as fileprivate instead of private or internal.

  2. Create a utility file with both public APIs and fileprivate helper functions that support those APIs.

  3. Refactor a large file to use fileprivate access for components that need to share data with each other but shouldn't be accessible outside the file.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)