Preface

The goal of this was to document my journey reverse engineering the Apple Music app to make the queue more like Spotify. Pre iOS 18 the Apple Music queue acted more as a stack than a queue and I wanted to modify the queue feature to be more like a queue. In iOS 18 it the Apple Music queue was updated to act more like Spotify so I have abandoned working on it. I decided to publish the post even though it is extremely rough and unfinished to share my findings and hopefully to help others to start reverse engineering with iOS. I also used this blog as a notebook to remember stuff I found earlier when reverse engineering.

Setup

Setting up debugserver entitlements

Using debugserver after scping to iphone

➜  Payload git:(master) ✗ ssh mobile@192.168.1.29
(mobile@192.168.1.29) Password for mobile@Rahuls-Developer-iPhone:
Rahuls-Developer-iPhone:~ mobile% /var/jb/usr/bin/debugserver 0.0.0.0:4445 -a Music
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-1300.2.10
 for arm64.
Attaching to process Music...
error: failed to attach to process named: ""
Exiting.

LLDB

Initial setup

(lldb) platform select remote-ios
(lldb) process connect connect://192.168.1.29:4445

In Apple Music if you want to queue songs you can swipe right on the song and it will queue. After swiping on some songs to load the proper classes into memory I take a recursiveDescription of the keyWindow. This gives a hierarchy for the UI

(lldb) po [[UIWindow keyWindow] recursiveDescription]
...
<UISwipeActionPullView: 0x104495100; cellEdge = UIRectEdgeLeft, actions = <NSArray: 0x282ca7030>>
    | <UISwipeActionStandardButton: 0x1044fdb60; frame = (74 0; 74 48); anchorPoint = (1, 0.5); opaque = NO; autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x28222a880>>
    | <UIView: 0x1044e7470; frame = (-517 0; 591 48); layer = <CALayer: 0x282228660>>
    | <UIImageView: 0x10987fc70; frame = (26 16; 22 16.5); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x28222a4e0>>
    | <UIButtonLabel: 0x109804f20; frame = (27 7.5; 47 33); text = 'Playing Last'; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2800de490>>
    | <UISwipeActionStandardButton: 0x1098c4fb0; frame = (0 0; 74 48); anchorPoint = (1, 0.5); opaque = NO; autoresize = W+H; tintColor = UIExtendedGrayColorSpace 1 1; layer = <CALayer: 0x28222a9c0>>
    | <UIView: 0x10982a880; frame = (-517 0; 591 48); layer = <CALayer: 0x28222ae80>>
    | <UIImageView: 0x10982b1f0; frame = (26 15.5; 22 17); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x282229f60>>
    | <UIButtonLabel: 0x1098c52a0; frame = (27 7.5; 47 33); text = 'Playing Next'; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2800dead0>>
	...

From the output of this I search for keywords that has to do with queue songs and I found the string Playing Next

After some google searching I find the header file for UISwipeActionPullView. There is no official Apple documentation on this class. We can also see these instace methods with lldb.

(lldb) po [@"" __methodDescriptionForClass:[UISwipeActionPullView class]]

in UISwipeActionPullView:
	Properties:
		@property (nonatomic, getter=_roundedStyleCornerRadius, setter=_setRoundedStyleCornerRadius:) double roundedStyleCornerRadius;  (@synthesize roundedStyleCornerRadius = _roundedStyleCornerRadius;)
		@property (weak, nonatomic) <UISwipeActionPullViewDelegate>* delegate;  (@synthesize delegate = _delegate;)
		@property (readonly, nonatomic) unsigned long cellEdge;  (@synthesize cellEdge = _cellEdge;)
		@property (nonatomic) struct UIEdgeInsets contentInsets;  (@synthesize contentInsets = _contentInsets;)
		@property (copy, nonatomic) UIColor* backgroundPullColor;  (@synthesize backgroundPullColor = _backgroundPullColor;)
		@property (readonly, nonatomic) UIContextualAction* primarySwipeAction;
		@property (readonly, nonatomic) double currentOffset;  (@synthesize currentOffset = _currentOffset;)
		@property (readonly, nonatomic) double openThreshold;
		@property (readonly, nonatomic) double confirmationThreshold;
		@property (readonly, nonatomic) UIColor* primaryActionColor;
		@property (readonly, nonatomic) BOOL primaryActionIsDestructive;
		@property (readonly, nonatomic) BOOL hasActions;
		@property (nonatomic) BOOL buttonsUnderlapSwipedView;  (@synthesize buttonsUnderlapSwipedView = _buttonsUnderlapSwipedView;)
		@property (nonatomic) BOOL autosizesButtons;  (@synthesize autosizesButtons = _autosizesButtons;)
		@property (nonatomic) unsigned long state;  (@synthesize state = _state;)
	Instance Methods:
		- (void) freeze; (0x18413260c)
		- (struct UIEdgeInsets) contentInsets; (0x184133c34)
		- (void) setDelegate:(id)arg1; (0x184133c20)
		- (void) moveToOffset:(double)arg1 extraOffset:(double)arg2 animator:(id)arg3 usingSpringWithStiffness:(double)arg4 initialVelocity:(double)arg5; (0x184132958)
		- (void) setContentInsets:(struct UIEdgeInsets)arg1; (0x184133c4c)
		- (double) currentOffset; (0x184133bf0)
		- (unsigned long) cellEdge; (0x184133be0)
		- (void) layoutSubviews; (0x184131ad4)
		- (void) _setWidth:(double)arg1; (0x184131fb0)
		- (void) resetView; (0x184132364)
		- (unsigned long) state; (0x184133cb0)
		- (id) delegate; (0x184133c00)
		- (void) .cxx_destruct; (0x184133cf0)
		- (void) setFrame:(struct CGRect)arg1; (0x184131e90)
		- (BOOL) hasActions; (0x184130e8c)
		- (id) hitTest:(struct CGPoint)arg1 withEvent:(id)arg2; (0x18413382c)
		- (Class) _buttonClass; (0x184131240)
		- (double) openThreshold; (0x184130f80)
		- (double) _roundedStyleCornerRadius; (0x184133cd0)
		- (void) _setRoundedStyleCornerRadius:(double)arg1; (0x184133ce0)
		- (BOOL) autosizesButtons; (0x184133c90)
		- (id) primarySwipeAction; (0x184130dec)
		- (void) setAutosizesButtons:(BOOL)arg1; (0x184133ca0)
		- (void) configureWithSwipeActions:(id)arg1; (0x1841328b4)
		- (double) confirmationThreshold; (0x184131154)
		- (id) sourceViewForAction:(id)arg1; (0x1841334c8)
		- (void) _performAction:(id)arg1 offset:(double)arg2 extraOffset:(double)arg3; (0x184133404)
		- (BOOL) buttonsUnderlapSwipedView; (0x184133c80)
		- (id) initWithFrame:(struct CGRect)arg1 cellEdge:(unsigned long)arg2 style:(unsigned long)arg3; (0x184130cdc)
		- (void) setBackgroundPullColor:(id)arg1; (0x184133c74)
		- (void) setState:(unsigned long)arg1; (0x184133cc0)
		- (void) setButtonsUnderlapSwipedView:(BOOL)arg1; (0x184130f04)
		- (BOOL) primaryActionIsDestructive; (0x184130e40)
		- (unsigned long) _swipeActionCount; (0x184131228)
		- (double) _paddingToSwipedView; (0x184131294)
		- (double) _totalInterButtonPadding; (0x1841312d4)
		- (double) _directionalMultiplier; (0x1841311fc)
		- (double) _interButtonPadding; (0x1841312b4)
		- (void) _setupClippingViewIfNeeded; (0x184131334)
		- (void) _tappedButton:(id)arg1; (0x1841336b0)
		- (void) _pressedButton:(id)arg1; (0x184133788)
		- (void) _unpressedButton:(id)arg1; (0x1841337e0)
		- (void) _rebuildButtons; (0x1841314c0)
		- (void) _layoutClippingLayer; (0x184132244)
		- (void) _setLayerBounds:(struct CGRect)arg1; (0x184132004)
		- (id) primaryActionColor; (0x184130eb0)
		- (id) backgroundPullColor; (0x184133c64)
		- (id) description; (0x184133ad4)
		- (void) setBounds:(struct CGRect)arg1; (0x184131f14)

LLDB also shows the address instance functions. _tappedButton looks interesting so I set a breakpoint there and take a look at it

(lldb) b -a 0x1841336b0
Breakpoint 13: where = UIKitCore`-[UISwipeActionPullView _tappedButton:], address = 0x00000001841336b0
(lldb) con
Process 29939 resuming
Process 29939 stopped // It stopped after I tapped the "Play Next" button
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 13.1
    frame #0: 0x00000001841336b0 UIKitCore`-[UISwipeActionPullView _tappedButton:]
UIKitCore`-[UISwipeActionPullView _tappedButton:]:
->  0x1841336b0 <+0>:  stp    x24, x23, [sp, #-0x40]!
    0x1841336b4 <+4>:  stp    x22, x21, [sp, #0x10]
    0x1841336b8 <+8>:  stp    x20, x19, [sp, #0x20]
    0x1841336bc <+12>: stp    x29, x30, [sp, #0x30]
Target 0: (Music) stopped.
(lldb) po $x0
<UISwipeActionPullView: 0x11e097190; cellEdge = UIRectEdgeLeft, actions = <NSArray: 0x2820b5230>>

(lldb) po NSStringFromSelector($x1)
_tappedButton:

Every objective C method implicitally starts with 2 parameters: self, the instance were calling the method on and command the selector we are calling. When we ask $x0 is class and $x1 is the function. The UIViewController is responsible for handling the logic when users perform actions in the UI. To find the UIViewController for our UISwipeActionPullView we can start by looking at the nextResponder in lldb

(lldb) po [$x0 nextResponder]
<UICollectionView: 0x1048ed600; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x2820ba6d0>; layer = <CALayer: 0x282e3a920>; contentOffset: {0, 3739.5}; contentSize: {375, 6398}; adjustedContentInset: {64, 0, 113, 0}; layout: <_TtCC16MusicApplication19SongsViewControllerP33_A45DAA2A24828EFCFB77323F0B26236B11TableLayout: 0x11e03dfa0>; dataSource: <MusicApplication.SongsViewController: 0x1048d5c00>>

The recursiveDescription of keyWindow also showed that this UICollectionView was the parent view. Every UICollectionView has a delegate which follows the UICollectionViewDelegate protocol. We can use lldb to see the delegate of our UICollectionView and to describe what functions the delegate implements.

(lldb) po [$x0 nextResponder] delegate]
note: object description requested, but type doesn't implement a custom object description. Consider using "p" instead of "po" (this note will only be shown once per debug session).
<MusicApplication.SongsViewController: 0x10682c800>
(lldb) po [@"" __methodDescriptionForClass:(Class)NSClassFromString(@"MusicApplication.SongsViewController")]

in MusicApplication.SongsViewController:
	Properties:
		@property (nonatomic, readonly) BOOL canBecomeFirstResponder;
	Instance Methods:
		- (id) init; (0x1037070c8)
		- (id) initWithCoder:(id)arg1; (0x1037070e8)
		- (void) viewWillAppear:(BOOL)arg1; (0x1037082bc)
		- (void) viewWillDisappear:(BOOL)arg1; (0x1037082cc)
		- (void) viewDidAppear:(BOOL)arg1; (0x1037084e0)
		- (void) viewDidLoad; (0x10370985c)
		- (void) traitCollectionDidChange:(id)arg1; (0x103709f78)
		- (void) viewWillTransitionToSize:(struct CGSize)arg1 withTransitionCoordinator:(id)arg2; (0x103709fc8)
		- (void) viewDidLayoutSubviews; (0x10370a1e4)
		- (BOOL) canBecomeFirstResponder; (0x10370a210)
		- (long) numberOfSectionsInCollectionView:(id)arg1; (0x10370a218)
		- (long) collectionView:(id)arg1 numberOfItemsInSection:(long)arg2; (0x10370a270)
		- (id) collectionView:(id)arg1 cellForItemAtIndexPath:(id)arg2; (0x10370a99c)
		- (id) collectionView:(id)arg1 viewForSupplementaryElementOfKind:(id)arg2 atIndexPath:(id)arg3; (0x10370b540)
		- (id) _sectionIndexTitlesForCollectionView:(id)arg1; (0x10370b66c)
		- (id) _collectionView:(id)arg1 indexPathForSectionIndexTitle:(id)arg2 atIndex:(long)arg3; (0x10370b878)
		- (void) collectionView:(id)arg1 willPerformPreviewActionForMenuWithConfiguration:(id)arg2 animator:(id)arg3; (0x10370bcf0)
		- (id) collectionView:(id)arg1 contextMenuConfigurationForItemAtIndexPath:(id)arg2 point:(struct CGPoint)arg3; (0x10370bd7c)
		- (id) collectionView:(id)arg1 tableLayout:(id)arg2 leadingSwipeActionsConfigurationForRowAtIndexPath:(id)arg3; (0x10370be64)
		- (id) collectionView:(id)arg1 tableLayout:(id)arg2 trailingSwipeActionsConfigurationForRowAtIndexPath:(id)arg3; (0x10370bf68)
		- (void) collectionView:(id)arg1 willEndContextMenuInteractionWithConfiguration:(id)arg2 animator:(id)arg3; (0x10370c3c0)
		- (void) collectionView:(id)arg1 willDisplayCell:(id)arg2 forItemAtIndexPath:(id)arg3; (0x10370c8c8)
		- (BOOL) collectionView:(id)arg1 shouldSelectItemAtIndexPath:(id)arg2; (0x10370c9cc)
		- (void) collectionView:(id)arg1 didSelectItemAtIndexPath:(id)arg2; (0x10370d234)
		- (double) collectionView:(id)arg1 heightForHeaderViewInTableLayout:(id)arg2; (0x10370d314)
		- (id) initWithCollectionViewLayout:(id)arg1; (0x10370e724)
		- (id) initWithNibName:(id)arg1 bundle:(id)arg2; (0x10370e758)
		- (void) .cxx_destruct; (0x10370e798)

(lldb)

The leadingSwipeActionsConfigurationForRowAtIndexPath looks like it might be triggered when swiping to queue so I set a breakpoint here to see where it gets triggered. The breakpoint got triggered when I performed the action. This is before the play next and play last buttons are rendered in the view.

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 11.1
    frame #0: 0x000000010370be64 MusicApplication`___lldb_unnamed_symbol42258
MusicApplication`___lldb_unnamed_symbol42258:
->  0x10370be64 <+0>:  stp    x28, x27, [sp, #-0x60]!
    0x10370be68 <+4>:  stp    x26, x25, [sp, #0x10]
    0x10370be6c <+8>:  stp    x24, x23, [sp, #0x20]
    0x10370be70 <+12>: stp    x22, x21, [sp, #0x30]
Target 0: (Music) stopped.
(lldb) breakpoint list^C
(lldb) po $x0
<MusicApplication.SongsViewController: 0x1048d5c00>

(lldb) po po NSStringFromSelector($x1)^C
(lldb) po NSStringFromSelector($x1)
collectionView:tableLayout:leadingSwipeActionsConfigurationForRowAtIndexPath:

(lldb) po $x2
<UICollectionView: 0x1048ed600; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x2820ba6d0>; layer = <CALayer: 0x282e3a920>; contentOffset: {0, 5223.5}; contentSize: {375, 6398}; adjustedContentInset: {64, 0, 113, 0}; layout: <_TtCC16MusicApplication19SongsViewControllerP33_A45DAA2A24828EFCFB77323F0B26236B11TableLayout: 0x11e03dfa0>; dataSource: <MusicApplication.SongsViewController: 0x1048d5c00>>

(lldb) po $x3
<_TtCC16MusicApplication19SongsViewControllerP33_A45DAA2A24828EFCFB77323F0B26236B11TableLayout: 0x11e03dfa0>

(lldb) po $x4
<NSIndexPath: 0x94e2caae6068a274> {length = 2, path = 6 - 25}

This function is also documented on Apple’s website attableView:leadingSwipeActionsConfigurationForRowAtIndexPath:. NSIndexPath is list of indexes that together represent the path to a specific location in a tree of nested arrays. From the documentation we see the return value is a UISwipeActionsConfiguration We can also use lldb to see the return value of the function. I used di to disassemble the current function and set a breakpoint on the last line of the function. When the breakpoint got triggered this is just before function

(lldb) po $x0
<UISwipeActionsConfiguration: 0x281149140: actions=(
    "<UISwipeAction: 0x283bcaf40: style=0, title=Playing Next, backgroundColor=<UIDynamicSystemColor: 0x28053ed80; name = systemIndigoColor>, image=<UIImage:0x282d48ea0 symbol(com.apple.MusicApplication: play.next) {21.5, 16.5} baseline=2,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-1.5, 0, -2, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>",
    "<UISwipeAction: 0x283bca760: style=0, title=Playing Last, backgroundColor=<UIDynamicSystemColor: 0x28053f280; name = systemOrangeColor>, image=<UIImage:0x282d48120 symbol(com.apple.MusicApplication: play.last) {21.5, 16.5} baseline=2.5,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-2, 0, -1.5, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>"
), performsFirstActionWithFullSwipe=1>

I still need to understand the process of creating a UISwipeActionsConfiguration and figure out where the actual queing happens so I open this function in a disassember. The address for the app in Memory is always randomized because of ASLR. With lldb we can see exactly what memory address of the music binary this function is at

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000105857e64 MusicApplication`___lldb_unnamed_symbol42258
MusicApplication`___lldb_unnamed_symbol42258:
->  0x105857e64 <+0>:  stp    x28, x27, [sp, #-0x60]!
    0x105857e68 <+4>:  stp    x26, x25, [sp, #0x10]
    0x105857e6c <+8>:  stp    x24, x23, [sp, #0x20]
    0x105857e70 <+12>: stp    x22, x21, [sp, #0x30]
Target 0: (Music) stopped.
(lldb) image lookup -a 0x0000000105857e64
      Address: MusicApplication[0x0000000000367e64] (MusicApplication.__TEXT.__text + 3551108)
      Summary: MusicApplication`___lldb_unnamed_symbol42258

This means that this function exists at 0x0000000000367e64 in the Music binary. We can also disassemble the current function with di in lldb.

(lldb) di
MusicApplication`___lldb_unnamed_symbol42258:
->  0x105857e64 <+0>:   stp    x28, x27, [sp, #-0x60]!
    0x105857e68 <+4>:   stp    x26, x25, [sp, #0x10]
    0x105857e6c <+8>:   stp    x24, x23, [sp, #0x20]
    0x105857e70 <+12>:  stp    x22, x21, [sp, #0x30]
    0x105857e74 <+16>:  stp    x20, x19, [sp, #0x40]
    0x105857e78 <+20>:  stp    x29, x30, [sp, #0x50]
    0x105857e7c <+24>:  add    x29, sp, #0x50
    0x105857e80 <+28>:  mov    x19, x4
    0x105857e84 <+32>:  mov    x20, x3
    0x105857e88 <+36>:  mov    x21, x2
    0x105857e8c <+40>:  mov    x22, x0
    0x105857e90 <+44>:  mov    x0, #0x0
    0x105857e94 <+48>:  bl     0x105eb049c
    0x105857e98 <+52>:  mov    x23, x0
    0x105857e9c <+56>:  ldur   x27, [x0, #-0x8]
    0x105857ea0 <+60>:  ldr    x8, [x27, #0x40]
    0x105857ea4 <+64>:  mov    x9, x8
    0x105857ea8 <+68>:  adrp   x16, 2162
    0x105857eac <+72>:  ldr    x16, [x16, #0xff8]
    0x105857eb0 <+76>:  blr    x16
    0x105857eb4 <+80>:  mov    x9, sp
    0x105857eb8 <+84>:  add    x8, x8, #0xf
    0x105857ebc <+88>:  and    x8, x8, #0xfffffffffffffff0
    0x105857ec0 <+92>:  sub    x24, x9, x8
    0x105857ec4 <+96>:  mov    sp, x24
    0x105857ec8 <+100>: mov    x8, x24
    0x105857ecc <+104>: mov    x0, x19
    0x105857ed0 <+108>: bl     0x105eb043c
    0x105857ed4 <+112>: mov    x0, x22
    0x105857ed8 <+116>: bl     0x105eb38bc               ; symbol stub for: objc_retain
    0x105857edc <+120>: mov    x25, x0
    0x105857ee0 <+124>: mov    x0, x21
    0x105857ee4 <+128>: bl     0x105eb38bc               ; symbol stub for: objc_retain
    0x105857ee8 <+132>: mov    x21, x0
    0x105857eec <+136>: mov    x0, x20
    0x105857ef0 <+140>: bl     0x105eb38bc               ; symbol stub for: objc_retain
    0x105857ef4 <+144>: mov    x26, x0
    0x105857ef8 <+148>: mov    x0, x19
    0x105857efc <+152>: bl     0x105eb38bc               ; symbol stub for: objc_retain
    0x105857f00 <+156>: mov    x19, x0
    0x105857f04 <+160>: mov    x0, x24
    0x105857f08 <+164>: mov    x20, x22
    0x105857f0c <+168>: bl     0x10585cd34               ; ___lldb_unnamed_symbol42327
    0x105857f10 <+172>: mov    x20, x0
    0x105857f14 <+176>: ldr    x8, [x27, #0x8]
    0x105857f18 <+180>: mov    x0, x24
    0x105857f1c <+184>: mov    x1, x23
    0x105857f20 <+188>: blr    x8
    0x105857f24 <+192>: mov    x0, x21
    0x105857f28 <+196>: bl     0x105eb38b0               ; symbol stub for: objc_release
    0x105857f2c <+200>: mov    x0, x26
    0x105857f30 <+204>: bl     0x105eb38b0               ; symbol stub for: objc_release
    0x105857f34 <+208>: mov    x0, x25
    0x105857f38 <+212>: bl     0x105eb38b0               ; symbol stub for: objc_release
    0x105857f3c <+216>: mov    x0, x19
    0x105857f40 <+220>: bl     0x105eb38b0               ; symbol stub for: objc_release
    0x105857f44 <+224>: mov    x0, x20
    0x105857f48 <+228>: sub    sp, x29, #0x50
    0x105857f4c <+232>: ldp    x29, x30, [sp, #0x50]
    0x105857f50 <+236>: ldp    x20, x19, [sp, #0x40]
    0x105857f54 <+240>: ldp    x22, x21, [sp, #0x30]
    0x105857f58 <+244>: ldp    x24, x23, [sp, #0x20]
    0x105857f5c <+248>: ldp    x26, x25, [sp, #0x10]
    0x105857f60 <+252>: ldp    x28, x27, [sp], #0x60
    0x105857f64 <+256>: b      0x105eb37f0               ; symbol stub for: objc_autoreleaseReturnValue

I am curious about the function call to ___lldb_unnamed_symbol42327 so I set a breakpoint after it returns to check what is returned.

(lldb) b -a 0x105857f10
Breakpoint 16: where = MusicApplication`___lldb_unnamed_symbol42258 + 172, address = 0x0000000105857f10
(lldb) c
Process 38799 resuming
Process 38799 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 16.1
    frame #0: 0x0000000105857f10 MusicApplication`___lldb_unnamed_symbol42258 + 172
MusicApplication`___lldb_unnamed_symbol42258:
->  0x105857f10 <+172>: mov    x20, x0
    0x105857f14 <+176>: ldr    x8, [x27, #0x8]
    0x105857f18 <+180>: mov    x0, x24
    0x105857f1c <+184>: mov    x1, x23
Target 0: (Music) stopped.
(lldb) po $x0
<UISwipeActionsConfiguration: 0x28261d6e0: actions=(
    "<UISwipeAction: 0x280d37c00: style=0, title=Playing Next, backgroundColor=<UIDynamicSystemColor: 0x2833c6900; name = systemIndigoColor>, image=<UIImage:0x281baeeb0 symbol(com.apple.MusicApplication: play.next) {21.5, 16.5} baseline=2,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-1.5, 0, -2, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>",
    "<UISwipeAction: 0x280d34180: style=0, title=Playing Last, backgroundColor=<UIDynamicSystemColor: 0x2833c6e00; name = systemOrangeColor>, image=<UIImage:0x281bad3b0 symbol(com.apple.MusicApplication: play.last) {21.5, 16.5} baseline=2.5,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-2, 0, -1.5, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>"
), performsFirstActionWithFullSwipe=1>

Notice how this is the same thing that the original function is returning. I am guessing the original function is just a wrapper for ___lldb_unnamed_symbol42327 which does memory management. The pseudocode of setup looks like this

def leadingswipedaction():  # ___lldb_unnamed_symbol42258
    # malloc
    res = create_UISwipeActionsConfiguration() # ___lldb_unnamed_symbol42327
    # free memory
    return res

We still need to understand how the inner create_UISwipeActionsConfiguration call is being made. I was also able to find documentation for UISwipeActionsConfiguration. The documentation shows there is an init function which takes a list of actions. These actions look similar to the UISwipeAction however the documentation shows the type is of UIContextualAction. We can use lldb to check if our UISwipeAction has a super class

(lldb) po $x0
<UISwipeActionsConfiguration: 0x283b4ccf0: actions=(
    "<UISwipeAction: 0x281244180: style=0, title=Playing Next, backgroundColor=<UIDynamicSystemColor: 0x282d031c0; name = systemIndigoColor>, image=<UIImage:0x28056e910 symbol(com.apple.MusicApplication: play.next) {21.5, 16.5} baseline=2,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-1.5, 0, -2, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>",
    "<UISwipeAction: 0x281244d20: style=0, title=Playing Last, backgroundColor=<UIDynamicSystemColor: 0x282d036c0; name = systemOrangeColor>, image=<UIImage:0x28056f600 symbol(com.apple.MusicApplication: play.last) {21.5, 16.5} baseline=2.5,contentInsets={1, 0.5, 1, 1},alignmentRectInsets={-2, 0, -1.5, 0} config=<traits=(UserInterfaceIdiom = Phone, DisplayScale = 2, DisplayGamut = P3, HorizontalSizeClass = Compact, VerticalSizeClass = Regular, UserInterfaceStyle = Dark, UserInterfaceLayoutDirection = LTR, PreferredContentSizeCategory = L, AccessibilityContrast = Normal)> renderingMode=automatic>>"
), performsFirstActionWithFullSwipe=1>

(lldb) po [0x281244180 superclass]
UIContextualAction

The create_UISwipeActionsConfiguration is probably creating two UIContextualAction for the actions array. The documentation for UIContextualAction shows there is an init function which takes a handler as a parameter. the handler is the third argument.

ss—————————————–

MediaPlaybackCore`+[MPCMediaRemoteController _sendLocalCommand:playbackIntent:options:toPlayerPath:completion:]:
->  0x1b027dea4 <+0>:  sub    sp, sp, #0xb0
    0x1b027dea8 <+4>:  stp    x26, x25, [sp, #0x60]
    0x1b027deac <+8>:  stp    x24, x23, [sp, #0x70]
    0x1b027deb0 <+12>: stp    x22, x21, [sp, #0x80]
Target 0: (Music) stopped.
(lldb) po $x0
MPCMediaRemoteController

(lldb) po NSStringFromSelector($x1)
_sendLocalCommand:playbackIntent:options:toPlayerPath:completion:

(lldb) po $x2
125

(lldb) po $x3
<MPCPlaybackIntent: 0x281970280 source=3>

(lldb) po $x4
{
    MPCPlayerCommandRequestMediaRemoteOptionPlaybackIntent = "<MPCPlaybackIntent: 0x281970280 source=3>";
    kMRMediaRemoteOptionAssistantCommandSendTimestamp = "739667642.7544";
    kMRMediaRemoteOptionNowPlayingContentItemID = "XgeEllFNg\U2206wXe84XFkw";
    kMRMediaRemoteOptionPlaybackQueueInsertionPosition = 0;
    kMRMediaRemoteOptionRemoteControlInterfaceIdentifier = "Player.DataSource.PlaybackCommand";
}

<MPInsertIntoPlaybackQueueCommand: 0x281c59810 type=InsertIntoPlaybackQueue (125) enabled=YES handlers=[0x281c36760:_performCommandEvent:completion:]>

+[MPRemoteCommandEvent eventWithCommand:mediaRemoteType:options:]
-[MPRemoteCommandEvent initWithCommand:mediaRemoteType:options:]
Process 67952 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 15.1
    frame #0: 0x00000001b0138c40 MediaPlaybackCore`-[MPCPlayerRequest playingItemProperties]
MediaPlaybackCore`-[MPCPlayerRequest playingItemProperties]:
->  0x1b0138c40 <+0>:  adrp   x8, 295481
    0x1b0138c44 <+4>:  ldrsw  x8, [x8, #0xdd0]
    0x1b0138c48 <+8>:  ldr    x0, [x0, x8]
    0x1b0138c4c <+12>: ret
Target 0: (Music) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 15.1
  * frame #0: 0x00000001b0138c40 MediaPlaybackCore`-[MPCPlayerRequest playingItemProperties]
    frame #1: 0x00000001b0134588 MediaPlaybackCore`-[_MPCPlayerResponseTracklistDataSource itemAtIndexPath:] + 208
    frame #2: 0x00000001897f3530 MediaPlayer`-[MPLazySectionedCollection itemAtIndexPath:] + 236
    frame #3: 0x00000001b01385e0 MediaPlaybackCore`-[MPCPlayerResponseTracklist playingItem] + 32
    frame #4: 0x00000001b0235c10 MediaPlaybackCore`-[_MPCPlayerInsertItemsCommand insertAfterPlayingItemWithPlaybackIntent:] + 808
    frame #5: 0x000000010259ae00 MusicApplication`___lldb_unnamed_symbol66873 + 132
    frame #6: 0x0000000102599d18 MusicApplication`___lldb_unnamed_symbol66821 + 304
    frame #7: 0x000000010258ab00 MusicApplication`___lldb_unnamed_symbol66530
    frame #8: 0x0000000102588d18 MusicApplication`___lldb_unnamed_symbol66519
    frame #9: 0x0000000101e141dc MusicApplication`___lldb_unnamed_symbol25420
    frame #10: 0x0000000102430b28 MusicApplication`___lldb_unnamed_symbol57987
    frame #11: 0x0000000102438684 MusicApplication`___lldb_unnamed_symbol58139
    frame #12: 0x0000000101e4dbf4 MusicApplication`___lldb_unnamed_symbol26505
    frame #13: 0x0000000102438660 MusicApplication`___lldb_unnamed_symbol58130
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
    frame #0: 0x00000001b012c5b4 MediaPlaybackCore`-[MPCPlayerCommandRequest initWithMediaRemoteCommand:options:controller:label:]
MediaPlaybackCore`-[MPCPlayerCommandRequest initWithMediaRemoteCommand:options:controller:label:]:
->  0x1b012c5b4 <+0>:  stp    x26, x25, [sp, #-0x50]!
    0x1b012c5b8 <+4>:  stp    x24, x23, [sp, #0x10]
    0x1b012c5bc <+8>:  stp    x22, x21, [sp, #0x20]
    0x1b012c5c0 <+12>: stp    x20, x19, [sp, #0x30]
Target 0: (Music) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
  * frame #0: 0x00000001b012c5b4 MediaPlaybackCore`-[MPCPlayerCommandRequest initWithMediaRemoteCommand:options:controller:label:]
    frame #1: 0x00000001b02361e0 MediaPlaybackCore`-[_MPCPlayerInsertItemsCommand _insertWithOptions:] + 392
    frame #2: 0x00000001b0235c84 MediaPlaybackCore`-[_MPCPlayerInsertItemsCommand insertAfterPlayingItemWithPlaybackIntent:] + 924
    frame #3: 0x00000001061dee00 MusicApplication`___lldb_unnamed_symbol66873 + 132
    frame #4: 0x00000001061ddd18 MusicApplication`___lldb_unnamed_symbol66821 + 304
    frame #5: 0x00000001061ceb00 MusicApplication`___lldb_unnamed_symbol66530
    frame #6: 0x00000001061ccd18 MusicApplication`___lldb_unnamed_symbol66519
    frame #7: 0x0000000105a581dc MusicApplication`___lldb_unnamed_symbol25420
    frame #8: 0x0000000106074b28 MusicApplication`___lldb_unnamed_symbol57987
    frame #9: 0x000000010607c684 MusicApplication`___lldb_unnamed_symbol58139
    frame #10: 0x0000000105a91bf4 MusicApplication`___lldb_unnamed_symbol26505
    frame #11: 0x000000010607c660 MusicApplication`___lldb_unnamed_symbol58130
(lldb) po $x0
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.1
    frame #0: 0x00000001b01e0e20 MediaPlaybackCore`-[MPCPlayerChangeRequest initWithCommandRequests:]
MediaPlaybackCore`-[MPCPlayerChangeRequest initWithCommandRequests:]:
->  0x1b01e0e20 <+0>:  sub    sp, sp, #0x30
    0x1b01e0e24 <+4>:  stp    x20, x19, [sp, #0x10]
    0x1b01e0e28 <+8>:  stp    x29, x30, [sp, #0x20]
    0x1b01e0e2c <+12>: add    x29, sp, #0x20
Target 0: (Music) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.1
  * frame #0: 0x00000001b01e0e20 MediaPlaybackCore`-[MPCPlayerChangeRequest initWithCommandRequests:]
    frame #1: 0x00000001025a1e98 MusicApplication`___lldb_unnamed_symbol66821 + 688
    frame #2: 0x0000000102592b00 MusicApplication`___lldb_unnamed_symbol66530
    frame #3: 0x0000000102590d18 MusicApplication`___lldb_unnamed_symbol66519
    frame #4: 0x0000000101e1c1dc MusicApplication`___lldb_unnamed_symbol25420
    frame #5: 0x0000000102438b28 MusicApplication`___lldb_unnamed_symbol57987
    frame #6: 0x0000000102440684 MusicApplication`___lldb_unnamed_symbol58139
    frame #7: 0x0000000101e55bf4 MusicApplication`___lldb_unnamed_symbol26505
    frame #8: 0x0000000102440660 MusicApplication`___lldb_unnamed_symbol58130
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
    frame #0: 0x000000018262eb04 Foundation`-[NSOperationQueue addOperation:]
Foundation`-[NSOperationQueue addOperation:]:
->  0x18262eb04 <+0>: mov    x3, #0x0
    0x18262eb08 <+4>: mov    w4, #0x0
    0x18262eb0c <+8>: b      0x182591a14               ; __addOperations

Foundation`:
    0x18262eb10 <+0>: stp    x20, x19, [sp, #-0x20]!
Target 0: (Music) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
  * frame #0: 0x000000018262eb04 Foundation`-[NSOperationQueue addOperation:]
    frame #1: 0x00000001b01e1740 MediaPlaybackCore`-[MPCPlayerChangeRequest performWithExtendedStatusCompletion:] + 1592
    frame #2: 0x00000001061ddf68 MusicApplication`___lldb_unnamed_symbol66821 + 896
    frame #3: 0x00000001061ceb00 MusicApplication`___lldb_unnamed_symbol66530
    frame #4: 0x00000001061ccd18 MusicApplication`___lldb_unnamed_symbol66519
    frame #5: 0x0000000105a581dc MusicApplication`___lldb_unnamed_symbol25420
    frame #6: 0x0000000106074b28 MusicApplication`___lldb_unnamed_symbol57987
    frame #7: 0x000000010607c684 MusicApplication`___lldb_unnamed_symbol58139
    frame #8: 0x0000000105a91bf4 MusicApplication`___lldb_unnamed_symbol26505
    frame #9: 0x000000010607c660 MusicApplication`___lldb_unnamed_symbol58130
(lldb)
(lldb) bt
* thread #36, queue = 'NSOperationQueue 0x113fb2cb0 (QOS: USER_INITIATED)', stop reason = breakpoint 4.1
  * frame #0: 0x00000001b027dea4 MediaPlaybackCore`+[MPCMediaRemoteController _sendLocalCommand:playbackIntent:options:toPlayerPath:completion:]
    frame #1: 0x00000001b027db0c MediaPlaybackCore`+[MPCMediaRemoteController sendCommand:options:toPlayerPath:completion:] + 224
    frame #2: 0x00000001b027a264 MediaPlaybackCore`-[MPCMediaRemoteController sendCommand:options:completion:] + 448
    frame #3: 0x00000001b01e21a8 MediaPlaybackCore`-[MPCMediaRemoteCommandOperation execute] + 1860
    frame #4: 0x000000018982c730 MediaPlayer`-[MPAsyncOperation start] + 148
    frame #5: 0x00000001825ba498 Foundation`__NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 20
    frame #6: 0x00000001825c7ccc Foundation`__NSOQSchedule_f + 180
    frame #7: 0x0000000180bab094 libdispatch.dylib`_dispatch_call_block_and_release + 24
    frame #8: 0x0000000180bac094 libdispatch.dylib`_dispatch_client_callout + 16
    frame #9: 0x0000000180b4ebb8 libdispatch.dylib`_dispatch_continuation_pop$VARIANT$mp + 440
    frame #10: 0x0000000180b4e2e0 libdispatch.dylib`_dispatch_async_redirect_invoke + 588
    frame #11: 0x0000000180b5bb94 libdispatch.dylib`_dispatch_root_queue_drain + 340
    frame #12: 0x0000000180b5c39c libdispatch.dylib`_dispatch_worker_thread2 + 172
    frame #13: 0x00000001dc250dc4 libsystem_pthread.dylib`_pthread_wqthread + 224
Process 56769 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.1
    frame #0: 0x0000000189905094 MediaPlayer`+[MPRemoteCommandEvent eventWithCommand:mediaRemoteType:options:]
MediaPlayer`+[MPRemoteCommandEvent eventWithCommand:mediaRemoteType:options:]:
->  0x189905094 <+0>:  stp    x22, x21, [sp, #-0x30]!
    0x189905098 <+4>:  stp    x20, x19, [sp, #0x10]
    0x18990509c <+8>:  stp    x29, x30, [sp, #0x20]
    0x1899050a0 <+12>: add    x29, sp, #0x20
Target 0: (Music) stopped.
(lldb) po $x0
MPRemoteCommandEvent

(lldb) po NSStringFromSelector($x1)
eventWithCommand:mediaRemoteType:options:

(lldb) po $x3
125

(lldb) po $x4
{
    MPCPlayerCommandRequestMediaRemoteOptionPlaybackContext = "<MPCModelPlaybackContext:0x283dd0e40 actionAfterQueueLoad=Play playbackRequestEnvironment=<MPCPlaybackRequestEnvironment: 0x28127d440 identity=<ICUserIdentity 0x280485ce0: [Active Account: <ef30d873b760aab549b318eb2bd03209120026a5>]>> queueEndAction=UserDefault[Reset] repeat/shuffle=UserDefault[Off]/UserDefault[Off] request=<MPModelLibraryRequest: 0x2839c9c20 label=\U201cSong: Optional(\"CHIHIRO\")\U201c itemKind=\U201csongs\U201d allowedItemIdentifiers=[(databaseID=\"8ABAD0A8-598A-49E1-AEB9-3C90AFF32057\" personID=\"25116999996\" persistentID=3325340846184242708 storeCloudAlbumID=\"l.SKGOG9t\" storeCloudID=182889506 cloudUniversalLibraryID=\"i.mmpe6Q9iLDeXolW\" storeSubscriptionAdamID=1739659141 reportingAdamID=1739659141 assetAdamID=1739659141 kind=songs)]> startItemIdentifiers=(databaseID=\"8ABAD0A8-598A-49E1-AEB9-3C90AFF32057\" personID=\"25116999996\" persistentID=3325340846184242708 storeCloudAlbumID=\"l.SKGOG9t\" storeCloudID=182889506 cloudUniversalLibraryID=\"i.mmpe6Q9iLDeXolW\" storeSubscriptionAdamID=1739659141 reportingAdamID=1739659141 assetAdamID=1739659141 kind=songs)>";
    kMRMediaRemoteOptionAssistantCommandSendTimestamp = "739698003.02045";
    kMRMediaRemoteOptionCommandID = "63A1B5E2-0D0E-497E-BCC9-89247B1B20CA";
    kMRMediaRemoteOptionNowPlayingContentItemID = "g8SkwnkEk\U2206w8XHXXS6n";
    kMRMediaRemoteOptionPlaybackQueueInsertionPosition = 0;
    kMRMediaRemoteOptionRemoteControlInterfaceIdentifier = "Player.DataSource.PlaybackCommand";
    kMRMediaRemoteOptionSendOptionsNumber = 0;
    kMRMediaRemoteOptionSenderID = "SenderDevice = <Rahul\U2019s Developer iPhone>, SenderBundleIdentifier = <com.apple.Music>, SenderPID = <56769>";
    kMRMediaRemoteOptionSystemAppPlaybackQueueData = {length = 143, bytes = 0x62706c69 73743030 d5010203 04050607 ... 00000000 00000064 };
}

(lldb) expr -l objc++ -O -- [$x4 setValue:@(2) forKey:@"kMRMediaRemoteOptionPlaybackQueueInsertionPosition"]
0

(lldb) c
Process 56769 resuming

MediaPlaybackCore

-[MPCQueueController moveContentItemID:afterContentItemID:completion:]

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 13.4
  * frame #0: 0x00000001b0239260 MediaPlaybackCore`-[_MPCPlayerReorderItemsCommand moveItem:afterItem:]
    frame #1: 0x0000000101ecccf4 MusicApplication`___lldb_unnamed_symbol29077 + 1236
    frame #2: 0x0000000101ebfff0 MusicApplication`___lldb_unnamed_symbol28973 + 296
    frame #3: 0x00000001839d02b8 UIKitCore`-[UICollectionView _notifyDataSourceForMoveOfItemFromIndexPath:toIndexPath:] + 276
    frame #4: 0x00000001839cf3bc UIKitCore`-[UICollectionView _completeInteractiveMovementWithDisposition:completion:] + 1332
    frame #5: 0x0000000101ebee80 MusicApplication`___lldb_unnamed_symbol28954 + 184
    frame #6: 0x0000000101ebf020 MusicApplication`___lldb_unnamed_symbol28955 + 28
    frame #7: 0x00000001839a5604 UIKitCore`-[UICollectionViewTableCell _endReorderingForCell:wasCancelled:animated:] + 100
    frame #8: 0x000000018414113c UIKitCore`-[UITableViewCell _grabberReleased:] + 64
    frame #9: 0x0000000184147558 UIKitCore`-[UITableViewCellReorderControl endTrackingWithTouch:withEvent:] + 68
    frame #10: 0x00000001837f147c UIKitCore`-[UIControl touchesEnded:withEvent:] + 448
    frame #11: 0x00000001832f717c UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1228
    frame #12: 0x0000000183326c9c UIKitCore`-[UIWindow sendEvent:] + 4372
    frame #13: 0x000000010228bd08 MusicApplication`___lldb_unnamed_symbol49000 + 84
    frame #14: 0x00000001834c7ab4 UIKitCore`-[UIApplication sendEvent:] + 892
    frame #15: 0x00000001832fbc7c UIKitCore`__dispatchPreprocessedEventFromEventQueue + 8148
    frame #16: 0x00000001832f0b40 UIKitCore`__processEventQueue + 6544
    frame #17: 0x00000001832f5f5c UIKitCore`__eventFetcherSourceCallback + 168
    frame #18: 0x0000000180f0c468 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
    frame #19: 0x0000000180f1c598 CoreFoundation`__CFRunLoopDoSource0 + 204
    frame #20: 0x0000000180e5e774 CoreFoundation`__CFRunLoopDoSources0 + 256
    frame #21: 0x0000000180e63e48 CoreFoundation`__CFRunLoopRun + 768
    frame #22: 0x0000000180e77194 CoreFoundation`CFRunLoopRunSpecific + 572
    frame #23: 0x00000001a19a2988 GraphicsServices`GSEventRunModal + 160
    frame #24: 0x000000018367aa88 UIKitCore`-[UIApplication _run] + 1080
    frame #25: 0x0000000183413fc8 UIKitCore`UIApplicationMain + 336
    frame #26: 0x0000000101e144b0 MusicApplication`main + 68
    frame #27: 0x0000000100dac4d0 dyld`start + 444
(lldb)

ss—————————————–

This shows where in memory these binary functions are loaded. These functions most likely don’t get loaded until they are opened which is why we see errors if we try running this without loading

(lldb) b -a 0x101ca01a8
Breakpoint 1: where = MusicApplication`___lldb_unnamed_symbol29133, address = 0x0000000101ca01a8
(lldb) con
Process 20285 resuming
Process 20285 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000101ca01a8 MusicApplication`___lldb_unnamed_symbol29133
MusicApplication`___lldb_unnamed_symbol29133:
->  0x101ca01a8 <+0>:  stp    x28, x27, [sp, #-0x60]!
    0x101ca01ac <+4>:  stp    x26, x25, [sp, #0x10]
    0x101ca01b0 <+8>:  stp    x24, x23, [sp, #0x20]
    0x101ca01b4 <+12>: stp    x22, x21, [sp, #0x30]
Target 0: (Music) stopped.
(lldb) po $x0
<MusicApplication.NowPlayingQueueViewController: 0x101165400>
(lldb) po $x1
6894563425
(lldb) po NSStringFromSelector($x1)
collectionView:targetIndexPathForMoveFromItemAtIndexPath:toProposedIndexPath:

To get stopped in this debugger I was moving the song in the second position to first position. Let’s take a look at the variables that were passed. There are 3 variables herearg1 targetIndexPathForMoveFromItemAtIndexPath:(id)arg2 toProposedIndexPath:(id)arg3 Since $arg1 and $arg2 are the implicit arguments lets look at $arg3

(lldb) po $arg3
<_TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView: 0x101404000; baseClass = UICollectionView; frame = (0 0; 375 539); autoresize = W+H; gestureRecognizers = <NSArray: 0x28023ee80>; layer = <CALayer: 0x280faa480>; contentOffset: {0, 1414}; contentSize: {375, 1744}; adjustedContentInset: {0, 0, 209, 0}; layout: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE619CompositionalLayout: 0x122dadb20>; dataSource: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource: 0x28023fae0>>

(lldb) po [@"" __methodDescriptionForClass:[_TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView class]]

in _TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView:
	Instance Methods:
		- (BOOL) beginInteractiveMovementForItemAtIndexPath:(id)arg1; (0x101c8ad08)
		- (void) endInteractiveMovement; (0x101c8b004)
		- (void) cancelInteractiveMovement; (0x101c8b0ac)
		- (id) initWithFrame:(struct CGRect)arg1 collectionViewLayout:(id)arg2; (0x101c8b0d8)
		- (id) initWithCoder:(id)arg1; (0x101c8b1ac)
		- (void) .cxx_destruct; (0x101c8b204)

(lldb)

I set a breakpoint at beginInteractiveMovementForItemAtIndexPath and continued execution. This breakpoint did not get called. However after reordering the queue again I saw breakpoint beginInteractiveMovementForItemAtIndexPath is called before any calls to targetIndexPathForMoveFromItemAtIndexPath are made. We know the structure looks something like this

beginInteractiveMovementForItemAtIndexPath(arg1)
targetIndexPathForMoveFromItemAtIndexPath(arg1, arg2, arg3)
targetIndexPathForMoveFromItemAtIndexPath(arg1, arg2, arg3)
targetIndexPathForMoveFromItemAtIndexPath(arg1, arg2, arg3)

I’ve added targetIndexPathForMoveFromItemAtIndexPath 3 times since it gets called multiple times everytime I reorder. I stopped at the beginInteractiveMovementForItemAtIndexPath Let’s see what the arguments to this function are

(lldb) po $x0
<_TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView: 0x1033d6600; baseClass = UICollectionView; frame = (0 0; 375 539); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x28022ad90>; layer = <CALayer: 0x280fd0c20>; contentOffset: {0, 1414}; contentSize: {375, 1912}; adjustedContentInset: {0, 0, 196, 0}; layout: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE619CompositionalLayout: 0x123165f60>; dataSource: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource: 0x280228780>>

(lldb) po NSStringFromSelector($x1)
beginInteractiveMovementForItemAtIndexPath:

(lldb) po $x2
<NSIndexPath: 0x8108f24b6bc48251> {length = 2, path = 1 - 1}

I wanted to see how the index paths changed as I changed the queue. The $x2 above is when 2nd song in queue moves to 1st.. This time I moved 3rd -> 1st and then 1st -> 3rd. This was the result

// 3rd -> 1st
<NSIndexPath: 0x8108f24b68c48251> {length = 2, path = 1 - 2}
// 4th -> 1st
<NSIndexPath: 0x8108f24b69c48251> {length = 2, path = 1 - 3}
// 5th -> 1st
<NSIndexPath: 0x8108f24b6ec48251> {length = 2, path = 1 - 4}

// 5th -> 2nd
<NSIndexPath: 0x8108f24b6ec48251> {length = 2, path = 1 - 4}
// 5th -> 3rd
<NSIndexPath: 0x8108f24b6ec48251> {length = 2, path = 1 - 4}

// 1st -> 4th
<NSIndexPath: 0x8108f24b6ac48251> {length = 2, path = 1 - 0}

First number is always 1 second number is the index of the song we moved. Next I put a breakpoint on the datasource itself

The ViewController is of type UICollectionView. Asking for the delegate of the _TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView shows the following

(lldb) po $arg3
<_TtCC16MusicApplication29NowPlayingQueueViewController14CollectionView: 0x1034a6a00; baseClass = UICollectionView; frame = (0 0; 375 539); autoresize = W+H; gestureRecognizers = <NSArray: 0x280318180>; layer = <CALayer: 0x2808aa1a0>; contentOffset: {0, 1750}; contentSize: {375, 2360}; adjustedContentInset: {0, 0, 0, 0}; layout: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE619CompositionalLayout: 0x124e72d40>; dataSource: <_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource: 0x2803539c0>>

(lldb) po [$arg3 delegate]
<MusicApplication.NowPlayingQueueViewController: 0x103529c00>
(lldb) po [@"" __methodDescriptionForClass:(Class)NSClassFromString(@"MusicApplication.NowPlayingQueueViewController")]

in MusicApplication.NowPlayingQueueViewController:
	Instance Methods:
		- (void) collectionView:(id)arg1 willDisplayContextMenuWithConfiguration:(id)arg2 animator:(id)arg3; (0x105641620)
		- (void) collectionView:(id)arg1 willEndContextMenuInteractionWithConfiguration:(id)arg2 animator:(id)arg3; (0x105641638)
		- (void) collectionView:(id)arg1 willDisplayCell:(id)arg2 forItemAtIndexPath:(id)arg3; (0x10563f690)
		- (void) collectionView:(id)arg1 willDisplaySupplementaryView:(id)arg2 forElementKind:(id)arg3 atIndexPath:(id)arg4; (0x10563f794)
		- (struct CGPoint) collectionView:(id)arg1 targetContentOffsetForProposedContentOffset:(struct CGPoint)arg2; (0x10563fa28)
		- (id) collectionView:(id)arg1 targetIndexPathForMoveFromItemAtIndexPath:(id)arg2 toProposedIndexPath:(id)arg3; (0x1056401a8)
		- (BOOL) collectionView:(id)arg1 shouldSelectItemAtIndexPath:(id)arg2; (0x1056404e8)
		- (void) collectionView:(id)arg1 didSelectItemAtIndexPath:(id)arg2; (0x105640fbc)
		- (void) collectionView:(id)arg1 willPerformPreviewActionForMenuWithConfiguration:(id)arg2 animator:(id)arg3; (0x10564109c)
		- (id) collectionView:(id)arg1 contextMenuConfigurationForItemAtIndexPath:(id)arg2 point:(struct CGPoint)arg3; (0x105641314)
		- (id) collectionView:(id)arg1 previewForHighlightingContextMenuWithConfiguration:(id)arg2; (0x105641414)
		- (id) collectionView:(id)arg1 previewForDismissingContextMenuWithConfiguration:(id)arg2; (0x105641490)
		- (id) _collectionView:(id)arg1 indexPathOfReferenceItemToPreserveContentOffsetWithProposedReference:(id)arg2; (0x1056414b4)
		- (void) configureCell:(id)arg1 forSong:(id)arg2; (0x10563eef0)
		- (void) configureCell:(id)arg1 forTVEpisode:(id)arg2; (0x10563eefc)
		- (void) configureCell:(id)arg1 forMovie:(id)arg2; (0x10563ef08)
		- (void) dealloc; (0x10562dd38)
		- (id) initWithCoder:(id)arg1; (0x10562decc)
		- (void) viewDidLoad; (0x105630e08)
		- (void) viewDidAppear:(BOOL)arg1; (0x105630e34)
		- (void) traitCollectionDidChange:(id)arg1; (0x105631ab4)
		- (void) viewLayoutMarginsDidChange; (0x105631f08)
		- (void) viewSafeAreaInsetsDidChange; (0x105631f34)
		- (void) scrollViewDidScroll:(id)arg1; (0x105632320)
		- (void) scrollViewWillEndDragging:(id)arg1 withVelocity:(struct CGPoint)arg2 targetContentOffset:(struct CGPoint*)arg3; (0x105632370)
		- (id) initWithNibName:(id)arg1 bundle:(id)arg2; (0x10563cedc)
		- (void) .cxx_destruct; (0x10562dd5c)

Next I inspected the DataSource

(lldb) po [@"" __methodDescriptionForClass:[_TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource class]]

in _TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource:
	Instance Methods:
		- (BOOL) collectionView:(id)arg1 canMoveItemAtIndexPath:(id)arg2; (0x101c8bce0)
		- (void) collectionView:(id)arg1 moveItemAtIndexPath:(id)arg2 toIndexPath:(id)arg3; (0x101c8bec8)
		- (void) .cxx_destruct; (0x101c8c0f0)

I put another breakpoint at moveItemAtIndexPath in _TtCC16MusicApplication29NowPlayingQueueViewControllerP33_7C0FB455470DF139DBFE3B6FE6F50CE610DataSource and saw that this method gets called before

image lookup -a 0x00000001034a3ec8