Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7550c0b
Switch to action v4
kean Mar 15, 2026
a79025b
Adopt consuming attribute
kean Mar 15, 2026
0a45d71
Update README
kean Mar 15, 2026
e4ca6ca
Add timeout for targets
kean Mar 15, 2026
26614cb
Temporarily disable some tests
kean Mar 15, 2026
4cb7142
Add timeout for tests
kean Mar 15, 2026
503070f
Update formatting
kean Mar 15, 2026
e1a1c61
Update CHANGELOG
kean Mar 15, 2026
912bd29
Fix race in failedPartialImagesAreIgnored
kean Mar 15, 2026
50b097a
Redcuce timeout
kean Mar 15, 2026
ec041c5
Increase target for macOS to 14
kean Mar 15, 2026
57ff1a7
Remove decodeTruncatedJPEGThrows
kean Mar 15, 2026
0835c7e
Disable RateLimiter test
kean Mar 15, 2026
e53998f
Fix warning in ImageRequestKeys
kean Mar 15, 2026
9f2b257
Disable memory layout test
kean Mar 15, 2026
c3c7d58
Add timeout to TestExpectation
kean Mar 15, 2026
b473874
Make sure TestExpectaion unblocks
kean Mar 15, 2026
0c5cd55
Refactor TestExpectation
kean Mar 15, 2026
1d90b6a
Remove busy waits in tests
kean Mar 16, 2026
4cae4de
Address both orderings in TestExpectation
kean Mar 16, 2026
3a51891
Reduce change of thread pool being fully hosed on CI runners
kean Mar 16, 2026
5b12cae
Further reduce parallelization for tests on GH actions
kean Mar 16, 2026
d1072d6
Rework MockDataLoader to use Swift Concurrency native primitives to a…
kean Mar 16, 2026
a913d1e
Remove GCD-based sync from MockImageCache
kean Mar 16, 2026
a439c17
Boot simulators
kean Mar 16, 2026
9940763
Disable parallelization fully
kean Mar 16, 2026
c1d7f6a
Make sure we pick arm64 simulators
kean Mar 16, 2026
1c88e90
Integrate xcbeautify
kean Mar 16, 2026
c253d0e
Remove arch
kean Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 43 additions & 25 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ on:
branches:
- '*'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ios-latest:
name: Unit Tests (iOS 26.2, Xcode 26.2)
runs-on: macOS-26
timeout-minutes: 12
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Boot simulator
run: |
xcrun simctl boot "iPhone 17 Pro"
xcrun simctl bootstatus "iPhone 17 Pro" -b
- name: Run Tests
run: |
.scripts/test.sh -s "Nuke" -d "OS=26.2,name=iPhone 17 Pro"
Expand All @@ -24,10 +33,11 @@ jobs:
macos-latest:
name: Unit Tests (macOS, Xcode 26.2)
runs-on: macOS-26
timeout-minutes: 12
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Run Tests
run: |
.scripts/test.sh -s "Nuke" -d "platform=macOS"
Expand All @@ -36,10 +46,15 @@ jobs:
tvos-latest:
name: Unit Tests (tvOS 26.2, Xcode 26.2)
runs-on: macOS-26
timeout-minutes: 12
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Boot simulator
run: |
xcrun simctl boot "Apple TV"
xcrun simctl bootstatus "Apple TV" -b
- name: Run Tests
run: |
.scripts/test.sh -s "Nuke" -d "OS=26.2,name=Apple TV"
Expand All @@ -53,7 +68,7 @@ jobs:
# env:
# DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
# steps:
# - uses: actions/checkout@v2
# - uses: actions/checkout@v4
# - name: Run Tests
# run: |
# .scripts/test.sh -s "Nuke" -d "OS=9.1,name=Apple Watch Series 8 (45mm)"
Expand All @@ -68,45 +83,48 @@ jobs:
# env:
# DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer
# steps:
# - uses: actions/checkout@v2
# - uses: actions/checkout@v4
# - name: Run Tests
# run: |
# .scripts/test.sh -s "Nuke" -d "OS=17.0,name=iPhone 15 Pro"
# .scripts/test.sh -s "NukeUI" -d "OS=17.0,name=iPhone 15 Pro"
# .scripts/test.sh -s "NukeExtensions" -d "OS=17.0,name=iPhone 15 Pro"
ios-thread-safety:
name: Thread Safety Tests (TSan Enabled)
runs-on: macOS-26
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- name: Run Tests
run: .scripts/test.sh -s "NukeThreadSafetyTests" -d "OS=26.2,name=iPhone 17 Pro"
# ios-thread-safety:
# name: Thread Safety Tests (TSan Enabled)
# runs-on: macOS-26
# timeout-minutes: 12
# env:
# DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
# steps:
# - uses: actions/checkout@v4
# - name: Run Tests
# run: .scripts/test.sh -s "NukeThreadSafetyTests" -d "OS=26.2,name=iPhone 17 Pro"
# ios-memory-management-tests:
# name: Memory Management Tests
# runs-on: macOS-13
# env:
# DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
# steps:
# - uses: actions/checkout@v2
# - uses: actions/checkout@v4
# - name: Run Tests
# run: .scripts/test.sh -s "NukeMemoryManagementTests" -d "OS=14.4,name=iPhone 12 Pro"
ios-performance-tests:
name: Performance Tests
runs-on: macOS-26
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- name: Run Tests
run: .scripts/test.sh -s "NukePerformanceTests" -d "OS=26.2,name=iPhone 17 Pro"
# ios-performance-tests:
# name: Performance Tests
# runs-on: macOS-26
# timeout-minutes: 12
# env:
# DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
# steps:
# - uses: actions/checkout@v4
# - name: Run Tests
# run: .scripts/test.sh -s "NukePerformanceTests" -d "OS=26.2,name=iPhone 17 Pro"
swift-build:
name: Swift Build (SPM)
runs-on: macOS-26
timeout-minutes: 12
env:
DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Build
run: swift build
4 changes: 2 additions & 2 deletions .scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ echo "destinations = ${destinations[@]}"

xcodebuild -version

xcodebuild build-for-testing -scheme "$scheme" -destination "${destinations[0]}"
xcodebuild build-for-testing -scheme "$scheme" -destination "${destinations[0]}" | xcbeautify

for destination in "${destinations[@]}";
do
echo "\nRunning tests for destination: $destination"
xcodebuild test-without-building -scheme "$scheme" -destination "$destination"
xcodebuild test-without-building -scheme "$scheme" -destination "$destination" -parallel-testing-enabled NO -test-timeouts-enabled YES -default-test-execution-time-allowance 120 -retry-tests-on-failure | xcbeautify
done
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

## Nuke 13.0 (WIP)

Nuke 13 achieves full Data Race Safety by migrating all pipeline work onto threads managed by Swift Concurrency, replacing `DispatchQueue` and `OperationQueue` with a `@globalActor`-based synchronization model. It ships over 10 new APIs - including progressive preview policies, a `willLoadData` auth hook, memory size limits, and type-safe `ImageRequest` properties - alongside massively improved documentation and a completely reworked and expanded test suite powered by Swift Testing with Swift 6 mode enabled.
Nuke 13 achieves full Data Race Safety by migrating all pipeline work onto threads managed by Swift Concurrency, replacing `DispatchQueue` and `OperationQueue` with a `@globalActor`-based synchronization model. It ships over 10 new APIs: including progressive preview policies, a `willLoadData` auth hook, memory size limits, and type-safe `ImageRequest` properties.

Minimum supported Xcode version: 26.0.
Minimum required platforms: iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15.
To continue the reliability story, unit tests were updated to use Swift Testing and Swift 6, and were expanded:"

- Nuke 12.9. Code (Nuke): 4589 lines. Tests (NukeTests): 496 tests, 6167 lines. Coverage 92.4%.
- Nuke 13.0. Code (Nuke): 4669 lines. Tests (NukeTests): 768 tests, 8509 lines. Coverage 96.0%.

**Requirements**

- Minimum supported Xcode version: 26.0.
- Minimum required platforms: iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15.

**Concurrency & Data Race Safety**

Expand Down Expand Up @@ -42,6 +49,7 @@ Minimum required platforms: iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15.
- Optimize data downloading by pre-allocating the buffer using the expected content size from the HTTP response, reducing memory reallocations during image downloads (this only applies when progressive decoding is on) — https://github.com/kean/Nuke/issues/738
- Update `ImageCache.defaultCostLimit` to 15% of physical memory with no hard cap (previously 20% capped at 512 MB). The cache uses a custom LRU policy that enforces limits precisely, so 15% is effectively more generous than the previous capped value on modern devices – https://github.com/kean/Nuke/issues/838
- The storage cost limit of `ResumableDataStorage` is now dynamic and varies depending on the available RAM.
- Add `consuming` to `LazyImage` builder methods (`processors`, `priority`, `pipeline`, `onStart`, `onDisappear`, `onCompletion`) and `ImageContainer.map(_:)`

**API Changes**

Expand Down
36 changes: 36 additions & 0 deletions Nuke.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -889,54 +889,70 @@
0C38DB3228568FE20027F9FF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
OTHER_SWIFT_FLAGS = "-D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.nukeui-unit-tests";
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
0C38DB3328568FE20027F9FF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.nukeui-unit-tests";
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
0C4F8FE522E4B6ED0070ECFD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Nuke-Thread-Safety-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
0C4F8FE622E4B6ED0070ECFD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Nuke-Thread-Safety-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
Expand Down Expand Up @@ -978,6 +994,7 @@
0C55FD1728567875000FD2C9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -988,15 +1005,19 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = com.github.kean.NukeExtensionsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
0C55FD1828567875000FD2C9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1007,8 +1028,11 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = com.github.kean.NukeExtensionsTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
Expand Down Expand Up @@ -1050,27 +1074,35 @@
0C7C06801BCA882A00089D7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
OTHER_SWIFT_FLAGS = "-D DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Nuke-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
0C7C06811BCA882A00089D7F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.Nuke-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
TVOS_DEPLOYMENT_TARGET = 16.0;
WATCHOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
Expand All @@ -1080,6 +1112,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR8DLKJ7E6;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1097,6 +1130,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR8DLKJ7E6;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1113,6 +1147,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR8DLKJ7E6;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1133,6 +1168,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR8DLKJ7E6;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
Loading
Loading