This question already has answers here:
NSUndoManager casting NSUndoManagerProxy crash in Swift code
(2 answers)
Closed 5 years ago.
I'm having trouble adopting the more complex invocation-based approach to undo registration in Swift (based on NSHipster article here. Apple's docs still have all sample code in Objective-C, and the semantics are very different for the invocation setup).
My NSDocument subclass Document has the following method that operates on the model objects, which I wish to make undoable:
func rename(object: Any, to newName: String) {
// This is basically a protocol that requires implementing:
// var name: String { get set }
//
guard var namedObject = object as? EditorHierarchyDisplayable else {
return
}
// Register undo:
let undoController = undoManager?.prepare(withInvocationTarget: self) as? Document
undoController?.rename(object: namedObject, to: namedObject.name)
undoManager?.setActionName("Rename \(namedObject.localizedClassName)")
// Perform the change:
namedObject.name = newName
}
What I have found out is that undoController above is nil, becuase the atempted cast to Document fails. If I remove the cast (and comment out the call to undoController.rename(...), prepare(withInvocationTarget:) returns the folowing object:
(lldb) print undoController
(Any?) $R0 = some {
payload_data_0 = 0x00006080000398a0
payload_data_1 = 0x0000000000000000
payload_data_2 = 0x0000000000000000
instance_type = 0x000060800024f0d8
}
(lldb) print undoController.debugDescription
(String) $R1 = "Optional(NSUndoManagerProxy)"
(lldb)
What am I missing?
I think the basic confusion is that prepare(withInvocationTarget:) returns a proxy object (that happens to be the undo manager itself, but that's an implementation detail). The idea is that you send this proxy object the same message(s) you send to undo the action, but instead of executing them (because it's not the actual object), it internally captures those invocations and saves them for later.
So your code should really start out something like this:
let selfProxy: Any = undoManager?.prepare(withInvocationTarget: self)
This works great in Objective-C because the "catchall" type (id) has very lax type checking. But the equivalent Any class in Swift is much more stringent and does not lend itself to the same technique, if at all.
See Using NSUndoManager and .prepare(withInvocationTarget:) in Swift 3
Related
I have read up on Swift optionals, specifically if let, but cannot make sense of this code. The tutorial is a swift project, but I am trying to use it to update an old Obj-C project. I am not trying to create an if let optional in objective-c, but rather just figure out how to make an Obj-C version do what he is doing here with Swift. The underlying code doesn't return a value.
if let user = user << Obj alternative
Following a tutorial and here is the code:
[[FIRAuth auth] signInWithEmail:userEmail
password:userPass
completion:^(FIRUser * _Nullable user, NSError * _Nullable error) {
if(error == nil) { // No ERROR so already so user already in Firebase
if let user = user{
NSDictionary *userData = #{ #"provider": user.providerID};
[[DataService instance] createFirebaseDBUserWithUid:user.uid
userData:userData
isDriver:NO];
}
DLog(#"Email user Auth successfully in Firebase");
Here is the createFirebaseDBUserWithUid:user code (possible returns a Firebase id value)
#objc func createFirebaseDBUser(uid: String, userData: Dictionary<String, Any>, isDriver: Bool) {
if isDriver {
REF_DRIVERS.child(uid).updateChildValues(userData)
} else {
REF_USERS.child(uid).updateChildValues(userData)
}
}
Would an obj-c version just be:
if( user != nil ){
NSDictionary *userData = #{ #"provider": user.providerID};
[[DataService instance] createFirebaseDBUserWithUid:user.uid userData:userData isDriver:NO];
}
The trick with Objective-C of course is its behaviour if you send any message to anil reference.
At the time of its invention (1986) it was actually progress to have any method sent to a nil reference (which is 'faulty' in an if statement since it is 0 (the number) at the same time) simply returning nil (or 0 if you interpret it as a number) and again 'faulty'.
In this sense you are 'mostly' correct if you do
if (user != nil)
in Objective-C wherever you see
if let user = user {
...
}
in Swift. Technically speaking it is not entirely the same, since the let-expression will give you a non-nullable reference to the same content shadowing the optional object for the following scope. So you do not have to do any more unwrapping of the user object in the scope of the if. Note however, that the user will again be optional after the if statement. This is adequately covered by the nil check in the Objective-C counterpart.
There may be "unusual" edge cases you could miss if the author of the Objective-C code relied on the "strange" interpretation of method passing in Objective-C. Nowadays it is considered bad practice to rely on these, but there used to be a time when we had chains of method calls that relied on said behaviour. Swift was invented partly to get rid of those sometimes unintended effects in method chains.
Objective-C Type is equivalent to Type! in Swift. for example: Objective-C NSDictionary would be NSDictionary! in Swift
Thus, Objective-C types are optionals by default and reading a Swift Type in Objective-C won't be affected by being an optional or not.
back to your question, as the intention of the code is not clear for me, generally, the code you wrote will generate a dictionary that contains one pair of (key, value) with the providerID which is not related to being optional or not.
the issue that you would face is if the User object was created in Swift, and you want to read it in Objective-C, then you have set it up for that
I’m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo() returns an optional CFArray of CFDictionaries, and that can be bridged to the corresponding Swift type [[String : Any]]. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
You can use unsafeBitCast(_:to:) to convert the opaque raw pointer to a CFDictionary. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update
You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.
I'm trying to understand of as typecasting.
Reading Type Casting chapter on Apple Documentation, I've two syntax for downcasting (as? and as! operators) but I didn't find anything about as.
So I thought that I should never have used this kink of operator but yesterday when I was typing code with do-try-catch statement, I met this one:
catch let error as NSError {
print(error)
}
Initially, error was type conforms to the Error protocol.
Now using the as NSError casting, it has become an instance of a NSError class.
But my question is: What does as operator do?
It's not a downcasting sure.. Could it be used for "convert" object?
EDIT
I don't think it's a duplicate.
In my case, the error variable, is not a class and doesn't inherit from a super class so I have not a upcasting. It is not even a pattern matching.
I've already read both Swift Blog page and this thread on StackOverflow.
EDIT 2
From Swift Blog
Swift 1.2 separates the notions of guaranteed conversion and forced
conversion into two distinct operators. Guaranteed conversion is still
performed with the as operator, but forced conversion now uses the as!
operator. The ! is meant to indicate that the conversion may fail.
This way, you know at a glance which conversions may cause the program
to crash.
The text above doesn't work for me because if I tried to use the as! operator instead of as, compiler complain me.
EDIT 3
Even in Using Swift with Cocoa and Obj-C documentation they use the let-as? syntax for checking and casting to a protocol.
So, why in my case, I can't use it?
First of all, as suggested in the dasblinkenlight's comment, your code snippet is not using a type-casting-operator. Check the syntax of do-statement and you can find these:
catch-clause → catch patternopt where-clauseopt code-block
pattern → value-binding-pattern
value-binding-pattern → var pattern | let pattern
pattern → type-casting-pattern
type-casting-pattern → is-pattern | as-pattern
as-pattern → pattern as type
So, your EDIT 2 has no meaning, there are no syntax accepting as! in catch-clause.
But this code (using type-casting-operator) works, so I try to explain how to use as-casting.
enum MyError: Error {
case bad
//...
}
let error: Error = MyError.bad
let nsError = error as NSError
As shown in the linked article in the EI Captain v2.0's comment, as-casting is used for Guaranteed conversion. I have collected some use cases of such conversions.
Upcasting
class Animal {}
class Dog: Animal {}
let d = Dog()
d as Animal // upcast succeeds
As shown in the article, upcasting always succeeds, so you can use as.
Specifying literal type
let byte = 123 as UInt8
let ch = "a" as UnicodeScalar
In Swift, literals are typeless, so you can use as to specify the types of literals
In case Swift can infer the type of the literal, you can omit such as-casting:
let byte: UInt8 = 123
let ch: UnicodeScalar = "a"
Disambiguating overloaded methods
class MyClass {
func aMethod(_ arg: String) {
print(arg)
}
func aMethod(_ arg: Int) {
print("\"\(arg)\"")
}
}
let obj = MyClass()
let theFunc = obj.aMethod as (String)->Void
theFunc("abc") //->abc
Always-succeeds bridging
let str: String = "a String"
let nsStr = str as NSString
let intArr: [Int] = [1,2,3]
let nsArr = intArr as NSArray
The example let nsError = error as NSError is included in this category, and you need to read this article carefully, to understand why this is an always-succeeds bridging.
For your EDIT 3.
You may need to distinguish these two syntaxes:
let a: Any = 1
//Using if-let -- Optional binding, `(a as? Int)` is an expression using type-casting-operator which generates an Optional result
if let intA = (a as? Int) {
print("\(intA) is Int")
}
//Using if-case -- pattern matching, `(let intA as Int)` is a pattern using as-pattern
if case (let intA as Int) = a {
print("\(intA) is Int")
}
As already noted, catch leads a pattern, and you cannot use as? in pattern.
as
Use as for types that Apple has done some work to handle the conversion in the background. These are usually Foundation types that Apple bridged into Swift and want you to have a quick way to convert back and forth from their ObjC equivalents, for example:
String <-> NSString
URL <-> NSURL
Array <-> NSArray
Data <-> NSData
These casts always succeed and Xcode will warn you if you use as? or as!. In your specific case, Apple has done some meddling in the background to make the Error protocol and NSError to be castable to/from each other.
as!
Use as! when you know the object is castable to another type. as! will crash your app if the object is not castable (for example, when sender is actually a UITextField)
let button = sender as! UIButton // you are sure that the sender is always
// a UIButton or your app will crash
as?
Use as? when you not sure if the object is castable to the other type. In practice, this should be your preferred method and you should optional binding to check for success. as? produces nil if the object is not castable:
// Exit the function if the sender is not a UIButton
guard let sender = sender as? UIButton else {
return
}
// Only execute the block if the sender is UIButton
if let button = sender as? UIButton {
// ...
}
I'm working, tentatively, with the AudioToolbox API using Swift 2.0 and Xcode 7b6. The API uses a lot of c-language constructs, including function pointers. This is my first time working with commands like withUnsafeMutablePointer and unsafeBitCast. I am looking for a reality check to make sure that I am not way off base in what I am doing.
For example, to open a file stream, you use the following function:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
Just the type signature of the function makes me start to sweat.
At any rate, the inClientData parameter needs to be an UnsafeMutablePointer<Void>, and the pointer will point to an instance of the same class I am working in. In other words, it needs to be a pointer to self. My approach is to call the function using withUnsafeMutablePointer like this:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen($0, AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
My first question is whether or not I'm using withUnsafeMutablePointer correctly here. I wasn't sure how to get a pointer to self - just writing &self doesn't work, because self is immutable. So I declared proxy as a variable and passed a reference to that, instead. I don't know if this will work or not, but it was the best idea I came up with.
Next, AudioFileStreamPropertyListener and AudioFileStreamPacketsListener are C callback functions. They each get passed the pointer to self that I created using withUnsafeMutablePointer in AudioFileStreamOpen. The pointer is passed in as an UnsafeMutablePointer<Void>, and I need to cast it back to the type of my class (AudioFileStream). To do that, I believe I need to use unsafeBitCast. For example, here is AudioFileStreamPropertyListener:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
That compiles fine, but again I'm not sure if I'm using unsafeBitCast correctly, or if that is even the correct function to be using in this kind of situation. So, is unsafeBitCast the correct way to take an UnsafeMutablePointer<Void> and cast it to a type that you can actually use inside of a C function pointer?
It's interesting that the inClientData "context" param is bridged as UnsafeMutablePointer, since I doubt the AudioToolbox APIs will modify your data. It seems it would be more appropriate if they'd used COpaquePointer. Might want to file a bug.
I think your use of withUnsafeMutablePointer is wrong. The pointer ($0) will be the address of the variable proxy, not the address of your instance. (You could say $0.memory = [a new instance] to change it out for a different instance, for example. This is a bit confusing because its type is UnsafeMutablePointer<MyClass> — and in Swift, the class type is itself a pointer/reference type.)
I was going to recommend you use Unmanaged / COpaquePointer, but I tested it, and realized this does exactly the same thing as unsafeAddressOf(self)!
These are equivalent:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
And these are equivalent:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
While the Unmanaged approach makes logical sense, I think you can see why it might be prefereable to use unsafeAddressOf/unsafeBitCast :-)
Or, you might consider an extension on Unmanaged for your own convenience:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
Then you can use:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
Of course, you will need to ensure that your object is being retained for the duration that you expect it to be valid in callbacks. You could use passRetained, but I would recommend having your top-level controller hold onto it.
See some related discussion at https://forums.developer.apple.com/thread/5134#15725.
Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?
I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:
addObserver(self, function: someCallback, name: "some notification", object: nil)
But addObserver takes a String selector argument.
You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):
addObserverForName:object:queue:usingBlock:
So just use that.
I'd still like to find an answer to my original question, but based on #matt's suggestion, I'm currently using this extension:
extension NSNotificationCenter {
class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
}
}
Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:
NSNotificationCenter.addObserver(doSomething, name: "some notification")
I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.
Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.
I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).
Instead, I would play this game:
Create an dictionary property mapping [String: Void -> ()] (string to function)
When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:
(assuming this is good Swift; I haven't tried it):
void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
let name = // strip the leading stuff off of _cmd
if let f = self.dispatchTable[name] {
f()
}
}
(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)