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()
Related
This is another Kotlin oddity I've run into.
I had this code:
TableCharsets::class.declaredMemberProperties.asSequence()
.map { p -> p.get(TableCharsets) }
and it worked fine.
Then I wanted to do it for more than one object in a loop. So I thought I could write this:
sequenceOf(TableCharsets, Iso2022Charsets, EucCharsets, ShiftJisCharsets).forEach { obj ->
obj::class.declaredMemberProperties.asSequence()
.map { p -> p.get(obj) }
}
But the compiler complains about the call to p.get(obj).
And indeed, if I write this:
val obj = TableCharsets
obj::class.declaredMemberProperties.asSequence()
.map { p -> p.get(obj) }
This gives the same error. Apparently p.get(R) takes Nothing, so there is no possible object I can pass in which would be acceptable.
Thinking that maybe I lost the type of the object somehow, I tried extracting to a function so that it had a known but generic type:
fun <T: Any> extract(obj: T): Sequence<Any> {
obj::class.declaredMemberProperties.asSequence()
.map { p -> p.get(obj) }
}
Again, I get the error that p.get(R) only takes Nothing, and won't let me pass in a T.
When I hover over declaredMemberProperties, IDEA says that it returns a Collection<KProperty<T, *>>, but somehow the property p inside my lambda is a KProperty1<out Any, *>, so that's surely the problem. But it makes no sense to me right now how it is getting that type.
How can I make this work?
When we use obj::class then it is unknown at the compile time, what exactly is the type of obj. We only know its upper bounds, but we don't know the specific type. Therefore, we don't know who is the owner of acquired members and what object do we have to use to access them. For this reason by default obj::class returns KClass<out MyType> which means we can't pass any owner object to it.
Unfortunately, the compiler is not smart enough to recognize that you acquired KClass from exactly the same object that you then use to access members. I believe in that case the operation is safe to do and you can force the compiler to allow it by making an unchecked cast:
(obj::class as KClass<T>).declaredMemberProperties
The difference is right at the start actually. TableCharsets::class is of type KClass<TableCharsets> - it's exactly that class. However, it's different when the object instance is extracted to a variable like this:
val obj = TableCharsets
val kClass = obj::class
Here obj is an instance of the class TableCharsets, and its static type is TableCharsets. However when calling ::class on it, the compiler can only be sure that it's TableCharsets OR a subclass of TableCharsets, so the class instance you get is of type KClass<out TableCharsets>.
This means that when iterating the properties, you cannot know that obj will be sufficient to satisfy the argument of get. Effectively, you're trying to pass an out-only type in an in position (the argument of get).
You can see the problem more clearly here:
val obj: Parent = Child()
val kClass: KClass<out Parent> = obj::class // actually KClass<Child>
kClass.declaredMemberProperties.asSequence()
.map { p -> p.get(Parent()) } // shouldn't accept a Parent instance here
Parent() is obviously not acceptable as input, and from the compiler's point of view obj is also just of Parent type, and thus not acceptable as input, even though it's the object you initially got the KClass from (the compiler doesn't realize that)
I started reading Kotlin course book. I stopped on function literals. Here I have a code:
val printMessage = { message: String -> println(message) }
printMessage("hello")
printMessage("world")
Then I have an information that I can omit parameter type:
{ message -> println(message) }
And now I have next step:
"In fact, Kotlin has a neater trick. If there is only a single parameter and the type can beinferred, then the compiler will allow us to omit the parameter completely. In this case, itmakes the implicit variable it available:
{println(it)}
And now after using this code I get an error "unresolved reference: it" and "too many arguments for public operator fun invoke(): ??? defined in kotlin.Function()":
val printMessage = {println(it)}
printMessage("print something")
My question is how to use implicit variable in single paramenter function literal?
See the Kotlin documentation, specifically where it says:
If the compiler can figure the signature out itself, it is allowed not
to declare the only parameter and to omit ->. The parameter will be
implicitly declared under the name it.
In your case, the compiler (at least up to current version 1.3.31) can't figure the signature out itself:
val printMessage = {println(it)}
But if you give your printMessage variable an explicit type, it will work:
val printMessage: (String) -> Unit = { println(it) }
You always need to provide all information about all generic parameters. If you want to omit it, it needs to be inferable from some other part of the code. The only information you provide though is that you want printMessage to be a lambda. So it assumes it to be of type ()->Unit. This is because you don't declare a parameter for the lambda itself. The implicit parameter it is therefore not usable.
val printMessage = { it: String -> println(it) }
val printMessage: (String)->Unit = { println(it) }
Simply put: If you're inside a lambda with one parameter, the implicit it can be used as this parameters name, but a reference named it within the body of the lambda doesn't declare the single parameter.
I encountered an unfamiliar pattern of initialization from Objective-C that I'm struggling to replicate in Swift.
Objective-C
In the example code, they defined a C struct such as this (abbreviated, original here):
struct AQPlayerState {
AudioFileID mAudioFile;
}
Here's an example that uses AQPlayerState:
AQPlayerState aqData; // 1
OSStattus result =
AudioFileOpenURL(
audioFileURL,
fsRdPerm,
0,
&aqData.mAudioFile // 2
);
The key takeaway from above is that aqData currently has uninitialized properties, and AudioFileOpenURL is initializing aqData.mAudioFile on it's behalf.
Swift
I'm trying to replicate this behaviour in Swift. Here's what I've tried so far:
Models:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Foo {
var person: Person?
}
My idea was to replicate the Objective-C code by passing a reference of Foo.person into a function that would instantiate it on it's behalf.
Initialization Function:
func initializeWithBob(_ ptr: UnsafeMutablePointer<Person?>) {
ptr.pointee = Person(name: "Bob")
}
initializeWithBob takes a pointer to an address for a Person? type and initializes it with a Person(name: "Bob") object.
Here's my test code:
let foo = Foo()
let ptr = UnsafeMutablePointer<Person?>.allocate(capacity: 1)
ptr.initialize(to: foo.person)
defer {
ptr.deinitialize()
ptr.deallocate(capacity: 1)
}
initializeWithBob(ptr)
print(foo.person) // outputs nil
initializeWithBob failed to "install" an instance of type Person in my Foo instance. I presume some of my assumptions are wrong. Looking for help in correcting my assumptions and understanding of this situation.
Thanks in advance!
You can achieve what you are looking for via withUnsafeMutablePointer(to:_:) like so:
let foo = Foo()
withUnsafeMutablePointer(to: &foo.person) { (ptr) -> Void in
initializeWithBob(ptr)
}
print(foo.person!.name) // outputs Bob
However, I wouldn't recommend this approach. IMHO it makes more sense to wrap the APIs you are working with in a C function that you can make 'nice' to call from Swift. The problem with your current approach is that this type of Swift is hard to read for Swift developers and also hard to read for Audio Toolbox developers.
#kelvinlau Is this what you were thinking of trying to achieve?
func initializeWithBob(_ ptr: UnsafeMutablePointer<Foo>) {
ptr.pointee.person = Person(name: "Bob")
}
let foo = Foo()
let ptr = UnsafeMutablePointer<Foo>.allocate(capacity: 1)
ptr.initialize(to: foo)
initializeWithBob(ptr)
print(foo.person?.name ?? "nil")
ptr.deinitialize()
ptr.deallocate(capacity: 1)
print(foo.person?.name ?? "nil")
The code pattern you have in Objective-C is for out parameters, that is parameters which return a value, or in out parameters, that is parameters which both pass a value in and return one. Objective-C does not directly support these so pointers are used to produce the semantics.
Swift has in out parameters indicated by the keyword inout in the function declaration. Within the function an assignment to an inout parameters effectively assigns a value to the variable that was passed as the argument. At the function call site the variable must be prefixed by & to indicate it is the variable itself and not its value which is effectively being passed.
Keeping your Person and Foo as is your function becomes:
func initializeWithBob(_ ptr: inout Person?)
{
ptr = Person(name: "Bob")
}
and it may be used, for example, like:
var example = Foo()
initializeWithBob(&example.person)
Using inout in Swift is better than trying to build the same semantics using pointers.
HTH
Note: You can skip this unless you are curious
"Effectively" was used a few times above. Typically out parameters are implemented by the parameter passing method call-by-result, while in out use call-by-value-result. Using either of these methods the returned value is only assigned to the passed variable at the point the function returns.
Another parameter passing method is call-by-reference, which is similar to call-by-value-result except that each and every assignment to the parameter within the function is immediately made to passed variable. This means changes to the passed variable may be visible before the function returns.
Swift by design does not specify whether its inout uses call-by-value-result or call-by-reference. So rather than specify the exact semantics in the answer "effectively" is used.
Here is my line of code in Swift, it calls a method:
networkManager.postUserProfile(self, onSuccess: {(username: String, userID: NSNumber, recommendedLessons: [AnyObject]) -> Void in
... code block
}, onFailure: {(error: NSError) -> Void in
... another code block
})
The networkManager class is from Objective-C and is:
- (void)postUserProfile:(Profile *)profile
onSuccess:(void(^)(NSString *username, NSNumber *userID, NSArray *recommendedLessons))successBlock
onFailure:(void(^)(NSError *error))failureBlock;
And the error message is:
error: cannot convert value of type (String, NSNumber, [AnyObject]) -> Void to expected argument type ((String!, NSNumber!, [AnyObject]!) -> Void)!
When calling a method, I understand that the ! operator will force-unwrap an optional. However in this situation, what is the meaning of the ! in the expected argument type?
There are many excellent existing answers (such as these) explaining what implicitly unwrapped optionals are and why they are used, so I won't go into that.
Object pointer types in Objective-C header files are treated as implicitly unwrapped optionals in Swift because the compiler doesn't know if nil is a valid value.
Your code should compile if you add an exclamation mark after each of the types in the block you are passing to postUserProfile:
networkManager.postUserProfile(self, onSuccess: { (username: String!, userID: NSNumber!, recommendedLessons: [AnyObject]!) -> Void in
... code block
}, onFailure: { (error: NSError!) -> Void in
... another code block
})
However, a better solution is to add nullability annotations to your Objective-C header file. The properties you mark nullable will be regular optionals and the rest will be non-optionals.
I'm using an NSTimer object in my Swift code, which requires a method signature to be passed to its 'selector' parameter in order to recurrently perform said method. When the method signature does not have an external parameter name i.e.
func timerMethod(internal: String) { ... }
I can pass the timer object this signature as so:
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("timerMethod:"),
userInfo: userInfo,
repeats: true)
However, if I give the method a signature with an external parameter name, such as:
func timerMethod(external internal: String) { ... }
I can't figure out how to call the method. I attempted to log it using:
println("\(__FUNCTION__)")
Which logs the following:
timerMethod(external:)
But whenever I try this or any of the following, I receive 'unrecognized selector' exceptions:
timerMethod:
timerMethod:external
timerMethod:external:
timerMethod:(external)
timerMethod:(external:)
timerMethod(external):
Stumped for now. Anybody running into something similar?
It is timerMethodWithExternal: you can test that with object_getClass(t).instancesRespondToSelector(Selector("timerMethodWithExternal:"))
i used following code to introspect
func with(t: Test, inout count : CUnsignedInt) -> UnsafePointer<Method> {
var mc : CUnsignedInt = 0
return class_copyMethodList(object_getClass(t), &count)
}
var i=0
var mc : CUnsignedInt = 0
var t = Test()
var mlist = with(t,&mc)
var n : Int = Int(mc)
for (i=0; i<n;i++) {
println(sel_getName(method_getName(mlist[i])))
}
Although the signature for your method doesn't look correct, in Swift you pass a selector simply as the string name. The Apple documentation:
Because string literals can be automatically converted to selectors,
you can pass a string literal to any method that accepts a selector.
As for the signature of NSTimer.scheduledTimerWithTimeInterval, the Apple documentation states (See NSTimer documentation, Swift info):
The selector should have the following signature: timerFireMethod:
(including a colon to indicate that the method takes an argument). The
timer passes itself as the argument, thus the method would adopt the
following pattern:
func timerFireMethod(timer: NSTimer) { }
When you define your own method as:
func timerMethod(external internal: String) { ... }
then you are not meeting the required signature. The expected call is:
someMethod(timer: ...)
but your call would be:
someMethod(external: ...)
Besides, the argument should be of type NSTimer, not String. You were probably lucky that your first attempt worked.
FWIW, I ran into the same problem and was also able to confirm the proper selector name by inspecting the Xcode-generated header file in the derived data folder for my project.
Just search for your method's name ~/Library/Developer/Xcode/DerivedData and for the original poster's example you would find that the selector to be used is timerMethodWithExternal: (from its Objective-C definition).
I believe this header is generated even for pure Swift projects, the project I tested on had some Objective-C in it though.