Swift 2 Conversion Hell Part 327: How to tame NSMatchingOptions being nil? - ios

I have this method in a Regex class:
func test(input:String) -> Bool
{
let matches = expression.matchesInString(input, options: nil, range: NSMakeRange(0, count(input)))
return matches.count > 0
}
Swift 2.1 tells me:
Nil is not compatible with expected argument type 'NSMatchingOptions'
Can somebody tell me how to fix this properly? NSMatchingOptions doesn't seem to offer any default empty property.

If you do not want to pass any options to the regex, use options: [].

In Swift 2 an empty OptionSetType can be represented with <Type>()
NSMatchingOptions()
or just with a pair of square brackets
[]

NSMatchingOptions is an enum, not a class, so nil makes no sense. You have to use one of the enum values, the most likely of which is Anchored. It's really not a Swift issue at all.

Related

Creating closed Range in Swift 3 not working

Can anyone tell me why the code below works in Swift 2, but somehow breaks in Swift 3?
let range: Range = 0...2
However it can simply be fixed by doing this
let range: Range = 0..<3
Anyone knows what is the reason behind this?
Operators ... and ..< used to produce the same type, Range, in Swift 2.x. Now they produce different types (migration guide):
Range
CountableRange
ClosedRange
CountableClosedRange
Changing the type in the first assignment to ClosedRange should fix the problem. Better yet, let Swift infer the type for you:
let range = 0...2

Convert Unmanaged<AnyObject>! to Bool in Swift

I am trying to get the result of a method of an existing Objective C class, called using performSelector
let result = controlDelegate.performSelector("methodThatReturnsBOOL") as? Bool
I need to cast this result to Bool type of Swift.
The code mentioned above, gives a warning
"Cast from 'Unmanaged!' to unrelated type 'Bool' always fails"
and the result is always "false", even when the method returns YES.
Any suggestions for converting the result to Bool ?
Signature for methodThatReturnsBOOL
- (BOOL)methodThatReturnsBOOL
{
return YES;
}
It's been a long time since this has remained unanswered so I'm adding what I have learned along the way.
To convert a BOOL value returned by an Objective C method you can simply cast it using,
if let result = controlDelegate.performSelector("methodThatReturnsBOOL") {
print("true")
} else {
print("false")
}
Here you can also assign the value true/false to a Swift Bool, if required.
Note : I tried casting Unmanaged<AnyObject> directly to Bool using takeRetainedValue() as suggested by many answers on SO, but that doesn't seem to work in this scenario.
You can't do what you want nicely in Swift. My issue with the accepted solution is that it takes advantage of the idea that 0x0 just so happens to be nil. This isn't actually guaranteed by Swift and Objective-C specifications. The same applies to boolean values since 0x0 being false and 0x1 being true is just an arbitrary implementation decision. Aside from being technically incorrect, it's also just awful code to understand. Without thinking about what a nil pointer is on most platforms (32/64 bits of zeros), what was suggested makes zero sense.
After talking to an engineer at WWDC '19 for a while, he suggested that you can actually use valueFor(forKey:) with the key being the function name/selector description. This works since the Obj-C runtime will actually execute any function with the given name/key in order to evaluate the expression. This is still a bit hacky since it requires knowledge of the Objective-C runtime, however it is guaranteed to be platform and implementation independent because valueFor(forKey:) returns an Any? which can be cast into an Int or a Bool without any trouble at all. By using the built in casts instead of speculating on what 0x0 or 0x1 means, we avoid the whole issue of interpreting a nil pointer.
Example:
#objc func doThing() -> Bool{
return true
}
...
let target = someObjectWithDoThing
let selectorCallResult = target.value(forKey: "doThing")
let intResult = selectorCallResult as? Int //Optional<Int(1)>
let boolResult = selectorCallResult as? Bool //Optional<Bool(true)>
This is the solution in Swift 3, as the methods are a bit different. This method also checks if Objective-C object responds to selector, to avoid crashing.
import ObjectiveC
let selector = NSSelectorFromString("methodThatReturnsBOOL")
guard controlDelegate.responds(to: selector) else {
return
}
if let result = controlDelegate.perform(selector) {
print("true")
}
else {
print("false")
}
Similarly to my answer here this can be done with #convention(c) instead:
let selector: Selector = NSSelectorFromString("methodThatReturnsBOOL")
let methodIMP: IMP! = controlDelegate.method(for: selector)
let boolResult: Bool = unsafeBitCast(methodIMP,to:(#convention(c)(Any?,Selector)->Bool).self)(controlDelegate,selector)
This^ particular syntax is available since Swift 3.1, also possible with one extra variable in Swift 3.
More compact cast to bool:
let result = controlDelegate.perform(NSSelectorFromString("methodThatReturnsBOOL")) != nil

Why is Apple using this "if let" code? [duplicate]

This question already has answers here:
Why would I use if and let together, instead of just checking if the original variable is nil? (Swift)
(2 answers)
Closed 7 years ago.
Apple has this segment of code on one of their sample projects:
let existingImage = cache.objectForKey(documentIdentifier) as? UIImage
if let existingImage = existingImage where cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage
}
why is apple using this if let? Isn't more logical to simply use
if cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage!
}
???!!
If you use
let existingImage = cache.objectForKey(documentIdentifier) as? UIImage
if let existingImage = existingImage where cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage
}
This will make sure that if existingImage == nil,it will not
execute return existingImage.
Besides,if let also unwrap existingImage from UIImage? to
UIImage
As Abhinav mentioned above, Apple introduced a new type called optional type with Swift.
What does optional mean?
Short and Sweet, "Optional types are types, which can contain a value of a particular data type or nil".
You can read more about optionals and their advantages here : swift-optionals-made-simple
Now whenever you want to make use of value present in an optional type, first you need to check what it contains i.e. does it contains a proper value or it contains nil. This process is called optional unwrapping.
Now there are two types of unwrapping,
Forced unwrapping : If you're sure that an optional will have an value all the time, you can then unwrap the value present in the optional type using "!" mark. This is force unwrapping.
The one more way is to use if let expression, this is safe unwrapping, here you'll check in your program that, if optional has a value you will do something with it; if it doesn't contain value you'd do something else. A simple example is this (You can test this in play ground:
func printUnwrappedOptional (opt:String?) {
if let optionalValue = opt { //here we try to assign opt value to optionalValue constant, if assignment is successful control enters if block
println(optionalValue) // This will be executed only if optionalValue had some value
}
else {
println("nil")
}}
var str1:String? = "Hello World" //Declaring an optional type of string and assigning it with a value
var str2:String? //Declaring an optional type of string and not assigning any value, it defaults to nil
printUnwrappedOptional(str1) // prints "Hello World"
printUnwrappedOptional(str2) // prints "nil"
Hope this clears your question, read through the link given above it'll be more clear to you. Hope this helps. :)
Edit: In Swift 2.0, Apple introduced "guard" statements, once you're good with optionals go through this link, guard statement in swift 2. This is another way to deal with optionals.
Using if let, makes sure that the object (existingImage) is not nil, and it unwraps it automatically, so you are sure inside the if that the condition is true, and the object is not nil, and you can use it without unwrap it !
With Swift, Apple has introduced a new concept/type - Optional Type. I think you better go through Apple Documentation.
Swift also introduces optional types, which handle the absence of a
value. Optionals say either “there is a value, and it equals x” or
“there isn’t a value at all”. Optionals are similar to using nil with
pointers in Objective-C, but they work for any type, not just classes.
Optionals are safer and more expressive than nil pointers in
Objective-C and are at the heart of many of Swift’s most powerful
features.
existingImage is an optional (as? UIImage) and therefor needs to be unwrapped before used, otherwise there would be a compiler error. What you are doing is called forced unwrapping via !. Your program will crash, if existingImage == nil and is therefor only viable, if you are absolutely sure, that existingImage can't be nil
if let and optional types is more help where is there is changes to get nil values to void crashes and unwanted code executions.
In Swift 2.0,
guard
will help us lot where our intention is clear not to execute the rest of the code if that particular condition is not satisfied

Cannot subscript a value of type [String: AnyObject]? with an index of type String

I know have many the same question but still cannot find the way to fix my error. Please see the image for more detail. I used Xcode 7 and swift 2.0
Edit: fcking the warning of Swift. finnaly (change?[NSKeyValueChangeNewKey]?.boolValue)! fixed the error
change is an optional. Either unwrap the optional
let isCaptureStillImage = change![NSKeyValueChangeNewKey]!.boolValue
or use optional bindings
if let changeNewKey = change?[NSKeyValueChangeNewKey] {
let isCaptureStillImage = changeNewKey.boolValue
...

Swift 1.2 NSTextStorage removeAttribute with Range<String.Index>

I have a subclass of NSTextStorage and I'm trying to remove the foreground color of a paragraph the following way:
var paragraphRange = self.string.paragraphRangeForRange(
advance(self.string.startIndex, theRange.location)..advance(self.string.startIndex, theRange.location + theRange.length))
self.removeAttribute(NSForegroundColorAttributeName, range: paragraphRange)
However, I get the following error Cannot invoke 'removeAttribute' with an argument list of type '(String, range: (Range<String.Index>))'
Help Please. I think TextKit on Swift is a mess. Some methods receive/return NSRange but String works with Range<String.Index> making it a hell to work with.
The problem here is that the NSString returned by self.string
is automatically bridged to a Swift String. A possible solution is
to convert it back to NSString explicitly:
func removeColorForRange(theRange : NSRange) {
let paragraphRange = (self.string as NSString).paragraphRangeForRange(theRange)
self.removeAttribute(NSForegroundColorAttributeName, range: paragraphRange)
}
Note also that the range operator .. has been replaced by ..<
in newer Swift versions (to avoid confusion with ... and to
emphasize that the upper bound is not included).

Resources