Im a little dumfounded with how to use LLDB to inspect an object in a swift project. In this particular case I'm using NSJSONSerializer to serialize a small chunk of JSON and I'd like to inspect the value. In Xcode 5.1 this was super simple, just type "po json" at the lldb prompt and I'd get what I wanted. Now the commands po and print fail me by printing out mostly garbage. I even tried calling the description property because that works with some swift types but that still doesn't work. As a last resort I used an expression with a println statement and finally that works. Surely there must be a better simpler way? Here is the output of LLDB:
(lldb) print json
(AnyObject?) $R4 = (instance_type = Builtin.RawPointer = 0x00007ff05c0c49d0 -> 0x0000000107ef32c8 (void *)0x0000000107ef32f0: __NSCFDictionary)
(lldb) po json
(instance_type = Builtin.RawPointer = 0x00007ff05c0c49d0 -> 0x0000000107ef32c8 (void *)0x0000000107ef32f0: __NSCFDictionary)
{
instance_type = 0x00007ff05c0c49d0 -> 0x0000000107ef32c8 (void *)0x0000000107ef32f0: __NSCFDictionary
}
(lldb) print json.description?
error: <EXPR>:1:1: error: 'Optional<AnyObject>' does not have a member named 'description'
json.description?
^ ~~~~~~~~~~~
(lldb) po json.description?
error: <EXPR>:1:1: error: 'Optional<AnyObject>' does not have a member named 'description'
json.description?
^ ~~~~~~~~~~~
(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
1 println(json)
2
Optional({
errors = {
"authorizations.provider_user_id" = (
"has already been taken"
);
};
})
(lldb)
you could try
(lldb) expr -O -d run -- json!
The fact that "po" does not exactly work in the same way it does in ObjC is a known limitation. Explicitly unwrapping the optional, and allowing dynamic type resolution on the unwrapped value should work
Related
I am using Xcode 10 and Swift and I have a breakpoint in my code:
let logoWPoints = logoUIImage.size.width
let logoHPoints = logoUIImage.size.height
let logoScale = logoUIImage.scale
let logoWPixels = logoWPoints * logoScale
let logoHPixels = logoHPoints * logoScale // <--- breakpoint
I type in the console in LLDB "print logoWPoints" which results in the error:
(CGFloat) $R0 = 281 Fix-it applied, fixed expression was:
logoWPixels
Wrong! I want logoWPoints just like I typed. I've been seeing this lately. What's the deal?
UPDATE - When I use the frame variable command in LLDB, I can see the message that logoWPoints isn't available:
(CGFloat) logoWPoints = variable not available
Strangely, if I print out what that equals (logoUIImage.size.width) I get the result just fine. So why does it say it's not available?
There's already an answer to the question that leads back to the "variable not available" message in connection to the "Fix-it applied" message, which relates to a compiler optimization.
For those that are having the Fix-it problem, see this answer for details: lldb error: variable not available
I'm trying to access the balance of the data fields of the current user.
The following is what I can only see when querying the currentUser in the debugger:
(lldb) po currentUser
<PFUser: 0x17642d80, objectId: FUsro30ZFu, localId: (null)> {
displayName = Turkey;
email = "frederick.c.lee#gmail.com";
firstName = Frederick;
lastName = Lee;
username = "UncleRic ";
}
Here's the field (as other) that I want to get:
These are the unsuccessful attempts:
(lldb) po currentUser["location"]
error: indexing expression is invalid because subscript type 'const char *' is not an Objective-C pointer
error: 1 errors parsing expression
(lldb) po currentUser.location
error: property 'location' not found on object of type 'PFUser *'
error: 1 errors parsing expression
(lldb) po currentUser.valueForKey("location")
error: property 'valueForKey' not found on object of type 'PFUser *'
error: 1 errors parsing expression
What is the correct method for accessing the remainder fields of the object 'PFUser'?
Also, how do I update/assign a field value of PFUser?
Apparently I need to recast a String into an AnyObject object. But the 'location' field has already been defined as a String field.
There are a couple issues here.
1) I was attempting to user Swift instead of Objective-C in the debugger.
So I got the syntax all screwed up.
2) I didn't restart (cold start) my app # device after I changed my parse.com data on-line (via web). So I was looking a latent (non refreshed data).
The following is the correct format of looking at a PFUser attribute value:
(lldb) po [currentUser objectForKey:#"firstName"]
Frederick
(lldb) po [currentUser objectForKey:#"location"]
Ann Arbor
Here's the correct Swift syntax for updating the PFUser object. Note: be sure to have the actual SAVE() done on a backend thread. The code user is essentially of proof-of-concept that I can actually update the file.
func updateParseUser(newUser:User, sender:UIViewController) {
let currentUser = PFUser.currentUser()
if ((currentUser) == nil) {
return
}
if let revisedlocation = newUser.location {
currentUser?.setValue(revisedlocation, forKey: "location")
}
if let phone = newUser.phoneNumber {
currentUser?.setValue(phone, forKey: "phone")
}
currentUser!.save()
}
Thanks for the feedback
All I'm trying to do is to inverse key/values in a dictionary, and I get the following error:
extra argument 'count' in cal
var dictionary = SWIFT_DICTIONARY
var inverseDictionary = NSDictionary.dictionaryWithObjects(dictionary?.keys, forKeys: dictionary?.values, count: dictionary?.count)
Use this instead:
var inverseDictionary = NSDictionary(objects: dictionary.keys.array, forKeys: dictionary.values.array)
I notice that you are unwrapping dictionary in your code, but it is declared as non optional. Code mistake or just copy & paste mistake?
Addendum - if you try the static version without the count parameter:
var inverseDictionary = NSDictionary.dictionaryWithObjects(dictionary.keys.array, forKeys: dictionary.values.array)
the compiler complains with this message:
'dictionaryWithObjects(_:forKeys:) is unavailable: use object construction 'NSDictionary(objects:forKeys:)'
I think the same happens for the other method you want to use, but the compiler doesn't report the proper error message.
How can I plot out variable's value in a Swift App with LLDB?
Earlier it was like po variable_name
Now I usually get some nasty error, like:
(lldb) po a
error: <EXPR>:11:5: error: use of unresolved identifier '$__lldb_injected_self'
$__lldb_injected_self.$__lldb_wrapped_expr_2(
^
That error sounds like it might be because DWARF is not telling LLDB where to find your self object. Given the nature of Swift, LLDB needs to know the type of self in order to be able to inject an expression inside your local scope.
One way to find out if that is your problem is to do at the LLDB prompt:
(lldb) frame variable -L self
You are probably going to not see a location for it. Worth filling a bug report for, just to track your specific repro case.
Anyway, to get to the bulk of your question. In Swift, there is no language-sanctioned mechanism for "print description" like for ObjC, so while you can type po self, unless self is an Objective-C type, you will pretty much see the same thing that "p self" or even "frame variable self" would tell you - which is entirely based on the LLDB data formatters mechanism. If you want to hook into that to customize the way your Swift objects look, the obligatory reference is: http://lldb.llvm.org/varformats.html
I have made a few tests to figure out how it works with Swift, results surprised me a bit. With ObjC objects po calls debugDescription which by default calls description. That is clear. Unfortunately the same doesn't apply while working with Swift classes. I focused on objects rather than on printing single variables.
To make it working (po command in lldb) I had to override description. Below code I used for testing:
class Test : NSObject
{
var name : String?
var surname : String?
override var debugDescription : String{
return "debugDescription method"
}
override var description : String {
return "description Method"
}
}
Testing:
let test = Test()
test.name = "name"
test.surname = "surname"
(lldb) po test
description Method
(lldb) p test
(DebugTest.Test) $R1 = 0x00007fce11404860 {
ObjectiveC.NSObject = {
isa = DebugTest.Test
}
name = "name"
surname = "surname"
}
(lldb) po dump(test)
▿ DebugTest.Test #0
- super: debugDescription method
▿ name: name
- Some: name
▿ surname: surname
- Some: surname
description Method
(lldb) po print(test)
description Method
The thing that surprised me is that po on Swift objects calls description rather than debugDescription that differs from ObjC.
EDIT
To make it acting like with ObjC, Your class have to implement CustomDebugStringConvertible and then po will call debugDescription, which by default calls description. The only thing which have to be changed in my example would be adding:
class Test : NSObject, CustomDebugStringConvertible
Reference
Checked with XCode 7.0.1 and iOS SDK 9, Swift 2.0
Great answers in this page about swift defaulting to return the description when running lldb) po
If it helps, when hitting errors with lldb and Swift objects I alway tried to be in a good place.
First, tell lldb you are in a Swift context (not Objective-C):
(lldb) settings set target.language swift
Then I would always double-check I had imported the Framework..
lldb) exp import WebKit
(lldb) expr let $ds = unsafeBitCast(0x6000006d74b0, to: WKWebsiteDataStore.self)
(lldb) po $ds
<WKWebsiteDataStore: 0x6000006d74b0>
I can confirm the same error, for Xcode beta4, and
frame variable -L self
displays something, but seems worst:
: (SwiftCollectionViewSample.DetailViewController) self =
I will definitively filed a bug, Enrico
17819707 debugger prints error: use of unresolved identifier '$__lldb_injected_self'
According to the "Using Swift with Cocoa and Objective-C" iBook (near Loc 9) regarding id compatibility:
You can also call any Objective-C method and access any property without casting to a more specific class type.
The example given in the book (shown below) does not compile, which seems to contradict the above statement. According to the book, the last line below should not execute because the length property does not exist on an NSDate object.
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()
let myLength = myObject.length?
Instead, this fails to compile with a "Could not find an overload for 'length' that accepts the supplied arguments" error on the last line.
Is this a bug, a typo, or is the statement about calling any Objective-C method wrong?
the compiler error is saying length is a function and you call it with incorrect arguments
you can do myObject.length?() which return nil
also myObject.count? will give you nil without compile error
xcrun swift
Welcome to Swift! Type :help for assistance.
1> import Cocoa
2> var obj : AnyObject = NSDate()
obj: __NSDate = 2014-06-25 13:02:43 NZST
3> obj.length?()
$R5: Int? = nil
4> obj.count?
$R6: Int? = nil
5> obj = NSArray()
6> obj.length?()
$R8: Int? = nil
7> obj.count?
$R9: Int? = 0
8>
I think what compiler was doing is search all method and property that called length, it found some class provide length() but no class provide length, hence the error message. Just like when you call [obj nonexistmethod] in ObjC, compiler will give your error saying nonexistmethod does not exist even you call it on id type object
This is what happened if you try to call non-exist method in Swift
14> obj.notexist?
<REPL>:14:1: error: 'AnyObject' does not have a member named 'notexist'
obj.notexist?
^ ~~~~~~~~
14> obj.notexist?()
<REPL>:14:1: error: 'AnyObject' does not have a member named 'notexist()'
obj.notexist?()
^ ~~~~~~~~