I'm currently using Reflection to inspect an element at runtime using the class.memberProperties function. The type of properties is collection<KProperty1<I, *>> so I run through each of the KProperty objects to find the one that I want by checking if the name is equal to "nameIWant", though I would much rather be able to get the instance of the property from the KProperty by using the .get() method on the property, so that then I could do a check such as:
if (property.get(receiver) is ClassIWant) {
//Do something
}
My code looks like this:
val properties = request.payload::class.memberProperties
properties.forEach { property ->
run {
if (property.name.equals("nameIWant")) {
}
}
}
So far I've been trying to use the .get() method on the KProperty1 type but it takes an argument receiver of type Nothing. I'm not able to work out what I need to pass in order to call the .get() method and get the particular instance of the property. I've also checked the documentation here: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property1/index.html but it hasn't really helped at all.
justPassingBy is right. but the more simple way is to use:
myObj.javaClass.kotlin.memberProperties.foreach { property ->
property.get(myObj)
}
If you want to get the value of the property, cast the class into invariant type.
instance::class.memberProperties.first() // returns KProperty1<out Instance, *>
(instance::class as KClass<Instance>).memberProperties.first() // returns KProperty1<Instance, *>
If your KClass<Instance> is KClass<*>, use Any as Instance.
Why did the KProperty.call take Nothing as receiver?
Because instance::class returns KClass<out Instance>, which propagates the covariant type argument down to the property, which it becomes KProperty<out Instance, *>, which narrows down the possible method receiver to any subtype of Instance, but because we do not know which, we can not safely supply any instance of Instance, as show by the rules of variance, which here limit the generic type argument to Nothing, which means it is impossible to call the method at all.
Why is ::class designed to be covariant?
To guarantee safety. This has been an issue of great debates as it seems somewhat illogical.
If you want to know the type of the value that the property can return, use
property.returnType
It returns a KType, wich is Kotlin's version of Java's Type, which is a more generic concept of a Class (which is one of the implementations of Type).
If you need to 'convert' the KType to a KClass, you need to do the same as if you needed to convert Type to a Class, which is get the raw type of the type. Raw type is type stripped of the any generic information, yes, an erased type. The way to do this is (seemingly) more complicated (involves handling each possible KType/Type implementation) and I recommend checking for answer to this problem separately.
You will be able to reuse Java implementation (that you will surely find on your own) using:
kType.javaType.covertJavaTypeToJavaClass().kotlin // returns KClass<*>
Corrections in your question. I recommend using the proper terms if you wish to receive proper answers:
* I in your question is type of the method receiver, not the value of the property
* collection is not a type, Collection is
* property is ClassIWantis ambiguous as property.type is type of the value in the property and property::class is simply the property implementation, is is also an instanceof check, but in reflection, you need to use KClass.isSubclassOf, or what is known in Java as type.isAssignableFrom (watch the call order), which then makes your condition to be ClassIWant.isSuperclassOf(property.type.getRawType())
* instance of the property properties have values, not instances. Only classes have instances. Instances are values and values are instances (of some class), but you must still say instance representing the value of the property
You can create a KType for your ClassIWant and then check the property's returnType. It will be something like this:
val properties = request.payload::class.memberProperties
val desiredType = ClassIWant::class.createType()
properties.forEach { property ->
if (property.name == "nameIWant" && property.returnType == desiredType) {
//todo
}
}
btw you can cast your property variable to correct type and use get
val properties = request.payload::class.memberProperties
properties.forEach { property ->
val value = (property as KProperty1<Payload, *>).get(request.payload)
if (property.name == "nameIWant" && value is ClassIWant) {
//todo
}
}
prop.getter.call(obj) as String?
This is a fairly short question, but I'm a bit confused on how to fix it.
for item in filteredAndSortedDates {
print(item.datesSectionHeader()) // Returns a value
print(item.value(forKeyPath: "datesSectionHeader") as Any) // Returns nil
// The "as Any" part up above is just to keep the compiler quiet. It doesn't have any meaning as this is just for testing purposes.
}
I'm a bit confused on why this is happening. How come valueForKeyPath is returning nil when the above is returning a value? I'm calling this on an NSDictionary.
This is the log I'm getting:
HAPPENING THIS WEEK
nil
HAPPENING THIS WEEK
nil
HAPPENING THIS WEEK
nil
HAPPENING WITHIN A YEAR
nil
Here's how I'm declaring datesSectionHeader:
extension NSDictionary {
// FIXME
func datesSectionHeader() -> String {
// Doing some work in here.
}
}
NSDictionary modifies the standard behavior of Key-Value Coding so that it accesses the dictionary's contents rather than its properties. It does this by overriding value(forKey:) (which is, in turn, used by value(forKeyPath:)).
As documented, its override of value(forKey:) checks if the key is prefixed by "#". If it's not, it returns the result of object(forKey:), accessing the dictionary contents. If it is prefixed with "#", it strips the "#" and returns the result from the superclass's implementation, which accesses the dictionary's properties.
So, in this particular case, you can access the results from your datesSectionHeader() getter method using the following:
item.value(forKeyPath: "#datesSectionHeader")
JXA, with its built-in ObjC bridge, exposes enumeration and constants from the Foundation framework automatically via the $ object; e.g.:
$.NSUTF8StringEncoding // -> 4
However, there are also useful CFString constants in lower-level APIs that aren't automatically imported, namely the kUTType* constants in CoreServices that define frequently-used UTI values, such as kUTTypeHTML for UTI "public.html".
While you can import them with ObjC.import('CoreServices'), their string value isn't (readily) accessible, presumably because its type is CFString[Ref]:
ObjC.import('CoreServices') // import kUTType* constants; ObjC.import('Cocoa') works too
$.kUTTypeHTML // returns an [object Ref] instance - how do you get its string value?
I have yet to find a way to get at the string at the heart of what's returned:
ObjC.unwrap($.kUTTypeHTML) doesn't work, and neither does ObjC.unwrap($.kUTTypeHTML[0]) (nor .deepUnwrap()).
I wonder:
if there's a native JXA way to do this that I'm missing.
otherwise, if there's away to use ObjC.bindFunction() to define bindings for CFString*() functions that can solve the problem, such as to CFStringGetCString() or CFStringGetCStringPtr(), but it's not obvious to me how to translate the ObjC signatures.
While I don't understand all implications, the following seems to work:
$.CFStringGetCStringPtr($.kUTTypeHTML, 0) // -> 'public.html'
# Alternative, with explicit UTF-8 encoding specification
$.CFStringGetCStringPtr($.kUTTypeHTML, $.kCFStringEncodingUTF8) // ditto
The kUTType* constants are defined as CFStringRef, and CFStringGetCStringPtr returns a CFString object's internal C string in the specified encoding, if it can be extracted "with no memory allocations and no copying, in constant time" - or NULL otherwise.
With the built-in constants, it seems that a C string (rather than NULL) is always returned, which - by virtue of C data types mapping onto JXA data types - is directly usable in JavaScript:
$.CFStringGetCStringPtr($.kUTTypeHTML, 0) === 'public.html' // true
For background information (as of OSX 10.11.1), read on.
JXA doesn't natively recognize CFString objects, even though they can be "toll-free bridged" to NSString, a type that JXA does recognize.
You can verify that JXA does not know the equivalence of CFString and NSString by executing $.NSString.stringWithString($.kUTTypeHTML).js, which should return a copy of the input string, but instead fails with -[__NSDictionaryM length]: unrecognized selector sent to instance.
Not recognizing CFString is our starting point: $.kUTTypeHTML is of type CFString[Ref], but JXA doesn't return a JS string representation of it, only [object Ref].
Note: The following is in part speculative - do tell me if I'm wrong.
Not recognizing CFString has another side effect, namely when invoking CF*() functions that accept a generic type (or Cocoa methods that accept a toll-free bridged CF* type that JXA is unaware of):
In such cases, if the argument type doesn't exactly match the invoked function's parameter type, JXA apparently implicitly wraps the input object in a CFDictionary instance, whose only entry has key type, with the associated value containing the original object.[1]
Presumably, this is why the above $.NSString.stringWithString() call fails: it is being passed the CFDictionary wrapper rather than the CFString instance.
Another case in point is the CFGetTypeID() function, which expects a CFTypeRef argument: i.e., any CF* type.
Since JXA doesn't know that it's OK to pass a CFStringRef argument as-is as the CFTypeRef parameter, it mistakenly performs the above-mentioned wrapping, and effectively passes a CFDictionary instance instead:
$.CFGetTypeID($.kUTTypeHTML) // -> !! 18 (CFDictionary), NOT 7 (CFString)
This is what houthakker experienced in his solution attempt.
For a given CF* function you can bypass the default behavior by using ObjC.bindFunction() to redefine the function of interest:
// Redefine CFGetTypeID() to accept any type as-is:
ObjC.bindFunction('CFGetTypeID', ['unsigned long', [ 'void *']])
Now, $.CFGetTypeID($.kUTTypeHTML) correctly returns 7 (CFString).
Note: The redefined $.CFGetTypeID() returns a JS Number instance, whereas the original returns a string representation of the underlying number (CFTypeID value).
Generally, if you want to know the specific type of a given CF* instance informally, use CFShow(), e.g.:
$.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}'
Note: CFShow() returns nothing and instead prints directly to stderr, so you can't capture the output in JS.
You may redefine CFShow with ObjC.bindFunction('CFShow', ['void', [ 'void *' ]]) so as not to show the wrapper dictionary.
For natively recognized CF* types - those that map onto JS primitives - you'll see the specific type directly (e.g., CFBoolean for false); for unknown - and therefore wrapped - instances, you'll see the wrapper structure as above - read on for more.
[1] Running the following gives you an idea of the wrapper object being generated by JXA when passing an unknown type:
// Note: CFShow() prints a description of the type of its argument
// directly to stderr.
$.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}'
// Alternative that *returns* the description as a JS string:
$.CFStringGetCStringPtr($.CFCopyDescription($.kUTTypeHTML), 0) // -> (see above)
Similarly, using the known-to-JXA equivalence of NSDictionary and CFDictionary,
ObjC.deepUnwrap($.NSDictionary.dictionaryWithDictionary( $.kUTTypeHTML ))
returns {"type":"{__CFString=}"}, i.e., a JS object with property type whose value is at this point - after an ObjC-bridge call roundtrip - a mere string representation of what presumably was the original CFString instance.
houthakker's solution attempt also contains a handy snippet of code to obtain the type name of a CF* instance as a string.
If we refactor it into a function and apply the necessary redefinition of CFGetTypeID(), we get the following, HOWEVER:
A hack is needed to make it return a value predictably (see comments and source code)
Even then a random character sometimes appears as the end of the string returned, such as CFString, rather than CFString.
If anyone has an explanation for why the hack is needed and where the random characters come from, please let me know. The issues may be memory-management related, as both CFCopyTypeIDDescription() and CFStringCreateExternalRepresentation() return an object that the caller must release, and I don't know whether/how/when JXA does that.
/*
Returns the type name of the specified CF* (CoreFoundation) type instance.
CAVEAT:
* A HACK IS EMPLOYED to ensure that a value is consistently returned f
those CF* types that correspond to JS primitives, such as CFNumber,
CFBoolean, and CFString:
THE CODE IS CALLED IN A TIGHT LOOP UNTIL A STRING IS RETURNED.
THIS SEEMS TO WORK WELL IN PRACTICE, BUT CAVEAT EMPTOR.
Also, ON OCCASION A RANDOM CHARACTER APPEARS AT THE END OF THE STRING.
* Only pass in true CF* instances, as obtained from CF*() function
calls or constants such as $.kUTTypeHTML. Any other type will CRASH the
function.
Example:
getCFTypeName($.kUTTypeHTML) // -> 'CFString'
*/
function getCFTypeName(cfObj) {
// Redefine CFGetTypeID() so that it accepts unkown types as-is
// Caution:
// * ObjC.bindFunction() always takes effect *globally*.
// * Be sure to pass only true CF* instances from then on, otherwise
// the function will crash.
ObjC.bindFunction('CFGetTypeID', [ 'unsigned long', [ 'void *' ]])
// Note: Ideally, we'd redefine CFCopyDescription() analogously and pass
// the object *directly* to get a description, but this is not an option:
// ObjC.bindFunction('CFCopyDescription', ['void *', [ 'void *' ]])
// doesn't work, because, since we're limited to *C* types, we can't describe
// the *return* type in a way that CFStringGetCStringPtr() - which expects
// a CFStringRef - would then recognize ('Ref has incompatible type').
// Thus, we must first get a type's numerical ID with CFGetTypeID() and then
// get that *type*'s description with CFCopyTypeIDDescription().
// Unfortunately, passing the resulting CFString to $.CFStringGetCStringPtr()
// does NOT work: it yields NULL - no idea why.
//
// Using $.CFStringCreateExternalRepresentation(), which yields a CFData
// instance, from which a C string pointer can be extracted from with
// CFDataGetBytePtr(), works:
// - reliably with non-primitive types such as CFDictionary
// - only INTERMITTENTLY with the equivalent types of JS primitive types
// (such as CFBoolean, CFString, and CFNumber) - why??
// Frequently, and unpredictably, `undefined` is returned.
// !! THUS, THE FOLLOWING HACK IS EMPLOYED: THE CODE IS CALLED IN A TIGHT
// !! LOOP UNTIL A STRING IS RETURNED. THIS SEEMS TO WORK WELL IN PRACTICE,
// !! BUT CAVEAT EMPTOR.
// Also, sometimes, WHEN A STRING IS RETURNED, IT MAY CONTAIN A RANDOM
// EXTRA CHAR. AT THE END.
do {
var data = $.CFStringCreateExternalRepresentation(
null, // use default allocator
$.CFCopyTypeIDDescription($.CFGetTypeID(cfObj)),
0x08000100, // kCFStringEncodingUTF8
0 // loss byte: n/a here
); // returns a CFData instance
s = $.CFDataGetBytePtr(data)
} while (s === undefined)
return s
}
You can coerce a CF type to an NS type by first re-binding the CFMakeCollectable function so that it takes 'void *' and returns 'id', and then using that function to perform the coercion:
ObjC.bindFunction('CFMakeCollectable', [ 'id', [ 'void *' ] ]);
var cfString = $.CFStringCreateWithCString(0, "foo", 0); // => [object Ref]
var nsString = $.CFMakeCollectable(cfString); // => $("foo")
To make this easier to use in your code, you might define a .toNS() function on the Ref prototype:
Ref.prototype.toNS = function () { return $.CFMakeCollectable(this); }
Here is how you would use this new function with a CFString constant:
ObjC.import('CoreServices')
$.kUTTypeHTML.toNS() // => $("public.html")
$.kUTTypeHTML appears to return a CFDictionary (see below), so you should find useable methods at:
EDIT: It turns out that some typing complexities in JXA-ObjC-CF interactions mean that snippet below is NOT a reliable or generally applicable approach to learning the type of a CF Object reference. (See the discussion that follows).
https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFDictionaryRef/
ObjC.import('CoreServices')
var data = $.CFStringCreateExternalRepresentation(
null,
$.CFCopyTypeIDDescription(
$.CFGetTypeID($.kUTTypeHTML)
),
'UTF-8',
0
); // CFDataRef
cPtr = $.CFDataGetBytePtr(data);
// --> "CFDictionary"
I want to implement a callback in a swift project just like I used to do in Objective-C
I need a variable of type closure. That closure should take as a parameter an object and return nothing.
var downloadCompleted: (MLBook) -> (Void)!
When I need to trigger the callback I do this:
if self.downloadCompleted {
self.downloadCompleted(book)
}
The compiler complains with this error message:
Type '(MLBook) -> (Void)!' does not conform to protocol 'BooleanType'
If I remove the if statement the compiler says:
Property 'self.downloadCompleted' not initialized
even though it's implicitly unwrapped.
When I try to get the callback:
BookStore.sharedInstance.downloadCompleted{(book: MLBook) -> () in
println("Print if you got the callback")
}
I get this error message:
'(MLBook) -> ()' is not convertible to 'MLBook'
I'm more worried about the last error message as I'm not quite sure what it is trying to tell me.
Any help would be appreciated. Thanks
Here is your working example. You have a number of mistakes which the attached illustrates. Note
I had the download() method return Bool so that the result can be see in this screen shot.
But, your use of an implicitly unwrapped optional (aka '!') is incorrect. Such an optional is used when the value may be nil but will be assigned at a known time and not changed (see Apple documentation for a description). Your downloadCompleted is a true optional (at least in your example use). Thus, better code, which turns out to be slightly simpler is:
2 mistakes. 1st, The whole type should be wrapped in (), then followed a ? or ! as a optional or implicit unwrapped optional. 2nd, you should check with nil, in swift, no implicit boolean conversion.
In your use case, you should use Optional instead of Implicit unwrapped. Because there is big chance that your property has a nil value. With IUO(Implicit unwrapped optional), you skip compiler warning and will get a runtime error.
import Foundation
class MLBook {
var name = "name"
}
class A {
var downloadCompleted: ((MLBook) -> Void)?
func down(){
var book = MLBook()
if let cb = self.downloadCompleted {
cb(book)
}
}
}
var a = A()
a.downloadCompleted = {
(book: MLBook) -> Void in
println(book.name)
}
a.down()
Can you send messages to nil in Swift the same way you can in Objective-C without causing a crash?
I tried looking into the documentation and couldn't find anything relating to this.
Not exactly, you have to use Optional Chaining. In swift, an instance can only be nil if it is declared as an "optional" type. Normally this looks like this:
var optionalString : String?
Notice the ? after the String That is what makes it possible to be nil
You cannot call a method on that variable unless you first "unwrap" it, unless you use the aforementioned Optional Chaining.
With optional chaining you can call multiple methods deep, that all allow for a nil value to be returned:
var optionalResult = optionalString.method1()?.method2()?.method3()
optionalResult can also be nil. If any of the methods in the chain return nil, methods after it are not called, instead optionalResult immediately gets set to nil.
You cannot deal directly with an optional value until you explicitly handle the case that it is nil. You can do that in one of two ways:
Force it to unwrap blindly
println(optionalString!)
This will throw a runtime error if it is nil, so you should be very sure that it is not nil
Test if it is nil
You can do this by using a simple if statement:
if optionalString {
println(optionalString!)
}
else {
// it was nil
}
or you can assign it to a scoped variable so that you don't have to forcefully unwrap it:
if let nonoptionalString = optionalString {
println(nonoptionalString)
}
else {
// it was nil
}