I'm writing UI testing code using XCTest. Here is my test method:
func testLandingUI() {
let app = XCUIApplication()
let headerExpectedMessage = "Title"
let headerLabel = app.staticTexts[headerExpectedMessage]
XCTAssert(headerLabel.exists)
}
I'm getting this error:
ExampleUITests.swift:38:19: error: value of type 'Any?' has no member 'exists'
XCTAssert(headerLabel.exists)
^~~~~~~~~~~ ~~~~~~
What's strange about this error is that I expected headerLabel to be of type XCUIElement, not Any?.
XCUIApplication.staticTexts is an XCUIElementQuery, which has a subscript method declared thusly:
open class XCUIElementQuery : NSObject, XCUIElementTypeQueryProvider {
...
open subscript(key: String) -> XCUIElement { get }
...
}
What I believe is happening is the subscript method in XCUIElementQuery is not being selected by Swift's overload resolution. Instead, it's selecting this category on NSObject:
#interface NSObject (MyCategory)
- (id)objectForKeyedSubscript:(NSString*)key;
- (void)setObject:(id)obj forKeyedSubscript:(NSString*)key;
#end
I verified that if I remove that category from my project, the error goes away. Assume that removing that category is not possible (because it's not). Is there any way to get Swift to use the correct subscript method?
Minimal test case that shows the problem: https://www.dropbox.com/s/f0fm5ennco7t2ua/SubscriptCategoryTest.zip?dl=1
(note that the error is in the UI tests, so press command-shift-U to see the error)
EDIT: It looks like the problem only shows up if the category defines setObject:forKeyedSubscript:. Interestingly, I get a slightly different error if both getter and setter are defined vs. just the setter.
Since the compiler is confused, you need to help it....
Given this example:
let example1: Any? = "1"
let example2: Any = "2"
You have two issues (it looks like you have an optional...)
if example1.exists { // won't work - I got your error message
}
if (example2 as AnyObject).exists { // works with a cast
}
I believe if you correct identify the type of the variable it will solve your problem. It is matching the NSObject category only because it didn't match something more specific.
I have read this question and some other questions. But they are somewhat unrelated to my question
For UILabel if you don't specify ? or ! you will get such an error:
#IBOutlet property has non-optional type 'UILabel'
Which then Xcode gives you 2 choices to have it fixed, you can do:
fix-it Add ? to form the optional type UIlabel?
fix-it Add ! to form
the implicitly unwrapped optional type UIlabel?
However for string you can just type string without ? or ! and you won't get an error why is that?
What happens if the name isn't set? Then we would have a nil isn't using ?, ! and Swift all about satisfying 'type-safety'?
Example:
struct PancakeHouse {
let name: String // this doesn't have '?' nor '!'
let photo: UIImage?
let location: CLLocationCoordinate2D?
let details: String
}
My major confussion is when would we want to not use Optional?
All this is covered in the documentation: The Swift Programming Language - The Basics.
In short:
String represents a String that's guaranteed to be present. There is no way this value can be nil, thus it's safe to use directly.
String? represents a Optional<String>, which can be nil. You can't use it directly, you must first unwrap it (using a guard, if let, or the force unwrap operator !) to produce a String. As long as you don't force unwrap it with !, this too is safe.
String! also represents an Optional<String>, which can be nil. However, this optional can be used where non-optionals are expected, which causes implicit forced unwrapping. It's like having a String? and having it always be implicitly force unwrapped with !. These are dangerous, as an occurrence of nil will crash your program (unless you check for nil manually).
For your PancakeHouse struct, name is non-optional. That means that its name property cannot be nil. The compiler will enforce a requirement that name be initialized to a non-nil value anytime an instance of PancakeHouse is initialized. It does this by requiring name to be set in any and all initializers defined for PancakeHouse.
For #IBOutlet properties, this is not possible. When an Interface Builder file (XIB or Storyboard) is unarchived/loaded, the outlets defined in the IB file are set, but this always occurs after the objects therein have been initialized (e.g. during view loading). So there is necessarily a time after initialization but before the outlets have been set, and during this time, the outlets will be nil. (There's also the issue that the outlets may not be set in the IB file, and the compiler doesn't/can't check that.) This explains why #IBOutlet properties must be optional.
The choice between an implicitly unwrapped optional (!) and a regular optional (?) for #IBOutlets is up to you. The arguments are essentially that using ! lets you treat the property as if it were non-optional and therefore never nil. If they are nil for some reason, that would generally be considered a programmer error (e.g. outlet is not connected, or you accessed it before view loading finished, etc.), and in those cases, failing by crashing during development will help you catch the bug faster. On the other hand, declaring them as regular optionals, requires any code that uses them to explicitly handle the case where they may not be set for some reason. Apple chose implicitly unwrapped as the default, but there are Swift programmers who for their own reasons, use regular optionals for #IBOutlet properties.
The whole "optional" thing messed with me bad at first. What made it "click" for me, was when I stopped thinking of those as "String" objects and started thinking of them as generics. Just like "Array" with generic identifier for "String" is an array that, if it has values, contains strings... "String?" is an Optional that, if it has a value, is a string.
String - This is always guaranteed to be some kind of string, and NEVER nil. When you declare a variable, it must be given a value.
String? - This is an optional. It can be nil, and IF it has a value, it will be a string. In order to access the optional's value, you must unwrap it.
String! - This is an optional, with syntax sugar that lets you access its value directly as if it where just a String. It COULD be nil, but for whatever reason, the context around the variable has winked at you and said "don't worry, it will have a value."
Thanks to Andrew Madsen's answer and all other answers, I learnt a bit myself:
struct pairOfOptionalAndNonOptionalAndImplicitUnwrapped{
var word1 :String?
var word2 :String
var word3: String!
init (a:String, b: String, c: String){
self.word1 = a // Line A
self.word2 = b // Line B
self.word3 = c // Line C
} //Line BBBB
func equal() ->Bool{
return word1 == word2 + word3
}
}
let wordParts = pairOfOptionalAndNonOptionalAndImplicitUnwrapped (a:"partTwo", b: "part", c:"Two")
wordParts.equal() // Line CCCC
If I comment out Line A only, I will get no errors, because it is optional, and optionals can be set to nil.
If I comment out Line B only, I will get a compilation error on Line BBBB, saying: Return from initializer without initializing all stored properties, I get this because I have declared property word2 as non-optional. I have told the compiler I have guaranteed to have you set...
If I comment out Line C only, I will not get an error, unless I actually run a method on my object. The error you could get is: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0) if and only if I run wordParts.equal(), because I told my code that it's an optional meaning that it will be set from elsewhere after instantiation/compilation. Meaning that lldb can through me a runtime error if you it didn't get set. ( the error would happen on line CCCC)
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
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
I've tried dozens of things to get this right and just can't come up with anything that works. Can anyone tell me what's wrong with the following Swift code:
let incomingRequest: CFHTTPMessageRef? = CFDictionaryGetValue(self.incomingRequests as CFMutableDictionaryRef, unsafeAddressOf(incomingFileHandle!)) as CFHTTPMessageRef
The code above gives the error message: 'UnsafePointer<Void>' is not convertible to 'CFHTTPMessageRef'
I guess what I don't understand is how do I convert an 'UnsafePointer' returned by a Core Foundation function to the pointer type it should be (i.e. CFHTTPMessageRef in this case). How do I find documentation on how to do this. I've read everything I can find, but nothing so far explains how to recast return values to what they should have been in the first place. This has to be documented somewhere, doesn't it?
EDIT
Here's the code I'm having trouble with:
let incomingRequest = CFDictionaryGetValue(self.incomingRequests as CFMutableDictionaryRef, unsafeAddressOf(incomingFileHandle!))
unsafeBitCast(incomingRequest, CFHTTPMessageRef.self)
if (incomingRequest != nil) {
let success: Boolean = CFHTTPMessageAppendBytes(incomingRequest as CFHTTPMessageRef, unsafeAddressOf(data!.bytes) as UnsafePointer<UInt8>, data!.length)
if success { // Do something... }
The CFHTTPMessageAppendBytes call still gives a "Type 'UnsafePointer' does not conform to protocol 'AnyObject'". And the following 'if' check for 'success' complains that "Type 'Boolean' doesn not conform to protocol 'BooleanType'". What the heck is that about? Boolean isn't a Boolean type?
I find the strict type checking of Swift extremely frustrating. So far it is far more difficult to code in than Obj-C, C, or any of the other languages I've used. I'm sure it's because I just don't get it, or haven't found the right documentation, but this is really driving me crazy.
Use unsafeBitCast. As following example:
import Foundation
var x = "1"
var dict : NSMutableDictionary = [x: "2"]
var y = CFDictionaryGetValue(dict as CFMutableDictionaryRef, unsafeAddressOf(x))
let str: NSString = unsafeBitCast(y, NSString.self)
str == "2"
FYI: There is one quora related with unsafeBitCast. http://www.quora.com/Why-does-Swift-not-allow-implicit-type-conversion