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
Replies
Boosts
Views
Activity
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.
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.
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.
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.
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 "" ?
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.
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.
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.
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.
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.
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.
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 ?
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.
It is in the answer I spoke about.
Thx for your help.