Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
0adbdb5
ios3: backport AppDrop to armv6 / iOS 3.1 (ARC kept; static ARC+block…
web-flow Jun 3, 2026
4a64126
Create main.yml
TimofeyLednev Jun 3, 2026
3695ecd
ios3: fix libarclite path on newer clang (CI) — install empty stub at…
web-flow Jun 3, 2026
c701a29
Merge branch 'iOS3iThink' of https://github.com/TimofeyLednev/AppDrop…
web-flow Jun 3, 2026
6e013ff
Merge upstream AppDrop v3.0 into iOS3iThink
Jun 4, 2026
bf922e6
ios3: green baseline on v3.0 — backfill iOS6/7 SDK decls + link CFNet…
Jun 4, 2026
61eb4d9
ios3: convert AppDrop from ARC to MRC for true iOS 3/4 support
Jun 4, 2026
63cd65a
ios3: update docs for MRC conversion
Jun 4, 2026
a1209d4
ios3: real FIFO serial GCD queues + lower Info.plist floor to iOS 3
Jun 4, 2026
6042aba
ios3: fix crash — CFBundleExecutable must be IPAInstaller, not AppDrop
Jun 4, 2026
74102b7
ios3: fix launch crash (imp_implementationWithBlock) + rename binary …
Jun 4, 2026
9e17b3c
ios3: fix launch SIGABRT — backfill iOS 4.0 scale APIs
TimofeyLednev Jun 4, 2026
42882a2
ios3: fix launch SIGABRT — backfill modern subscripting in AppDropRun…
TimofeyLednev Jun 4, 2026
ad75192
ios3: backfill block-based array sorting + UIWindow.rootViewControlle…
TimofeyLednev Jun 4, 2026
9818f28
ios3: fix launch crash — self-contained UIBezierPath (ADBezierPath) f…
Jun 4, 2026
12700b7
ios3: fix launch crash — self-contained UIGestureRecognizer backport …
web-flow Jun 4, 2026
d560673
ios3: fix launch crash — replace iOS 4.0 CFRunLoopPerformBlock with C…
web-flow Jun 4, 2026
8d16e3f
Update README.md
TimofeyLednev Jun 4, 2026
b3991d1
ios3: fix launch crash — wrap GCD shim worker-thread blocks in NSAuto…
TimofeyLednev Jun 5, 2026
436699e
ios3: fix launch crash — blocks runtime now retains captured objects …
TimofeyLednev Jun 5, 2026
547b77c
ios3: fix recurring main-thread launch crash — double-release in disp…
TimofeyLednev Jun 5, 2026
145567e
Trying to fix crash
TimofeyLednev Jun 5, 2026
5318e1c
fix
TimofeyLednev Jun 5, 2026
3d33814
More fixes please work..
TimofeyLednev Jun 5, 2026
6ec3eed
More fixes please work..
TimofeyLednev Jun 5, 2026
edeacaa
Fix compilation
TimofeyLednev Jun 5, 2026
20bf789
Fix attempt
TimofeyLednev Jun 5, 2026
e34f077
Fix attempt
TimofeyLednev Jun 5, 2026
48205a3
Maybe
TimofeyLednev Jun 5, 2026
4a0de62
ios3: fix launch crash — rewrite ImageIO to PrivateFrameworks path
TimofeyLednev Jun 5, 2026
44e798a
ios3: backfill +[UIImage imageWithCGImage:scale:orientation:] (iOS 4.0)
TimofeyLednev Jun 5, 2026
94ea1e3
ios3: backfill -[UIImage scale] (iOS 4.0) — fixes launch SIGABRT
TimofeyLednev Jun 5, 2026
64ff924
ios3: fix MRC use-after-free in IconLoader/CollectionVC init — fixes …
TimofeyLednev Jun 5, 2026
a45160c
ios3: backfill UINavigationItem setRightBarButtonItems:/setLeftBarBut…
TimofeyLednev Jun 5, 2026
c29113b
maybe
TimofeyLednev Jun 6, 2026
d18c120
more
TimofeyLednev Jun 6, 2026
82772ef
hmm
TimofeyLednev Jun 6, 2026
af449ee
idk
TimofeyLednev Jun 6, 2026
51dfcab
fix crash again
TimofeyLednev Jun 6, 2026
08b3eaf
jobcell
TimofeyLednev Jun 6, 2026
cf430e6
implement ipa installer for ios 3
TimofeyLednev Jun 6, 2026
7851b5a
Merge upstream AppDrop v3.1.2 into iOS3iThink
TimofeyLednev Jun 6, 2026
df77433
ios3: backfill four iOS 5.0 UIKit gaps in AppDropRuntime (fix #1 #2 #…
TimofeyLednev Jun 6, 2026
df95fb7
pkg: lower minimum to iOS 3.0 and make external IPA installer optional
TimofeyLednev Jun 6, 2026
daf274c
Maybe good
TimofeyLednev Jun 6, 2026
aa86c59
Fix install crash on iOS 3: backfill +[NSPropertyListSerialization pr…
TimofeyLednev Jun 7, 2026
e440b4d
Fix iOS 3 install: use InProcessInstaller in the autonomous-download …
TimofeyLednev Jun 7, 2026
1e8d14d
hm
TimofeyLednev Jun 7, 2026
54e283d
more fixes
TimofeyLednev Jun 7, 2026
769aacf
Fix build: restore @property decls so @dynamic block accessors compil…
web-flow Jun 7, 2026
658b2a7
Scrolling maybe fix???
TimofeyLednev Jun 10, 2026
08b5034
idk
TimofeyLednev Jun 10, 2026
1d64d22
moree
TimofeyLednev Jun 10, 2026
6adb137
Merge remote-tracking branch 'upstream/main' into iOS3iThink
Jun 10, 2026
1e8da6b
iOS3 fixups for v3.1.3 merge: MRC-safe ADIconReq (C blocks runtime), …
Jun 10, 2026
1e86e33
iOS3: backport -[NSOperationQueue addOperationWithBlock:] and +[NSOpe…
Jun 10, 2026
f6c7c27
Add Russian Language
TimofeyLednev Jun 10, 2026
40317c4
Merge upstream main into iOS3iThink
TimofeyLednev Jun 11, 2026
c67e77f
Fix MRC merge fallout and install stall timer
TimofeyLednev Jun 11, 2026
09909bc
fileprotection
TimofeyLednev Jun 11, 2026
eae2cf5
ah
TimofeyLednev Jun 11, 2026
ba7fde7
localcatalog
TimofeyLednev Jun 11, 2026
f30a19e
fix random crash in settings
TimofeyLednev Jun 11, 2026
cb943ba
Use loading imageio om ios3 and 4
TimofeyLednev Jun 11, 2026
41a5760
This should work
TimofeyLednev Jun 11, 2026
5494097
oops
TimofeyLednev Jun 11, 2026
14c09db
common
TimofeyLednev Jun 11, 2026
1b4481a
hm
TimofeyLednev Jun 11, 2026
4606587
Update .gitignore
TimofeyLednev Jun 11, 2026
5fe1e65
fixes with strange mach-o and arc
TimofeyLednev Jun 11, 2026
76d7fa1
Update Makefile
TimofeyLednev Jun 11, 2026
01e04ba
Update build.sh
TimofeyLednev Jun 11, 2026
4f3dbd0
fix
TimofeyLednev Jun 11, 2026
939b44a
Need to test
TimofeyLednev Jun 11, 2026
2e0a5b7
Update main.yml
TimofeyLednev Jun 11, 2026
e61aa82
Update control
TimofeyLednev Jun 11, 2026
e415cfb
Update main.yml
TimofeyLednev Jun 11, 2026
d358bc2
Update main.yml
TimofeyLednev Jun 11, 2026
fa5f1ab
Update main.yml
TimofeyLednev Jun 11, 2026
652d843
Update main.yml
TimofeyLednev Jun 11, 2026
9eab049
Update main.yml
TimofeyLednev Jun 11, 2026
b7f801a
Update main.yml
TimofeyLednev Jun 11, 2026
94cea6d
Update main.yml
TimofeyLednev Jun 11, 2026
3a0fd8f
Update main.yml
TimofeyLednev Jun 11, 2026
3cabd0a
Update main.yml
TimofeyLednev Jun 11, 2026
2f02e58
Update main.yml
TimofeyLednev Jun 12, 2026
37c4bed
Merge upstream AdrienRL1/AppDrop v3.2.0.3 into iOS3iThink
TimofeyLednev Jun 12, 2026
ee28011
BUILD-LINUX.md: document universal DEB instead of universal IPA
TimofeyLednev Jun 12, 2026
1f1b110
Update main.yml
TimofeyLednev Jun 12, 2026
3e9fc4f
MRC: fix v3.2 ARC-isms that broke both armv7 and armv6 builds
TimofeyLednev Jun 12, 2026
5dbd8eb
Fix armv7 CI stage: plutil fallback + rebind the real Mach-O
TimofeyLednev Jun 12, 2026
b1767de
Update CategoryViewController.m
TimofeyLednev Jun 12, 2026
fcb90a8
Update StatsClient.m
TimofeyLednev Jun 12, 2026
7205f75
Update Localizable.strings
TimofeyLednev Jun 12, 2026
8e643fe
Update control
TimofeyLednev Jun 12, 2026
4e94242
Update Info.plist
TimofeyLednev Jun 12, 2026
8b23648
Update main.yml
TimofeyLednev Jun 12, 2026
4ccc09d
Update AppDropLauncher.sh
TimofeyLednev Jun 12, 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
132 changes: 132 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Build AppDrop binaries

on:
push:
branches: [ iOS3iThink ]
workflow_dispatch:

jobs:
ios5_10-armv7:
name: Cross-compile armv7 / iOS 5-10
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
clang lld llvm llvm-dev \
cmake make git wget zip dpkg \
libplist-dev libplist-utils libssl-dev python3

- name: Bootstrap build-toolchain
run: |
mkdir -p build-toolchain
if [ ! -d build-toolchain/theos ]; then
git clone --recursive https://github.com/theos/theos.git build-toolchain/theos
fi
if [ ! -x build-toolchain/theos/toolchain/linux/iphone/bin/clang ]; then
curl -fsSL https://github.com/L1ghtmann/llvm-project/releases/latest/download/iOSToolchain-x86_64.tar.xz \
| tar -xJf - -C build-toolchain/theos/toolchain
fi
if [ ! -d build-toolchain/theos/sdks/iPhoneOS10.3.sdk ]; then
tmpdir="$(mktemp -d)"
git clone --no-checkout --depth 1 --filter=blob:none https://github.com/theos/sdks.git "$tmpdir/sdks"
( cd "$tmpdir/sdks" && git sparse-checkout set iPhoneOS10.3.sdk && git checkout )
mkdir -p build-toolchain/theos/sdks
mv "$tmpdir/sdks/iPhoneOS10.3.sdk" build-toolchain/theos/sdks/
rm -rf "$tmpdir"
fi

- name: Build armv7 package
run: |
chmod +x build.sh
./build.sh package FINALPACKAGE=1

- name: Upload armv7 artifact
uses: actions/upload-artifact@v4
with:
name: AppDrop-armv7
path: IPAInstaller/packages/*

ios3-armv6:
name: Cross-compile armv6 / iOS 3 (iOS 5.1 SDK)
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
clang lld llvm llvm-dev \
cmake make git wget zip dpkg \
libplist-dev libplist-utils libssl-dev python3

- name: Build IPA + DEB
run: |
chmod +x ios3/build-ios3.sh
./ios3/build-ios3.sh

- name: Upload IPA
uses: actions/upload-artifact@v4
with:
name: AppDrop-iOS3-armv6-ipa
path: ios3/build/AppDrop.ipa

- name: Upload DEB
uses: actions/upload-artifact@v4
with:
name: AppDrop-iOS3-armv6-deb
path: ios3/build/AppDrop.deb

universal-deb:
name: Assemble universal DEB (armv6 + armv7, separate slices)
runs-on: ubuntu-24.04
needs: [ ios5_10-armv7, ios3-armv6 ]
steps:
- name: Download armv7 DEB
uses: actions/download-artifact@v4
with:
name: AppDrop-armv7
path: artifacts/armv7

- name: Download armv6 DEB
uses: actions/download-artifact@v4
with:
name: AppDrop-iOS3-armv6-deb
path: artifacts/armv6-deb

- name: Build universal DEB
run: |
set -euo pipefail
rm -rf merge
mkdir -p merge/root

armv7_deb="$(find artifacts/armv7 -name '*.deb' | head -n1)"
armv6_deb="$(find artifacts/armv6-deb -name '*.deb' | head -n1)"
if [ -z "$armv7_deb" ] || [ -z "$armv6_deb" ]; then
echo "ERROR: missing armv7 or armv6 DEB artifact" >&2
exit 1
fi

dpkg-deb -R "$armv6_deb" merge/root
tmp_armv7="$(mktemp -d)"
dpkg-deb -x "$armv7_deb" "$tmp_armv7"
cp "$tmp_armv7/Applications/AppDrop.app/AppDrop.armv7" merge/root/Applications/AppDrop.app/AppDrop.armv7
chmod 0755 merge/root/Applications/AppDrop.app/AppDrop.armv7

mkdir -p merge/out
dpkg-deb -Zgzip -b merge/root merge/out/AppDrop-universal.deb >/dev/null
mv merge/out/AppDrop-universal.deb merge/AppDrop-universal.deb
echo "Assembled universal DEB:"
dpkg-deb -I merge/AppDrop-universal.deb | sed -n '1,20p'

- name: Upload universal DEB
uses: actions/upload-artifact@v4
with:
name: AppDrop-universal-deb
path: merge/AppDrop-universal.deb
2 changes: 1 addition & 1 deletion BUILD-LINUX.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ memes dylibs liees (il demarre donc sur iPad 1 / iOS 5.1.1 comme avant).
./build.sh clean
```

Le `.deb` (et le `.ipa` si besoin) sort dans `IPAInstaller/packages/`.
Le `.deb` (et l`.ipa` si besoin) sort dans `IPAInstaller/packages/`.

## Contenu de build-toolchain/

Expand Down
19 changes: 17 additions & 2 deletions IPAInstaller/ADNumberPickerSheet.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,28 @@
@interface ADNumberPickerSheet () <UIPickerViewDataSource, UIPickerViewDelegate>
@property (nonatomic, strong) NSArray *values; // NSNumber<int>
@property (nonatomic, strong) NSArray *labels; // NSString
@property (nonatomic, copy) void (^onPick)(NSInteger value);
@property (nonatomic, strong) UIControl *dim;
@property (nonatomic, strong) UIView *panel;
@property (nonatomic, strong) UIPickerView *picker;
@property (nonatomic, copy) void (^onPick)(NSInteger value);
@end

@implementation ADNumberPickerSheet
@implementation ADNumberPickerSheet {
// iOS 3: blocks aren't ObjC objects, so a synthesized @property(copy) block
// setter calls objc_setProperty(copy=YES) → -copyWithZone: on the block →
// objc_msgSend dereferences a zeroed isa → Bus error (signal 10). This is the
// crash when opening "Apps per row" / "Home tiles per row". Back the block
// manually through the C blocks runtime (_Block_copy/_Block_release) — see
// AppDropBlocks.h (AD_BLOCK_ACCESSORS).
void (^_onPickBlock)(NSInteger value);
}
@dynamic onPick;
AD_BLOCK_ACCESSORS(onPick, setOnPick, _onPickBlock, void(^)(NSInteger value))

- (void)dealloc {
if (_onPickBlock) _Block_release((const void *)_onPickBlock);
[super dealloc];
}

+ (void)presentInView:(UIView *)host
title:(NSString *)title
Expand Down
10 changes: 10 additions & 0 deletions IPAInstaller/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,16 @@ - (void)setupAppearance {
// harmless belt-and-suspenders for anything not reached directly.
if ([IOS6Theme isDefaultTheme]) return;

// The entire UIAppearance proxy system is iOS 5.0+. On iOS 3.x, +[UINavigationBar
// appearance] / +[UITabBar appearance] / +[UIBarButtonItem appearance] are themselves
// unrecognized selectors on the CLASS — sending them throws NSInvalidArgumentException
// before any respondsToSelector: on the (never-returned) proxy can guard us. That is the
// "+[UINavigationBar appearance]: unrecognized selector" launch crash seen after switching
// to a non-default theme. Dark/colour themes already do the real styling per-instance via
// [IOS6Theme applyToNavigationBar:] / applyToTabBar: (ADNavigationController + the tab bar),
// so the proxy here is purely belt-and-suspenders — skip it entirely when unavailable.
if (![UINavigationBar respondsToSelector:@selector(appearance)]) return;

id navProxy = [UINavigationBar appearance];
{
UIImage *navBg = [IOS6Theme navBarBackground];
Expand Down
17 changes: 9 additions & 8 deletions IPAInstaller/AppDetailViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ - (void)setContentOffset:(CGPoint)offset {
CGContextStrokeEllipseInRect(ctx, CGRectMake(4, 4, 18, 18));
CGContextMoveToPoint(ctx, 13, 13); CGContextAddLineToPoint(ctx, 13, 7.5); CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, 13, 13); CGContextAddLineToPoint(ctx, 17.5, 15); CGContextStrokePath(ctx);
img = UIGraphicsGetImageFromCurrentImageContext();
img = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
return img;
}
Expand All @@ -128,7 +128,7 @@ - (void)setContentOffset:(CGPoint)offset {
CGContextMoveToPoint(ctx, 13, 4); CGContextAddLineToPoint(ctx, 13, 15); CGContextStrokePath(ctx); // shaft
CGContextMoveToPoint(ctx, 8, 10); CGContextAddLineToPoint(ctx, 13, 15); CGContextAddLineToPoint(ctx, 18, 10); CGContextStrokePath(ctx); // arrowhead
CGContextMoveToPoint(ctx, 6, 20); CGContextAddLineToPoint(ctx, 20, 20); CGContextStrokePath(ctx); // tray
img = UIGraphicsGetImageFromCurrentImageContext();
img = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
return img;
}
Expand All @@ -141,7 +141,7 @@ - (void)setContentOffset:(CGPoint)offset {
[[UIColor whiteColor] setFill];
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(8, 7, 3.4, 12) cornerRadius:1.5] fill];
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(15, 7, 3.4, 12) cornerRadius:1.5] fill];
img = UIGraphicsGetImageFromCurrentImageContext();
img = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
return img;
}
Expand All @@ -158,7 +158,7 @@ - (void)setContentOffset:(CGPoint)offset {
[p closePath];
[[UIColor whiteColor] setFill];
[p fill];
img = UIGraphicsGetImageFromCurrentImageContext();
img = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
return img;
}
Expand All @@ -174,7 +174,7 @@ - (void)setContentOffset:(CGPoint)offset {
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextMoveToPoint(ctx, 8, 8); CGContextAddLineToPoint(ctx, 18, 18); CGContextStrokePath(ctx);
CGContextMoveToPoint(ctx, 18, 8); CGContextAddLineToPoint(ctx, 8, 18); CGContextStrokePath(ctx);
img = UIGraphicsGetImageFromCurrentImageContext();
img = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
return img;
}
Expand Down Expand Up @@ -448,12 +448,12 @@ - (void)probeEncryptionForCurrentVersion {
self.encryptionProbed = YES;
NSString *url = self.app[@"url"];
if (url.length == 0) return;
__weak AppDetailViewController *weakSelf = self;
AD_WEAK AppDetailViewController *weakSelf = self;
[MachOInspector inspectURL:url completion:^(MachOInspectionResult r) {
AppDetailViewController *s = weakSelf;
if (!s) return;
if (r != MachOInspectionResultEncrypted) return; // decrypted/unknown → nothing to do
__weak AppDetailViewController *ws = s;
AD_WEAK AppDetailViewController *ws = s;
if (s.allowVersionSwitch) {
// Auto path (catalog/search): resolve SILENTLY — no transient 🔒 banner. Switch to a
// clean build in place if one exists; otherwise say there's none.
Expand Down Expand Up @@ -498,7 +498,7 @@ - (void)findDecryptedAlternativeThen:(void (^)(NSDictionary *decryptedOrNil))don
- (void)probeCandidates:(NSArray *)candidates index:(NSUInteger)i then:(void (^)(NSDictionary *))done {
if (i >= candidates.count) { if (done) done(nil); return; }
NSDictionary *v = [candidates objectAtIndex:i];
__weak AppDetailViewController *weakSelf = self;
AD_WEAK AppDetailViewController *weakSelf = self;
[MachOInspector inspectURL:v[@"url"] completion:^(MachOInspectionResult r) {
AppDetailViewController *s = weakSelf;
if (!s) return;
Expand Down Expand Up @@ -1104,6 +1104,7 @@ - (void)refreshInstallButtonTitle {

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)o { return YES; }
Expand Down
47 changes: 47 additions & 0 deletions IPAInstaller/AppDropLauncher.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh
# AppDrop launcher: pick the right slice by iOS VERSION only.
# iOS 3-4 -> AppDrop.armv6
# iOS 5+ -> AppDrop.armv7
# No external tools (head/awk/defaults/plutil don't exist on stock iOS 3) —
# ProductVersion is parsed from SystemVersion.plist with shell builtins only.
DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"

SV="/System/Library/CoreServices/SystemVersion.plist"
VERSION=""
found=0
if [ -r "$SV" ]; then
while IFS= read -r line; do
if [ "$found" = 1 ]; then
case "$line" in
*"<string>"*)
VERSION="${line#*<string>}"
VERSION="${VERSION%%</string>*}"
break
;;
esac
fi
case "$line" in
*ProductVersion*"<string>"*)
VERSION="${line#*<string>}"
VERSION="${VERSION%%</string>*}"
break
;;
*ProductVersion*) found=1 ;;
esac
done < "$SV"
fi

MAJOR="${VERSION%%.*}"
case "$MAJOR" in
''|*[!0-9]*) MAJOR=0 ;;
esac

if [ "$MAJOR" -ge 5 ]; then
[ -x "$DIR/AppDrop.armv7" ] && exec "$DIR/AppDrop.armv7" "$@"
[ -x "$DIR/AppDrop.armv6" ] && exec "$DIR/AppDrop.armv6" "$@"
exit 1
fi
# iOS 3-4, or version unknown: armv6 first.
[ -x "$DIR/AppDrop.armv6" ] && exec "$DIR/AppDrop.armv6" "$@"
[ -x "$DIR/AppDrop.armv7" ] && exec "$DIR/AppDrop.armv7" "$@"
exit 1
20 changes: 18 additions & 2 deletions IPAInstaller/AppRowCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@ @interface AppRowCell ()
@property (nonatomic, copy) NSArray *appsCache;
@end

@implementation AppRowCell
@implementation AppRowCell {
void (^_onTileTapBlock)(NSDictionary *);
BOOL (^_isAppSelectedBlock)(NSDictionary *);
}

// iOS 3: blocks aren't ObjC objects, so the synthesized copy setters crash in
// objc_msgSend. Back them manually via the C blocks runtime — see
// AppDropBlocks.h (AD_BLOCK_ACCESSORS).
@dynamic onTileTap, isAppSelectedBlock;
AD_BLOCK_ACCESSORS(onTileTap, setOnTileTap, _onTileTapBlock, void(^)(NSDictionary *))
AD_BLOCK_ACCESSORS(isAppSelectedBlock, setIsAppSelectedBlock, _isAppSelectedBlock, BOOL(^)(NSDictionary *))

- (void)dealloc {
if (_onTileTapBlock) _Block_release((const void *)_onTileTapBlock);
if (_isAppSelectedBlock) _Block_release((const void *)_isAppSelectedBlock);
[super dealloc];
}

// v3.0: the catalogue grid is configured by an explicit COLUMN COUNT (chosen via the native wheel
// picker in Settings → Affichage), not a 0–1 density. Idiom-aware default: iPhone 1 = single-column
Expand Down Expand Up @@ -91,7 +107,7 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr
- (void)ensureTileCount {
while ((NSInteger)self.tiles.count < self.tilesPerRow) {
AppTileView *t = [[AppTileView alloc] initWithFrame:CGRectZero];
__weak typeof(self) ws = self;
AD_WEAK typeof(self) ws = self;
t.onTap = ^(NSDictionary *app) {
if (ws.onTileTap) ws.onTileTap(app);
};
Expand Down
Loading