HowTo set a property from a declared class?

Hi!

I have a problem where I need to set a property from a declared class. I created a Spotlight query sample in Swift which works fine. Now I want to port it to Xojo but I’m lost how to use it properly. Details below:

In Swift I use the NSMetadataQuery as shown here:

sq = NSMetadataQuery() sq.searchScopes = [targetURL] ...

Now I created a NSMetadataQuery class in Xojo:

declare function NSClassFromString lib "Cocoa" (aClassName as CFStringRef) as Ptr dim NSMetadataQueryRef as Ptr = NSClassFromString("NSMetadataQuery")

but I have no idea how to access the property which is defined in Objective-C as

@property(copy) NSArray *searchScopes

I looked at macoslib’s SetProperty examples but I can’t get it to work.
How do I use such properties?

TIA,

Tom

I haven’t looked into the references, but it would be something like

Declare Function searchScopes lib "AppKit" Selector "searchScopes" (ref as ptr) as ptr

simply returning the result (Return searchScopes( NSMetaDataQueryRef)) would give you the handle of the NSArray.
You’ll certainly want to analyze this, so pass it to the constructor of NSArray.

Does that help?

This won’t work because I need to set the search scope. That declare could be used to get the value.

Is that right or did I miss something?

Oh, I see! Whenever you find properties in declares, the Setter (if you build your classes like MacOSLib with the instance properties being computed properties containing the declares) would be

Apple doesn’t tell in most of the cases because properties do always follow that rule (except where marked differently in the docs): take the property name, capitalize it and put a “set” before it and a colon at its end if there’s an additional parameter passed beyond the Reference Ptr.

You would invoke this by passing the NSMetaDataQueryRef and the handle (in MacOSLib terms “id”) of a NSArray filled with your search scopes.

EDIT: Sorry, forgot the : in the declare!

Thanks for your help Ulrich.
This one sems to work (can tell for sure when a spotlight query was successful):

declare Sub searchScopes lib "Cocoa" selector "searchScopes" (class_id as ptr, SearchArray as Ptr)

(note that the selector above contains no colon)

Now I’m fiddeling around with the “predicateWithFormat” class function which is defined for NSPredicate as shown here:

[quote]+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(NSArray *)arguments;

  • (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, …; [/quote]
    I tried several declares but no success so far using:
 declare function predicateWithFormat lib "Cocoa" selector "predicateWithFormat:argumentArray:" (FormatStr as CFStringRef, argumentArray as Ptr) as Ptr

or

 declare function predicateWithFormat lib "Cocoa" selector "predicateWithFormat" (FormatStr as CFStringRef) as Ptr

I get always a “unrecognized selector sent to instance” exception.
So I assume that I missed something. Is there a different handling for class functions vs. instance functions via declare?

The + in the selecor indicates that it is a class method so you need to pass the class ref to the declare to get your instance. The declare should be:

Declare function predicateWithFormat lib "Cocoa" selector "predicateWithFormat:" (clsRef as ptr, format as cfstringref) as ptr

And you call it like:

Dim predicate as ptr = predicateWithFormat(NSClassFromString("NSPredicate"), FORMAT )

Where FORMAT is your format string.

This is a variadic method, which you can’t currently invoke via Xojo declares. The other method can be used, however.

Good catch. Didn’t see the three dots. Couldn’t he create a different declare for each number of arguments though?

Nope. The ABI-dictated calling conventions for normal functions are different than variadic functions. The fact that it’s worked on x86 and PowerPC is just ‘luck’. Trying to do this will crash on 64-bit ARM (been there, debugged that assembly).

Thanks for your help.
Now I use

[code]declare function predicateWithFormat lib “Cocoa” selector “predicateWithFormat:argumentArray:” (class_id as ptr, FormatStr as CFStringRef, argumentArray as Ptr) as Ptr

ref = predicateWithFormat(NSClassFromString(“NSPredicate”), searchQuery, nil)[/code]

This seems to work so far. I will give an update when I got it working.

First I want to thank all people above who gave me valuable clues. Now I answer my own question after I got it working. Here some sample code (macoslib is needed):
I have a Window property

pRunningQuery As NSMetadataQuery = nil

and I call the Spotlight search by using:

[code] //NSPredicate
declare function predicateWithFormat lib “Cocoa” selector “predicateWithFormat:argumentArray:” (class_id as ptr, FormatStr as CFStringRef, argumentArray as Ptr) as Ptr

//
dim volF as FolderItem = PopVolumelist.RowTag(PopVolumelist.ListIndex)
dim searchLocation as String = volF.NativePath
dim searchAttribute as String = popAttributeList.Text
dim searchCompareType as String = popAttributeCompareType.RowTag(popAttributeCompareType.ListIndex)
dim searchString as String = tfSearchString.Text

logLine(“Search location = " + searchLocation)
logLine(“Looking for " + searchAttribute + " " + searchCompareType + " “”” +searchString + “””")

pRunningQuery = new NSMetadataQuery
if pRunningQuery = nil then
logLine(“NSMetadataQueryRef is nil”)
return
end

dim searchLocationsArray As NSMutableArray = NSMutableArray.CreateWithObject(new NSURL(volF))
pRunningQuery.setSearchScopes(searchLocationsArray)

dim searchQuery As CFStringRef = searchAttribute + " " + searchCompareType + " “”" + searchString + “”""
logLine("searchQuery: "+ searchQuery)

pRunningQuery.setPredicate(predicateWithFormat(Cocoa.NSClassFromString(“NSPredicate”), searchQuery, nil))
if pRunningQuery.startQuery then
logLine(“startQuery was successful”)
else
logLine(“startQuery failed”)
end

logLine(“result check timer started”)
tiSearchWatcher.Mode = Timer.ModeMultiple
[/code]

Note the Timer at the end of the method where I check if the query is running using this code in timer.action:

[code] if pRunningQuery = nil then
logLine(CurrentMethodName + “: pRunningQuery is nil!”)
me.mode = 0
return
end

if not pRunningQuery.isRunning then
logLine(CurrentMethodName + “: isRunning false”)
me.mode = 0
fetchSearchResults()
return
end
//logLine(“Timer: isRunning true”)

if pRunningQuery.isStopped then
logLine(CurrentMethodName + “: isStopped”)
me.mode = 0
return
end

dim resCount As integer = pRunningQuery.resultCount
logLine(CurrentMethodName + “: got " + str(resCount) + " results”)
[/code]

When the search has finished I get the results via

[code]Sub fetchSearchResults()
if pRunningQuery = nil then
logLine(CurrentMethodName + “: pRunningQuery is nil!”)
return
end

dim resCount As integer = pRunningQuery.resultCount - 1 // 0 based
logLine(CurrentMethodName + “: got " + str(resCount) + " items”)

for i as integer = 0 to resCount
dim aResult As CFStringRef = pRunningQuery.resultAtIndex(i)
logLine(CurrentMethodName + “: result(”+str(i)+"= " + aResult)
next
End Sub
[/code]

And here is finally the NSMetadataQuery class with all used declares:

[code]#tag Class
Protected Class NSMetadataQuery
Inherits Cocoa.NSObject
#tag Method, Flags = &h1000
Sub Constructor()
// Calling the overridden superclass constructor.
// Note that this may need modifications if there are multiple constructor choices.
// Possible constructor calls:
// Constructor() – From NSObject
// Constructor(obj_id as Ptr, hasOwnership as Boolean = False) – From NSObject
super.Constructor(Allocate(“NSMetadataQuery”))

	End Sub
#tag EndMethod

#tag Method, Flags = &h0
	Function isRunning() As Boolean
	  declare function isGathering lib "Cocoa" selector "isGathering" (class_id as ptr) as Boolean
	  
	  return isGathering(self)
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Function isStarted() As Boolean
	  declare function isStarted lib "Cocoa" selector "isStarted" (class_id as ptr) as Boolean
	  
	  return isStarted(self)
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Function isStopped() As Boolean
	  declare function isStopped lib "Cocoa" selector "isStopped" (class_id as ptr) as Boolean
	  
	  return isStopped(self)
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Function resultAtIndex(index as integer) As String
	  declare function resultAtIndex lib "Cocoa" selector "resultAtIndex:" (class_id as ptr, index as integer) as Ptr
	  
	  // NSMetadataItem
	  declare function valueForAttribute lib "Cocoa" selector "valueForAttribute:" (class_id as ptr, attrName as CFStringRef) as CFStringRef
	  
	  dim anItem As Ptr = resultAtIndex(self, index) // result is NSMetadataItem
	  dim anItemPath As string = valueForAttribute(anItem, "kMDItemPath")
	  
	  return anItemPath
	  
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Function resultCount() As Integer
	  declare function resultCount lib "Cocoa" selector "resultCount" (class_id as ptr) as Integer
	  
	  return resultCount(self)
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Sub setPredicate(predicate as Ptr)
	  declare sub setPredicate lib "Cocoa" selector "setPredicate:" (class_id as ptr, predicate as Ptr)
	  
	  setPredicate(self, predicate)
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h0
	Sub setSearchScopes(searchLocationsArray as NSArray)
	  declare sub setSearchScopes lib "Cocoa" selector "setSearchScopes:" (ref as ptr, SearchArray as Ptr)
	  
	  setSearchScopes(self, searchLocationsArray)
	  
	End Sub
#tag EndMethod

#tag Method, Flags = &h0
	Function startQuery() As Boolean
	  declare function startQuery lib "Cocoa" selector "startQuery" (class_id as ptr) as Boolean
	  
	  return startQuery(self)
	End Function
#tag EndMethod

#tag Method, Flags = &h0
	Sub stopQuery()
	  declare sub stopQuery lib "Cocoa" selector "stopQuery" (class_id as ptr)
	  
	  stopQuery(self)
	End Sub
#tag EndMethod


#tag ViewBehavior
	#tag ViewProperty
		Name="Index"
		Visible=true
		Group="ID"
		InitialValue="-2147483648"
		Type="Integer"
	#tag EndViewProperty
	#tag ViewProperty
		Name="Left"
		Visible=true
		Group="Position"
		InitialValue="0"
		Type="Integer"
	#tag EndViewProperty
	#tag ViewProperty
		Name="Name"
		Visible=true
		Group="ID"
		Type="String"
	#tag EndViewProperty
	#tag ViewProperty
		Name="Super"
		Visible=true
		Group="ID"
		Type="String"
	#tag EndViewProperty
	#tag ViewProperty
		Name="Top"
		Visible=true
		Group="Position"
		InitialValue="0"
		Type="Integer"
	#tag EndViewProperty
#tag EndViewBehavior

End Class
#tag EndClass
[/code]