How do I set a conditional breakpoint based on a generic type? - swift

Is there any way to set a breakpoint as pictured below?
I'd like the breakpoint to trigger when Element is type ProjectEntity, but I get the following error:
Stopped due to an error evaluating condition of breakpoint 13.1: "Element == ProjectEntity"
Couldn't parse conditional expression:
error: <EXPR>:3:1: error: use of unresolved identifier 'Element'
Element == ProjectEntity
^~~~~~~
I also can't po Element when that breakpoint is hit. Any idea why?

Normally, in swift code, you can add .self after a type to get the Type object that represents the type.
So,
Element.self == ProjectEntity.self
However, the runtime does not seem to recognise any generic type parameters, so I suppose you cannot check for such a condition at runtime.
This means that you must get something that is of type Element and check that against ProjectEntity with is.
someElement is ProjectEntity

I ended up creating a local variable that assigned to the generic type, and then calling type(of:) on that and checking it against the type I wanted to break on.
let e = Element.self
let isProject = type(of: e) == ProjectEntity.Type.self
Then I made the condition for the breakpoint isProject.

Related

if statements and optionals in Swift

Is there a difference between something like
if let error = error {
print(error.localizedDescription)
}
and just checking if it is = nil
if error != nil {
print(error.localizedDescription)
}
if I want to check if error has a value? Think of firebase's creating user function.
Yes.
The if let statement allows you to bind the value of error to a variable if it is non-nil and use it in the block. If maybeError is of type Error?, when you do:
if let error = maybeError {
/* block contents */
}
the type of error will be Error within the block - ie: It won't be an optional anymore. If you just do a nil-check using if, error will still be of type Error? within the block. So the code that would actually be equivalent to your first snippet would be:
if error != nil {
print(error!.localizedDescription)
}
(Your second snippet, as it is, won't compile, as you're trying to get the localizedDescription variable of an Error? object, which has no such property)
By the way, in case you haven't seen it before, the !. thing is the unwrap operator. It runs the method on the object if the object is non-nil, but it crashes in case the object is nil. In this case, you generally know it won't crash. (But this might not actually be safe depending on where and how you use it - check #rmaddy's comment)
In the first, error is now a non-optional type, so you can use .. This is also idiomatic -- it more clearly shows what you are trying to do.
In the second, you would need to use ?. to look at error's properties (your code won't compile because you haven't done that).

How to use max with swift3?

i am getting an error for the bellow code:
var bubbleWidth:CGFloat!
bubbleWidth:CGFloat = max( CGFloat(15) , bubbleWidth )
here is the error message:
no candidates produce the expected contextual result type 'cgfloat!'
this code was working without any problem on swift 2 , i don't know why i am getting that error now !
EDIT:
here is my real code:
var bubbleWidth:CGFloat!
bubbleWidth = imageView.frame.width + 11
bubbleWidth = max( CGFloat(15) ,
bubbleWidth )
and here is the error that i am receving:
Edit:
please note: i don't want to assign value to bubbleWidth, like that
var bubbleWidth:CGFloat = -1
Thanks
This is a consequence of SE-0054 Abolish ImplicitlyUnwrappedOptional type which has been implemented in Swift 3:
However, the appearance of ! at the end of a property or variable declaration's type no longer indicates that the declaration has IUO type; rather, it indicates that (1) the declaration has optional type, and (2) the declaration has an attribute indicating that its value may be implicitly forced. ...
If the expression can be explicitly type checked with a strong optional type, it will be. However, the type checker will fall back to forcing the optional if necessary.
Now one could argue that the compiler should fall back to unwrapping
the optional in
bubbleWidth = max(CGFloat(15), bubbleWidth)
but for some reason that works only with a float literal
bubbleWidth = max(15, bubbleWidth)
and I am not sure if this is a bug or not. Alternatively, unwrap the value explicitly
bubbleWidth = max(CGFloat(15), bubbleWidth!)
or – better – provide a default value with the nil-coalescing operator ??:
bubbleWidth = max(CGFloat(15), bubbleWidth ?? 0)

Swift Compiler Error,Expression type 'Error' is ambiguous without more context

typealias SwiftAMapCompletion = (CLLocation?,AMapLocationReGeocode?,Error) -> Void
var locationResult : SwiftAMapCompletion?
I want to give a nil as Error, but "Swift Compiler Error" is
Expression type 'Error' is ambiguous without more context.(SwiftAMapCompletion can't change)
locationResult!(location, reGeocode, nil as! Error)
You cannot force nil to be an Error, not even if you use as!.
Your options are:
Change the declaration to be Error?, because that would mean that you can pass nil.
Pass an Error.
For example:
enum MyError: Error {
case ok
}
locationResult!(location, reGeocode, MyError.ok)
In my opinion, your SwiftAMapCompletion interface does not make any sense, because normally a callback like this would be "here is the result, or here is the error" so all parameters should be declared as optional (with ?). I would get this interface changed if you can.

Converting a string to a class in Swift

I am using Alamofire to make a GET request and am using the ObjectMapper library to convert the response into its own class in Swift.
Alamofire.request(self.REST_METHOD!, self.REQUEST_URL, headers : ["Authentication_Token" : authToken]).responseJSON { response in
if response.response != nil && response.response?.statusCode == 200 {
let json = JSON((response.result.value as? NSDictionary)!)
let classType : AnyObject.Type = NSClassFromString(entityType)! as AnyObject.Type
//let model = Mapper<classType>.map(json.rawString())
}
}
The entityType variable can be one of many types i.e. User, Student, Teacher, Staff, etc. I am trying to dynamically create a class type and create the model based on that.
However it crashes on the line let classType : AnyObject.Type = NSClassFromString(entityType)! as AnyObject.Type giving the error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
Also when I uncomment the line let model = Mapper<classType>.map(json.rawString()), it gives me a compiler error:
classType is not a type
What is wrong with the above code
You're getting the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Because NSClassFromString is failing and returning nil, which then you are unwrapping, thereby causing the error.
I'm guessing that entityType contains a class name along the lines of myClass. Swift now uses namespaces, so to create a class from a string, the string must contain AppName.myClass.
You could either hardcode your app name, or use the following code to get it:
NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
There are other problems with your code too. When NSClassFromString succeeds, it will create an instance of the class. This cannot be cast to a type. Also, you can't pass a variable as a generic in Mapper<classType>, as Swift needs to know the class type at compile time. Instead you could change the Mapper class to take the type as a parameter. For example:
Mapper.map(classInstance.dynamicType, json.rawString())
Although now that I re-read your question, you're using a library for Mapper, and are therefore probably reluctant to change it.
And looking at the doco for ObjectMapper, it needs you to create a Mapper instance - ie. instead of Mapper<MyClass>.map you need Mapper<MyClass>().map.

Swift 2.0 new 'perform changes' of PHPPhotoLibrary won't work

I just downloaded Xcode 7 Beta 2 and am trying to use my knowledge of Swift to make an app that deletes a photo from the user's camera roll. I know how to do this normally with Swift 1.2, but I can't seem to get it in Swift 2.0. I tried searching through the documentation to learn how to use the 'performChange' function in Swift 2.0, but it won't work. Here's my Swift :
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
}, completionHandler: { (success, error) -> Void in
NSLog("Finished deleting asset. %#", (success ? "Success" : error))
})
Here's my error:
Cannot invoke performChanges with an argument list of type (() -> _, completionHandler: (_, _) -> Void)
Any help is appreciated!!
The Swift compiler generally has issues with reporting the correct root cause of a type-checking failure in a complex expression. Rather than simply show you the correct form of this code, I'll walk through how I found the way there, so you can reuse that process for future debugging. (Skip down for the TLDR if you must.)
First, you've got an error of the form:
Cannot invoke 'function' with an argument list of type 'params'
That means that something about your function call has failed to type-check. Because you're calling a function where the parameters include closures, you'll need to look at the behavior of the closures to narrow down the type-checking issues.
Let's start by making the closures explicitly return Void:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
return
}, completionHandler: { (success, error) -> Void in
NSLog("Finished deleting asset. %#", (success ? "Success" : error))
return
})
What's going on here? You've already declared the type of the completion handler as returning Void, so why the extra return statement? Swift type checking works both bottom-up and top-down, and if there's an error along either way, it can't make assumptions about the other. Here, you have two single-expression closure, so Swift has to consider that each one's single expression could be an implicit return statement.
If one of those statements has a type-checking error, that statement's return type becomes <<error type>>, and because it's a single-statement closure, the closure's return type becomes <<error type>>, and therefore the function call to which the closure is a parameter fails, because the function is expecting a closure that returns Void, not a closure that returns <<error type>>.
Indeed that's what's happening — once we make the above change, we get a different error, on the NSLog statement (with "Success" highlighted):
'_' is not convertible to 'StringLiteralConvertible'
That's a bit unclear still, but we're closer to the root of the problem. If you replace the (success ? "Success" : error) part of the log statement with something static (say, just "Success"), it compiles. So, let's separate and dissect that ternary operation to see what's going wrong inside it.
let successString = "Success"
let report = (success ? successString : error)
NSLog("Finished deleting asset. %#", report)
This gets us a new error, on the ? of the ternary operator:
'NSString' is not a subtype of 'NSError'
Huh? Something to do with automatic conversion of Swift.String to NSString, maybe? Let's make that conversion explicit to be sure:
let successString = "Success" as NSString
let report = (success ? successString : error)
type of expression is ambiguous without more context
Now we reach the crux of the matter. What, indeed, is the type of report supposed to be? If success is true, it's NSString, but if false, it's NSError? (the type of error, inferred from the declaration of performChanges(_:completionHandler:)). This sort of type hand-waviness will fly in C, but Swift is much more strict about such things. (Someone very wise once said, "Incomplete type specification leads to unclear memory layout, unclear memory layout leads to undefined behavior, undefined behavior leads to suffering." Or something like that.)
The only supertype of both NSString and NSError? is Any, and Swift is reluctant to infer that type. (Because if you infer that everything can be anything, your type information is worthless.) And if you try to use that type manually, you get errors when you try to pass it to NSLog:
let report: Any = (success ? successString : error)
NSLog("Finished deleting asset. %#", report)
cannot invoke 'NSLog' with an argument list of type '(String, Any)'
expected an argument list of type '(String, [CVarArgType])'
Those errors take us off down the rabbit hole of C vararg functions, so let's step back a bit — what type does NSLog really want for this argument? NSLog is an ObjC function, with format string substitution (the %# business) built on NSString stringWithFormat. Per the docs, when you use a %# token, NSString looks for an object in the corresponding parameter and calls its description method. Because that implementation is ObjC and part of the Cocoa frameworks (dating back to before the dinosaurs were wiped out), not a Swift thing, it stands to reason that a pure-Swift type like Any won't work here.
NSObject would be a good Cocoa type to pass to the NSLog function. However, declaring that as the type of report won't fly, either — you can't implicitly convert both branches of the ternary operator to NSObject. The error parameter is an optional — its inferred type is NSError?, remember? And that's a Swift type, not a Cocoa type.
So that's the final problem — you have a ternary operator that's trying to have one branch be a perfectly sensible object, and the other branch a still-wrapped optional. Indeed, force-unwrapping the optional clears all the compiler errors:
let report = (success ? successString : error!) // report now type-infers NSObject
NSLog("Finished deleting asset. %#", report)
Now that we've fixed everything, we can put the wheels back on and collapse everything back down...
TLDR: Unwrap your optionals.
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(arrayToDelete)
}, completionHandler: { success, error in
NSLog("Finished deleting asset. %#", (success ? "Success" : error!))
})
We know it's safe to force-unwrap here because of the API contract: if success is true, error will be nil, but if success is false, there will actually be an error.
(This may have even worked for you without unwrapping on previous SDK versions because the closure type in the performChanges(_:completionHandler:) would have used an implicitly-unwrapped optional before Apple audited all their APIs for nullability.)