Anna23
Anna23

Reputation: 700

Swift3.0 Cannot convert value of type 'ClosedRange<Index>' to type 'Range<Index>'

I'm trying to migrate the Swift 2.3 to 3.0 and post-conversion facing this issue. Any suggestion what I'm doing wrong.

Swift 3.0:

override func setValue(_ value: AnyObject?, forKey key: String) {
    let uppercasedFirstCharacter = String(key.characters.first!).uppercased()
    let range = key.startIndex...key.index(key.startIndex, offsetBy: 0)
    let selectorString = key.replacingCharacters(in: range, with: uppercasedFirstCharacter)

    let selector = NSSelectorFromString("set\(selectorString):")
    let responds = self.responds(to: selector)

    if !responds {
        return
    }

Error:

let selectorString = key.replacingCharacters(in: range, with: uppercasedFirstCharacter)

Cannot convert value of type 'ClosedRange<Index>' (aka 'ClosedRange<String.CharacterView.Index>') to expected argument type 'Range<Index>' (aka 'Range<String.CharacterView.Index>')

Original code: Swift 2.3

override func setValue(value: AnyObject?, forKey key: String) {
    let uppercasedFirstCharacter = String(key.characters.first!).uppercaseString

    let range = key.startIndex...key.startIndex.advancedBy(0)
    let selectorString = key.stringByReplacingCharactersInRange(range, withString: uppercasedFirstCharacter)

    let selector = NSSelectorFromString("set\(selectorString):")
    let responds = self.respondsToSelector(selector)

    if !responds {
        return
    }

Upvotes: 22

Views: 15359

Answers (2)

Dasoga
Dasoga

Reputation: 5695

Swift 3+

override func setValue(_ value: Any?, forKey key: String) {
        let upperCaseFirstCharacter = String(key.characters.first!).uppercased()
        let range = key.startIndex..<key.index(key.startIndex, offsetBy: 1)
        let selectorString = key.replacingCharacters(in: range, with: upperCaseFirstCharacter)

        let selector = NSSelectorFromString("set\(selectorString):")
        let responds = self.responds(to: selector)

        if !responds{
            return
        }
        super.setValue(value, forKey: key)
    }

    init(dictionary: [String: Any]){
        super.init()
        setValuesForKeys(dictionary)
    }

Upvotes: 0

dfrib
dfrib

Reputation: 73236

You can use ..< instead of ... for range to be of type Range<Index> instead of ClosedRange<Index>, in which case the call to stringByReplacingCharactersInRange(...) wont yield an error (notice the offsetBy increase by 1).

let range = key.startIndex..<key.index(key.startIndex, offsetBy: 1)
// range is now type Range<Index>

Now, I might be wrong, but it seems as if you simply want the selectorString to be the version of key with the first character uppercased. An alternative method to your range solution you can e.g. use a String extension solution as follows:

extension String { 
   var firstCharacterUppercased: String {
        guard case let c = self.characters,
                   let c1 = c.first else { return self }
        return String(c1).uppercased() + String(c.dropFirst())
    }
}

/* example usage */
let key = "fooBar"
let selectorString = key.firstCharacterUppercased

print(selectorString) // FooBar

Upvotes: 24

Related Questions