Posts

Post not yet marked as solved
4 Replies
Hi, Same problem here with an 3 years old iOS project I've been working on for 3 days with a MacBook Pro M2 : Building for iOS Simulator, but linking in dylib built for iOS, file ... for architecture arm64 If I understood well, it is because the CocoaPod library isn't compatible with the ARM processor. I tried to upgrade the library from CocoaPod but same problem, CocoaPod isn't compatible with ARM processor, I need to upgrade Ruby and so on... But all the projects libraries exist as a Git repo (I have to check) so if I migrate from CocoaPods to Swift Package Manager (which what it will have to be done anyway) as described in many articles, it will be fine right ? Thx.
Post marked as solved
8 Replies
After a while being stucked with this problem, I finally found the solution in 5 minutes : When a NSSearchField is cleared, 2 functions are called : controlTextDidChange and searchFieldDidEndSearching. So I just add a control to check if the filter string count is equal to 0 to reset the filter : private func _filterTracks(str: String) { if str.count == 0 { resetFilter() return } var thePredicates = [NSPredicate]() let theArtistPredicate = NSPredicate(format: FilterType.format, FilterType.artistField, str) let theTitlePredicate = NSPredicate(format: FilterType.format, FilterType.titleField, str) let theAlbumPredicate = NSPredicate(format: FilterType.format, FilterType.albumField, str) if(_currentFilter?.tag == FilterType.FilterListType.ALL.rawValue){ thePredicates.append(theArtistPredicate) thePredicates.append(theTitlePredicate) thePredicates.append(theAlbumPredicate) }else if(_currentFilter?.tag == FilterType.FilterListType.artist.rawValue){ thePredicates.append(theArtistPredicate) }else if(_currentFilter?.tag == FilterType.FilterListType.album.rawValue){ thePredicates.append(theAlbumPredicate) }else if(_currentFilter?.tag == FilterType.FilterListType.title.rawValue){ thePredicates.append(theTitlePredicate) } let theCompoundPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: thePredicates) arrayController.filterPredicate = theCompoundPredicate print("V&G_Project____filterTracks : ", arrayController.filterPredicate?.predicateFormat) } func resetFilter() { searchField.stringValue = "" // in case the func is called from another func arrayController.filterPredicate = nil } Et voila... So that's right it is arrayController.filterPredicate = nil to reset the predicate. Thx.
Post not yet marked as solved
3 Replies
Yes I know about iTunes Library Framework because I spoke about it with the ITLibrary. But like I said, we can't control the Music / iTunes app, only get its infos : My question is about how to play a track in the Music / iTunes app from another app ? Thx.
Post not yet marked as solved
10 Replies
Hi, No answer but for those like me who have this problem, I solved it. As I don't need to show the tracks in the NSOutlineView : I add directly the ITTracks to an array in the album : for (albumID, tracksAlbum) in theArtistAlbums { let theTracksAlbum = tracksAlbum.map { $0 }.sorted(by: { $0.trackNumber < $1.trackNumber }) let theITAlbum = theTracksAlbum.first?.album let theAlbum = Album(theITAlbum: theITAlbum!) theAlbum.ITTracks = theTracksAlbum // tracks added here, don't need to build a NSObject in the loop like before... theArtist.albums.append(theAlbum) } The powerful way here is to use the map of tracksAlbum.map. But for my info, is it possible to build objects in this kind of function instead of doing a loop like I tried to do which is very slow ? Thx.
Post not yet marked as solved
10 Replies
Hi, Another problem here is when I get all the tracks artist's albums, the function is slow : for track in theTracksAlbum { print(track.title) let theTrack = Track(theITTrack: track) // a NSObject to show it in a NSOutlineView theAlbum.tracks.append(theTrack) } If I comment those lines, the tree appears instantly. Problem : I need the albums tracks to get the artwork's album (right ?) and especially to add them in a playlist. So each time I click on an artist, I could execute a function to retrieve all the tracks albums, it is not so difficult. But before to do that, I'd like to know if I could enhance something to get the tracks ? Each time I do a loop to build objects, the program goes slow... I think it is possible because when I click on the Artists / All artists in the Music app, it is fast : Thx.
Post not yet marked as solved
10 Replies
I tried : let theTracksByArtist = Dictionary(grouping: theITTracks, by: { $0.artist?.name ?? "--" } ).sorted(by: { $0.key.localizedStandardCompare($1.key) == .orderedAscending }) And it is working, thx ! What is "--" ? Special code or can I type "" ?
Post not yet marked as solved
10 Replies
Hi, So thanks to Eskimo and his group by trick (very useful), I just finished a pretty good version of my artists - albums - tracks tree. Here is my playground : import Cocoa import iTunesLibrary do { let lib = try ITLibrary(apiVersion: "1.1") let theITTracks = lib.allMediaItems.filter({$0.mediaKind == .kindSong}) let theTracksByArtist = Dictionary(grouping: theITTracks, by: { $0.artist?.name } ) var theArtistsTree = [Artist]() for(artistKey, tracksByArtist) in theTracksByArtist { print("artistKey >>>", artistKey) print("/////////") let theAlbumsTracks = tracksByArtist.map{ $0 } let theITArtist = theAlbumsTracks.first?.artist let theArtist = Artist(theITArtist: theITArtist!) let theArtistAlbums = Dictionary(grouping: theAlbumsTracks, by: { $0.album.persistentID } ) for (albumID, tracksAlbum) in theArtistAlbums { let theTracksAlbum = tracksAlbum.map { $0 } let theITAlbum = theTracksAlbum.first?.album let theAlbum = Album(theITAlbum: theITAlbum!) print(theTracksAlbum.first?.album.title) print("++++") for track in theTracksAlbum { print(track.title) let theTrack = Track(theITTrack: track) theAlbum.tracks.append(theTrack) } print("***************") theArtist.albums.append(theAlbum) } theArtistsTree.append(theArtist) print("---------------------------------------------------------------------------------------------------") } } catch let error { print(error) } But I have a bunch of problems to solve like the sort of the dictionary grouped by artist. I tried : let theTracksByArtist = Dictionary(grouping: theITTracks, by: { ($0.artist?.name)! } ).sorted(by: { $0.key.localizedStandardCompare($1.key) == .orderedAscending }) It is working until the moment an artist nil is parsed : Multiline error: Execution was interrupted, reason: EXC_BREAKPOINT (code=1, subcode=0x1006c2ad8). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation. So how to avoid this error ? Do I need to test the artist?.name before to compare it ? How to ? Thx.
Post not yet marked as solved
10 Replies
Hi, In fact, I'd like to reproduce what we have in the Music app : The Eskimo's way with the Dictionary seams to be great but I need to have the Artist >>> Albums >>> Tracks schema. Thx for your help.
Post not yet marked as solved
10 Replies
2 years ago, I'm back on this post ! I think my approach is too complicated even if it is working... Like Eskimo said, Dictionary seems to be the key but I'm not familiar with that to manipulate arrays... Do I need to do multiple dictionary of can I have all my structure in only one ? Thx.
Post marked as solved
8 Replies
Yes, searchFieldDidEndSearching is called each time the NSSearchField is cleared. It is working. And _filterTracks is called each time a char is typed in the NSSearchField in the controlTextDidChange method : func controlTextDidChange(_ obj: Notification) {         let theTextField = obj.object as! NSTextField         let theStr = theTextField.stringValue         _filterTracks(str: theStr)     } So each time a char is typed, the predicate is set to the NSArrayController. And after that, I "just" need to find how to clear the predicate once the NSSearchField is cleared, like we have in iTunes / Music app... Thx.
Post marked as solved
8 Replies
I've already read this post. It helped me to understand the multiple predicates but it is not about how to reset it because it is done automatically by the NSSearchField. Also, it is done in the Xcode Storyboard. Me, I'd like to do it programmatically. I just have to know what to call in the searchFieldDidEndSearching NSSearchField method... Do you know ? Thx.
Post not yet marked as solved
1 Replies
I simply took this code : class PredicateOutlineNode: NSObject { typealias Element = PredicateOutlineNode @objc dynamic var children: [Element] = [] { didSet { propagatePredicatesAndRefilterChildren() } } @objc private(set) dynamic var filteredChildren: [Element] = [] { didSet { count = filteredChildren.count isLeaf = filteredChildren.isEmpty } } @objc private(set) dynamic var count: Int = 0 @objc private(set) dynamic var isLeaf: Bool = true var predicate: NSPredicate? { didSet { propagatePredicatesAndRefilterChildren() } } private func propagatePredicatesAndRefilterChildren() { // Propagate the predicate down the child nodes in case either // the predicate or the children array changed. children.forEach { $0.predicate = predicate } // Determine the matching leaf nodes. let newChildren: [Element] if let predicate = predicate { newChildren = children.compactMap { child -> Element? in if child.isLeaf, !predicate.evaluate(with: child) { return nil } return child } } else { newChildren = children } // Only actually update the children if the count varies. if newChildren.count != filteredChildren.count { filteredChildren = newChildren } } } Thx.
Post marked as solved
8 Replies
I tried to use the NSSearchField bind method with my function to change the filter and it is working. So something is done by the NSSearchField bind method to reset the predicate and get the NSArrayController datas like it was before the predicate is executed when the reset button is pressed. How to do that programmatically ?
Post marked as solved
8 Replies
Thx for you answer. My question is not about how to test if the filter must be reset but how to reset it. To know when the filter must be reset, I use the NSSearchField searchFieldDidEndSearching method. I tried to set the predicate to nil but it doesn't work : func searchFieldDidEndSearching(_ sender: NSSearchField) {         print("V&G_Project___TrackListView searchFieldDidEndSearching : ", self)         arrayController.filterPredicate = nil     } The _filter var is used to change the predicate because I can change the filter type : At first I tried with the NSSearchField bind method directly and it is working : searchField.bind(.predicate, to: arrayController, withKeyPath: NSBindingName.filterPredicate.rawValue, options: [.predicateFormat: "(title contains[cd] $value) OR (artist.name CONTAINS[cd] $value)"]) Problem : I didn't find how to change the NSPredicate one another filter is selected... Thx for your help.
Post not yet marked as solved
2 Replies
Hi, I'm still stuck on that. I tried to add another condition OR : searchField.bind(.predicate, to: arrayController, withKeyPath: NSBindingName.filterPredicate.rawValue, options: [.predicateFormat: "(title contains[cd] $value) OR (artist.name CONTAINS[cd] $value)"]) It works : Bur it is not what I'm looking for. I'd like to concatenate to have all the tracks which contains the string in the artist, title and album, like in the Music app : How to do that ? Thx.