跪拜 Guibai
← Back to the summary

Xcode 26 Breaks AFNetworking Builds: Five Fixes, from Quick Patch to Full Migration

Problem Background

After upgrading to Xcode 26 (including 26.6), many legacy projects still using AFNetworking encounter a compilation error:

error: 'netinet6/in6.h' file not found

Or, in Explicit Modules mode:

error: importing private header <netinet6/in6.h> is not allowed

Root Cause: Xcode 26 enables a stricter Explicit Modules mechanism, prohibiting source code from directly #import-ing Darwin private headers. The IPv6-related type definitions in <netinet6/in6.h> have long been provided indirectly through the public header <netinet/in.h>. This import in AFNetworking is a legacy, redundant inclusion.

The official AFNetworking repository was archived in 2023 and no longer accepts Issues or PRs, so this problem can only be resolved by developers themselves.

This article provides 5 solutions ranging from shallow to deep, covering scenarios from personal projects to team collaboration and CI/CD. Choose based on your actual situation.


Solution 1: Podfile post_install Script Auto-Fix (Recommended First Choice)

Applicable Scenarios

Principle

CocoaPods provides a post_install hook that can batch-modify source files in the Pods directory after dependency installation. Use a Ruby script to locate and delete the problematic line of code, achieving a "zero-intrusion" fix.

Complete Code

Add the following at the end of your Podfile:

post_install do |installer|
  # Iterate over all Pod targets
  installer.pods_project.targets.each do |target|
    # Only process AFNetworking (add || conditions for other affected libraries)
    next unless target.name == 'AFNetworking'

    target.source_build_phase.files.each do |file|
      file_path = file.file_ref.real_path.to_s
      next unless file_path.end_with?('.m', '.h')

      content = File.read(file_path)
      if content.include?('#import <netinet6/in6.h>')
        new_content = content.gsub(/#import\s+<netinet6/in6.h>\s*\n/, '')
        File.write(file_path, new_content)
        puts "[Fix] Removed netinet6/in6.h from #{File.basename(file_path)}"
      end
    end
  end
end

Execution

pod install
# Or if Pods already exist
pod install --repo-update

Notes


Solution 2: Fork Private Repository + Tag Reference

Applicable Scenarios

Complete Operation Process

Step 1: Fork and Clone

# Fork AFNetworking/AFNetworking on GitHub/GitLab
git clone https://github.com/your-org-or-username/AFNetworking.git
cd AFNetworking

Step 2: Create a Fix Branch and Modify

git checkout -b fix/xcode26-netinet6

Find AFNetworking/Reachability/AFNetworkReachabilityManager.m and delete:

// ❌ Delete this line
#import <netinet6/in6.h>

Verify that <netinet/in.h> already exists (usually imported at the top of the file), as it contains all necessary IPv6 type definitions.

Step 3 (Strongly Recommended): Add a Privacy Manifest

Since Spring 2024, Apple has required apps submitted to the App Store to declare a privacy manifest. AFNetworking 4.x does not include this file by default. Create PrivacyInfo.xcprivacy in the project root directory:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSPrivacyTracking</key>
    <false/>
    <key>NSPrivacyTrackingDomains</key>
    <array/>
    <key>NSPrivacyCollectedDataTypes</key>
    <array/>
    <key>NSPrivacyAccessedAPITypes</key>
    <array/>
</dict>
</plist>

Confirm that AFNetworking.podspec contains a resource file wildcard (4.x includes *.xcprivacy by default; generally no changes are needed).

Step 4: Commit, Tag, and Push

git add .
git commit -m "fix: remove deprecated netinet6/in6.h & add privacy manifest"
git push origin fix/xcode26-netinet6

# Tag based on the current HEAD with a semantic version
git tag 4.0.1-xcode26-fix
git push origin 4.0.1-xcode26-fix

Step 5: Reference in Your Project

# Podfile
pod 'AFNetworking', :git => 'https://github.com/your-org-or-username/AFNetworking.git', :tag => '4.0.1-xcode26-fix'
pod cache clean AFNetworking --all   # Clear old cache
pod install

Team Collaboration Suggestions


Solution 3: Local Patch File + CocoaPods Patch Plugin

Applicable Scenarios

Prerequisites

gem install cocoapods-patch

Operation Steps

Step 1: Generate the Patch File

After manually modifying the source file in Pods once, execute the following in the project root directory:

pod patch create AFNetworking

After interactive confirmation, a patches/AFNetworking+4.0.1.patch file will be generated in your project.

Step 2: Declare in Podfile

plugin 'cocoapods-patch'

pod 'AFNetworking', '~> 4.0'

Afterwards, the plugin will automatically apply the patch every time pod install is run.

Advantages


Solution 4: Bypass at the Xcode Build Settings Level (Temporary Emergency Fix)

Applicable Scenarios

Operation Method

In Xcode, select Pods → AFNetworking Target → Build Settings, search for Explicit Modules, and set it to NO.

Or, disable it for a specific Pod in the Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.name == 'AFNetworking'
      target.build_configurations.each do |config|
        config.build_settings['CLANG_ENABLE_EXPLICIT_MODULES'] = 'NO'
      end
    end
  end
end

⚠️ Risk Warning


Solution 5: Migrate to a Modern Networking Framework (Fundamental Solution)

Why You Should Migrate

Dimension AFNetworking Modern Alternatives
Maintenance Status Archived in 2023, no longer updated Actively maintained
Language Objective-C Swift / ObjC compatible
Privacy Compliance No built-in Privacy Manifest Native support
Async/Await Not supported URLSession / Alamofire native support
Explicit Modules Multiple private header references Fully compliant
Security Audit Previously injected by malicious software Active community, fast security response

Three Migration Paths

Path A: Native NSURLSession Wrapper (Lightest Weight)

Suitable for projects with a thin network layer and simple request types. Directly wrap a lightweight NetworkClient using Apple's native API with zero external dependencies:

final class NetworkClient {
    static let shared = NetworkClient()
    private let session: URLSession

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

    func request(_ url: URL) async throws -> Data {
        let (data, response) = try await session.data(from: url)
        guard let http = response as? HTTPURLResponse,
              200...299 ~= http.statusCode else {
            throw URLError(.badServerResponse)
        }
        return data
    }
}

Path B: Alamofire (Preferred for Swift Projects)

The "spiritual successor" to AFNetworking, with a consistent API design style, offering the lowest migration cost for Swift projects:

# SPM or Podfile
pod 'Alamofire', '~> 5.9'
import Alamofire

AF.request("https://api.example.com/data")
  .validate()
  .responseDecodable(of: MyModel.self) { response in
      switch response.result {
      case .success(let model): print(model)
      case .failure(let error): print(error)
      }
  }

Path C: Moya + Alamofire (Recommended for Large Projects)

Provides an API endpoint abstraction layer on top of Alamofire, suitable for projects with many interfaces requiring modular management:

enum UserAPI {
    case profile(id: Int)
    case update(name: String)
}

extension UserAPI: TargetType {
    var baseURL: URL { URL(string: "https://api.example.com")! }
    var path: String { ... }
    var method: Moya.Method { ... }
    var task: Task { ... }
    var headers: [String: String]? { ... }
}

Incremental Migration Strategy

You don't need to replace all requests at once. The recommended approach:

  1. Use the new framework for all new interfaces.
  2. Gradually replace old AFNetworking calls module by module.
  3. Keep AFNetworking as a fallback until coverage reaches the target.
  4. Finally, remove the AFNetworking dependency.

Solution Selection Decision Tree

Compilation error netinet6/in6.h
        │
        ├── Urgent build, need a fix in 10 minutes? ──→ Solution 4 (Temporarily disable Explicit Modules)
        │
        ├── Team project, want automation without modifying source? ──→ Solution 1 (post_install script) ✅ Recommended
        │
        ├── Need customization + Privacy Manifest + long-term maintenance? ──→ Solution 2 (Fork + Tag)
        │
        ├── Fix is minimal, want to manage with a patch? ──→ Solution 3 (cocoapods-patch)
        │
        └── Project has a long lifecycle ahead? ──→ Solution 5 (Migration) 🎯 Ultimate Solution

Common Pitfalls FAQ

Q1: Will deleting netinet6/in6.h affect IPv6 functionality? No. <netinet/in.h> already includes all necessary definitions like sockaddr_in6 and IN6ADDR_ANY_INIT. AFNetworking itself does not directly use any symbols unique to netinet6/in6.h.

Q2: Besides AFNetworking, which other libraries have the same issue? According to community feedback, ZFPlayer, YYText, and some older versions of WCDB also have similar private header references. The post_install script in Solution 1 can be extended into a general fix by adding the corresponding Pod names to the next unless condition.

Q3: Does the post_install script work normally on M1/M2 Macs? Yes. The script operates on plain text files and is architecture-independent. However, note that CocoaPods 1.15+ has slightly adjusted the installer.pods_project API; it is recommended to use CocoaPods ≥ 1.15.2.

Q4: What if the upstream updates after forking? AFNetworking is archived and will not receive upstream updates. If you fork other libraries that are still maintained, it is recommended to periodically rebase the upstream main branch and submit your fix back upstream as a PR.

Q5: What happens if I don't add the Privacy Manifest? Since May 2024, App Store Connect issues warnings for third-party SDKs missing a privacy manifest. Starting in 2025, missing key API declarations may lead to app review rejection. It is recommended to complete this alongside fixing the compilation issue.


Summary

Xcode 26's strict validation of private headers is essentially pushing the ecosystem toward a safer, more modular direction. For archived historical libraries like AFNetworking, use scripts or forks to extend their life in the short term, but plan for migration in the long term. Technical debt doesn't disappear; it just erupts more violently with the next Xcode upgrade.

Choose the solution that best fits your current stage and start taking action now.

Comments

Top 1 from juejin.cn, machine-translated. The original thread is authoritative.

向新出发叭

Very thorough compilation, the scenario breakdown for the five solutions is clear. We've also used post_install to batch-process other legacy issues in Pods before; it's idempotent and works in CI, making it the top choice for team scenarios. Two additions: first, when forking for solution two, it's crucial to put the repo under the team organization and note the corresponding upstream commit in the README—this saves a lot of backtracking effort during future upgrades, and it's great that the article mentioned this. Second, we used solution four once in an emergency; the compilation time did increase and we lost module validation, so we switched back to solution one right after shipping the build. Solution five's migration is a permanent fix, but the regression testing cost for legacy projects is significant. Our approach is to first fix the build with solution one or three, then schedule the migration as technical debt to be digested gradually. Thanks for sharing, bookmarked!