saving to finder.plist

I’m trying to programmatically change the colour of tags in Apple Finder.

I can add tags using folder item.setTagNamesMBS, but there doesn’t seem to be an equivalent to set the colours. So instead I am trying to edit the content of com.apple.finder.plist.

I can find the appropriate cfDictionaryMBS in the plist, but I don’t seem to be able to change the content prior to saving. Here’s the latter part of the routine…

[code]Public Function updateTag(oldName as string, newName as string = “”, colourNumber as integer) as boolean

Dim cd As cfDictionaryMBS = get_CFDict_forTag(oldName) //returned from the array in plist

If cd = Nil Then Return False

//make it editable
Dim cdm As CFMutableDictionaryMBS = cd.edit

If newName = “” Then newName = oldName

cdm.set(NewCFStringMBS(“n”), NewCFStringMBS(“newName”) )
cdm.set(NewCFStringMBS(“l”), NewCFNumberMBSInteger( colourNumber ) )

//commit??
//cd = cdm
//this has modified the dictionary within the file
//now need to write back the whole file I think - can’t just update the one item

Dim b As Boolean = cd_plist.writeToFile(fPlist, True) //this is working but the plist still has old values in it

If b = False Then
MsgBox “failed to write to plist”
End If

//return true if it worked
Return b

End Function
[/code]

Basically I am unclear as to how to get the mutable dictionary back into the original dictionary (cd_plist) - which I have also made as mutable. Do I have to make all intermediate dictionaries in the hierarchy mutable too or is there something else I am missing?

I have updated the routines to make all cfDictionaries and cfArrays in the hierarchy mutable per the methods below, but still not saving the amended dictionary to file. Does anything else need to be mutable?

[code]Public Sub updateSystemTags()

If fPlist = Nil Then Return

//now load the plist into an xml reader
Dim b As LargeBinaryStreamMBS = fPlist.OpenAsLargeBinaryStreamMBS(False)
Dim s As String = b.Read(b.Length)

Dim cb As CFBinaryDataMBS = NewCFBinaryDataMBSStr(s)
Dim co As CFObjectMBS = NewCFObjectMBSFromXML(cb)

cdm_plist = CFDictionaryMBS(co).edit //save it for later use

co = cdm_plist.value(NewCFStringMBS(“values”))

Dim cdm As CFMutableDictionaryMBS
cdm = CFDictionaryMBS(co).edit

co = cdm.value(NewCFStringMBS(“FinderTagDict”))
cdm = CFDictionaryMBS(co).edit

co = cdm.value(NewCFStringMBS(“value”))
cdm = CFDictionaryMBS(co).edit

co = cdm.value(NewCFStringMBS(“FinderTags”))
cam = CFArrayMBS(co).edit //saved as a property so can be accessed later

//ca should be an array of dictionaries
//each has key n for name, l for colour number (0-7), p for binary

Dim c As Integer = cam.count-1

//clear the dictionary
diTags.RemoveAll

Dim o As CFObjectMBS

For i As Integer = 0 To c
co = cam.item(i)
cdm = CFDictionaryMBS(co).edit

Dim sysT As New systemTag
o= cdm.Value(NewCFStringMBS("n"))
If o <> Nil Then 
  
  sysT.name = CFStringMBS(o).Str
  
  o = cdm.value(NewCFStringMBS("l"))
  If o <> Nil Then sysT.colorNo= cfNumberMBS(o).integerValue
  
  o= cdm.Value(NewCFStringMBS("p"))
  If o <> Nil Then sysT.p = CFBooleanMBS(o).Value
  
  o= cdm.Value(NewCFStringMBS("v"))
  If o <> Nil Then sysT.v = CFBooleanMBS(o).Value
  
  //add the contents to the dictionary
  diTags.Value(sysT.name) = sysT
  
End If

Next

End Sub
[/code]

[code]Public Function updateTag(oldName as string, newName as string = “”, colourNumber as integer) as boolean

Dim cdm As cfMutableDictionaryMBS = get_CFDict_forTag(oldName) //returned from the array in plist

If cdm = Nil Then Return False

If newName = “” Then newName = oldName

cdm.set(NewCFStringMBS(“n”), NewCFStringMBS(“newName”) )
cdm.set(NewCFStringMBS(“l”), NewCFNumberMBSInteger( colourNumber ) )

Dim b As Boolean = cdm_plist.writeToFile(fPlist, True) //this is working but the plist still has old values in it

If b = False Then
MsgBox “failed to write to plist”
End If

//return true if it worked
Return b

End Function

[/code]

if you read and alter the plist and save it back you may need to synchronize it to make sure the chnages get pushed into the file ASAP
For some time Apple has used an in memory cache that will periodically save chnages to disk
But there is a plist API to force that to occur

As well you should be able to use the defaults command in terminal once you have made changes to see the results

Thanks Norman, but it appears the problem is before that. When I save the plist to a new test location I can see that the various dictionary/array elements have not been changed from their originals, so something seems to be wrong in my code.

quite possibly a permissions issue :slight_smile:

No, I don’t think so. I am able to save the file to desktop OK, but the file still has the old unchanged content.

It’s worth noting that Finder Labels and Finder Label Colors are read-only in NSWorkspace. It could be that only the Finder itself is allowed to change these now.

Have you tried AppleScript ?

Example:
set label index of item “Testing.sqlite” of front window to 2 # Red

[quote=470955:@James Pitchford]
No, I don’t think so. I am able to save the file to desktop OK, but the file still has the old unchanged content.[/quote]

This is odd, because if you try to send a mutating message to a non-mutable object in Object-C, you get an exception. So I would assume that you’d be aware if your dictionary was just a NSDictionary and not a NSMutableDictionary. Unless there some code (maybe in a plugin) that’s capturing the exception and not correctly forwarding it to your application.

Looking at your code, I see you’re using CFDictionaries, the same should still apply.

I see that you’re updating a value called “cdm”, but are writing out “cdm_plist”. Are these the same object?

Thanks Sam

No they are different objects. cdm_plist is the dictionary at the top of the plist hierarchy whereas cdm is the dictionary lower down in the hierarchy. The hierarchy goes something like this:







So I’ve converted each of the dicts and arrays to mutable in the hierarchy - but still doesn’t seem to change the resultant file saved.