Skip to content

Commit a32e0e2

Browse files
committed
refactor(ios): enhance segment view reconciliation with two-pass matching strategy
1 parent cfdcdee commit a32e0e2

2 files changed

Lines changed: 40 additions & 4 deletions

File tree

ios/utils/SegmentReconciler.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ NS_ASSUME_NONNULL_BEGIN
1212
@end
1313

1414
@interface ENRMSegmentReconciler : NSObject
15-
// Reuses views by index when the segment kind still matches. Matching
16-
// signatures skip updates; changed signatures update the existing view.
15+
// Reconciles segment views using a two-pass strategy:
16+
// 1. Try positional match: if the view at the same index matches kind, reuse it.
17+
// 2. Fall back to signature-based lookup: find an unused view with the same
18+
// kind+signature elsewhere in the old list. This handles mid-stream segment
19+
// insertions where a completed table/math block shifts position.
1720
+ (ENRMSegmentReconciliationResult *)
1821
reconcileCurrentViews:(NSArray<RCTUIView *> *)currentViews
1922
currentSignatures:(NSArray<NSNumber *> *)currentSignatures

ios/utils/SegmentReconciler.m

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ @implementation ENRMSegmentReconciler
2525
sourceSignatures = @[];
2626
}
2727

28+
// Build a signature -> (view, index) lookup for fallback reuse when
29+
// positional matching fails (e.g. a new segment inserted before an
30+
// existing table shifts it to a different index).
31+
NSMutableDictionary<NSNumber *, NSMutableArray<NSNumber *> *> *signatureToIndices =
32+
[NSMutableDictionary dictionaryWithCapacity:sourceSignatures.count];
33+
[sourceSignatures enumerateObjectsUsingBlock:^(NSNumber *sig, NSUInteger idx, BOOL *stop) {
34+
NSMutableArray<NSNumber *> *indices = signatureToIndices[sig];
35+
if (!indices) {
36+
indices = [NSMutableArray arrayWithObject:@(idx)];
37+
signatureToIndices[sig] = indices;
38+
} else {
39+
[indices addObject:@(idx)];
40+
}
41+
}];
42+
2843
NSMutableArray<RCTUIView *> *nextViews = [NSMutableArray arrayWithCapacity:renderedSegments.count];
2944
NSMutableArray<NSNumber *> *nextSignatures = [NSMutableArray arrayWithCapacity:renderedSegments.count];
3045
NSMutableSet<RCTUIView *> *reusedViews = [NSMutableSet setWithCapacity:sourceViews.count];
@@ -35,12 +50,30 @@ @implementation ENRMSegmentReconciler
3550
RCTUIView *view = nil;
3651
NSNumber *nextSignature = @(segment.signature);
3752

38-
if (existingView && matchesKind(existingView, segment)) {
53+
// 1. Positional match: same index, same kind.
54+
if (existingView && ![reusedViews containsObject:existingView] && matchesKind(existingView, segment)) {
3955
if (![existingSignature isEqual:nextSignature]) {
4056
updateView(existingView, segment);
4157
}
4258
view = existingView;
43-
} else {
59+
}
60+
61+
// 2. Signature-based fallback: find an unused view with exact same signature.
62+
if (!view) {
63+
NSMutableArray<NSNumber *> *candidateIndices = signatureToIndices[nextSignature];
64+
while (candidateIndices.count > 0) {
65+
NSUInteger candidateIdx = candidateIndices.firstObject.unsignedIntegerValue;
66+
[candidateIndices removeObjectAtIndex:0];
67+
RCTUIView *candidate = sourceViews[candidateIdx];
68+
if (![reusedViews containsObject:candidate] && matchesKind(candidate, segment)) {
69+
view = candidate;
70+
break;
71+
}
72+
}
73+
}
74+
75+
// 3. No reusable view found — create a new one.
76+
if (!view) {
4477
view = createView(segment);
4578
attachView(view);
4679
}

0 commit comments

Comments
 (0)