68 requires ::aria::ListSourceOf<L, T>
72 : state_(
std::make_shared<State>()) {
73 if (![NSThread isMainThread])
throw std::logic_error(
"table bridge construction requires the main thread");
74 state_->events = std::make_shared<EventQueue>();
75 state_->events->target = state_;
76 state_->table = tableView;
77 state_->cell_for_row = std::move(cell_for_row);
78 state_->snapshot = source.snapshot();
80 std::weak_ptr<State> weak_state = state_;
82 auto row_count_fn = [
weak_state]() -> NSInteger {
83 if (
auto s = weak_state.lock(); s && !s->detached.load(std::memory_order_acquire)) {
84 return static_cast<NSInteger
>(s->snapshot.size());
88 auto cell_for_fn = [
weak_state](UITableView* tv,
89 NSIndexPath* indexPath) -> UITableViewCell* {
90 auto s = weak_state.lock();
91 if (!s || s->detached.load(std::memory_order_acquire))
return nil;
92 const NSInteger row = indexPath.row;
94 ||
static_cast<std::size_t
>(row) >= s->snapshot.size()) {
96 initWithStyle:UITableViewCellStyleDefault
97 reuseIdentifier:@"empty"];
99 auto item = s->snapshot[
static_cast<std::size_t>(row)];
102 initWithStyle:UITableViewCellStyleDefault
103 reuseIdentifier:@"empty"];
105 return s->cell_for_row(tv, item, indexPath);
108 state_->ds = [[AriaUITableDataSource alloc]
109 initWithRowCount:std::move(row_count_fn)
110 cellForFn:std::move(cell_for_fn)];
111 sub_ = source.observe([events = state_->events](const ::aria::ListChange<T>& change) {
112 enqueue_(events, change);
114 tableView.dataSource = state_->ds;
115 tableView.delegate = state_->ds;
120 state_->detached.store(
true, std::memory_order_release);
122 std::lock_guard lock(state_->events->mutex);
123 state_->events->stopped =
true;
126 if ([NSThread isMainThread]) {
131 auto* owner =
new std::shared_ptr<State>(std::move(state_));
132 dispatch_async_f(dispatch_get_main_queue(), owner, [](
void* context) {
133 std::unique_ptr<std::shared_ptr<State>> state{
static_cast<std::shared_ptr<State>*
>(context)};
143 return state_ ? state_->snapshot.size() : 0;
147 if (!state_)
return nullptr;
148 if (i >= state_->snapshot.size())
return nullptr;
149 return state_->snapshot[
i];
156 std::deque<::aria::ListChange<T>> changes;
157 std::weak_ptr<State> target;
158 bool scheduled =
false;
159 bool stopped =
false;
162 UITableView* __weak table = nil;
163 AriaUITableDataSource* __strong ds = nil;
165 std::vector<std::shared_ptr<T>> snapshot;
166 std::atomic<bool> detached{
false};
167 std::shared_ptr<EventQueue> events;
170 static void cleanup_(State& state) {
171 std::deque<::aria::ListChange<T>> discarded;
173 std::lock_guard lock(state.events->mutex);
174 discarded.swap(state.events->changes);
176 UITableView* table = state.table;
177 if (table.dataSource == state.ds) table.dataSource = nil;
178 if (table.delegate == state.ds) table.delegate = nil;
181 static void enqueue_(
const std::shared_ptr<EventQueue>& events, const ::aria::ListChange<T>& change) {
183 std::lock_guard lock(events->mutex);
184 if (events->stopped)
return;
185 events->changes.push_back(change);
186 if (events->scheduled)
return;
187 events->scheduled =
true;
189 if ([NSThread isMainThread]) drain_(events);
191 auto pending = events;
192 dispatch_async(dispatch_get_main_queue(), ^{ drain_(pending); });
196 static void drain_(
const std::shared_ptr<EventQueue>& events) {
197 auto state = events->target.lock();
200 ::aria::ListChange<T> change{};
202 std::lock_guard lock(events->mutex);
203 if (events->stopped || events->changes.empty()) {
204 events->scheduled =
false;
207 change = std::move(events->changes.front());
208 events->changes.pop_front();
210 if (state->detached.load(std::memory_order_acquire))
return;
211 try { apply_change_(*state, change); }
216 static NSIndexPath* ip_(std::size_t row) {
217 return [NSIndexPath indexPathForRow:static_cast<NSInteger>(row) inSection:0];
220 static void apply_change_(State& s,
221 const ::aria::ListChange<T>& ch) {
224 case K::Insert: apply_insert_(s, ch.index, ch.item);
return;
225 case K::Remove: apply_remove_(s, ch.index);
return;
226 case K::Replace: apply_replace_(s, ch.index, ch.item);
return;
227 case K::ItemChanged: apply_item_changed_(s, ch.index);
return;
228 case K::Move: apply_move_(s, ch.from_index, ch.index);
return;
229 case K::Reset: apply_reset_(s, ch);
return;
233 static void apply_insert_(State& s,
235 const std::shared_ptr<T>& item) {
236 if (idx > s.snapshot.size()) idx = s.snapshot.size();
237 s.snapshot.insert(s.snapshot.begin() +
static_cast<std::ptrdiff_t
>(idx),
239 if (!s.table)
return;
240 [s.table insertRowsAtIndexPaths:@[ ip_(idx) ]
241 withRowAnimation:UITableViewRowAnimationFade];
244 static void apply_remove_(State& s, std::size_t idx) {
245 if (idx >= s.snapshot.size())
return;
246 s.snapshot.erase(s.snapshot.begin() +
static_cast<std::ptrdiff_t
>(idx));
247 if (!s.table)
return;
248 [s.table deleteRowsAtIndexPaths:@[ ip_(idx) ]
249 withRowAnimation:UITableViewRowAnimationFade];
252 static void apply_replace_(State& s,
254 const std::shared_ptr<T>& item) {
255 if (idx >= s.snapshot.size())
return;
256 s.snapshot[idx] = item;
257 if (!s.table)
return;
258 [s.table reloadRowsAtIndexPaths:@[ ip_(idx) ]
259 withRowAnimation:UITableViewRowAnimationFade];
262 static void apply_item_changed_(State& s, std::size_t idx) {
263 if (idx >= s.snapshot.size())
return;
264 if (!s.table)
return;
265 [s.table reloadRowsAtIndexPaths:@[ ip_(idx) ]
266 withRowAnimation:UITableViewRowAnimationNone];
269 static void apply_move_(State& s, std::size_t from, std::size_t to) {
270 if (from == to)
return;
271 if (from >= s.snapshot.size() || to >= s.snapshot.size())
return;
272 auto moved = s.snapshot[from];
273 s.snapshot.erase(s.snapshot.begin() +
static_cast<std::ptrdiff_t
>(from));
274 s.snapshot.insert(s.snapshot.begin() +
static_cast<std::ptrdiff_t
>(to),
276 if (!s.table)
return;
277 [s.table moveRowAtIndexPath:ip_(from) toIndexPath:ip_(to)];
280 static void apply_reset_(State& s, const ::aria::ListChange<T>& change) {
281 if (!change.snapshot)
throw std::logic_error(
"Reset requires an owned snapshot");
282 s.snapshot = *change.snapshot;
283 if (s.table) [s.table reloadData];
286 std::shared_ptr<State> state_;
287 ::aria::Subscription sub_;