93

You can create a String extension like so:

extension String {
   func someFunc() -> Bool { return true }
}

but what if you want it to apply to optional string?

var optionalString: String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */

Attempting to add extension String? { produces the error:

Constrained extension must be declared on the unspecialized generic type 'Optional' with constraints specified by a 'where' clause

pkamb
  • 33,281
  • 23
  • 160
  • 191
vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • You need to unwrap it always to apply an extension – Leo Dabus Apr 05 '15 at 22:44
  • So I unwrapped it, but maybe I did that wrong? `optionalString!.someFunc()` – vol7ron Apr 05 '15 at 22:45
  • If you are sure optional won't return nil that's ok otherwise you need to use if let – Leo Dabus Apr 05 '15 at 22:46
  • It will sometimes be nil, that's why optional is used. I'm still debugging, I suspect my Playground is just out of sync. I may close this question in a second. – vol7ron Apr 05 '15 at 22:46
  • @LeonardoSavioDabus yes, that's what i was hoping to put in the extension and use the extension to solve. I don't like the nested IF-statements. My guess is eventually a future version of Swift will improve on this – vol7ron Apr 05 '15 at 22:55
  • I think you should take a look at the "??" nil coalescing operator to return an empty string "" instead of nil – Leo Dabus Apr 05 '15 at 22:59
  • @LeonardoSavioDabus that sounds promising. It's been many months since I've read the Swift guide — just now getting back into it. – vol7ron Apr 06 '15 at 00:12

9 Answers9

215

In Swift 3.1 you can add an extension to optional values as well:

extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}
Vladyslav Zavalykhatko
  • 15,202
  • 8
  • 65
  • 100
  • 1
    What does its usage look like? – shim Aug 23 '17 at 15:53
  • 3
    `if myOptionalString?.isBlank {` gives value of optional type Bool? not unwrapped still. – shim Aug 23 '17 at 15:58
  • And getting a `Type Wrapped constrained to non-protocol type String` error as well. [Here's a probably relevant discussion on Reddit](https://www.reddit.com/r/swift/comments/42wf8m/is_there_a_way_to_write_extension_for_specific/) looks like this may be fixed in the future. – shim Aug 23 '17 at 16:05
  • Is it possible to do this `where Wrapped == T`? – Declan McKenna Dec 01 '17 at 10:14
  • 7
    Usage is `myOptionalString.isBlank`. Without the `?` since you're calling it on the optional itself instead of on the `String`. – lammert Mar 23 '18 at 15:09
  • this is giving error in swift 4.1 "'Optional' is ambiguous for type lookup in this context" – Mashhadi Nov 28 '18 at 11:12
  • Another usage might be adding a label string to an optional enum. Instead of using nil-coalescing for the `nil` case, now you can provide the string value in a normal switch-case. This is helpful for Picker-like UI with an optional enum as data source. – yo1995 Jan 24 '23 at 18:27
15
extension Optional where Wrapped == String {
    var isNil: Bool {
        return self == nil
    }
}

The above answer written by @Vlad Hatko works fine, but in Swift 4 there are some issues so I changed it to this.

pkamb
  • 33,281
  • 23
  • 160
  • 191
dheeru
  • 319
  • 5
  • 6
14

You can do it like this:

protocol OptionalType { typealias A; var opt: A? { get } }
extension Optional: OptionalType { var opt: A? { return self } }

protocol StringType { var get: String { get } }
extension String: StringType { var get: String { return self } }

extension Optional where Wrapped: StringType {
  func getOrElse(s: String) -> String {
    return self.opt?.get ?? s
  }
}

And:

let optStr: String? = nil
optStr.getOrElse("hello world")

The reason that you cannot constrain Optional or String for that matter, is because they are struct. By making pseudo-protocol for each, now we can constrain as we like.

I feel like swift has given up a lot of things just to make it easier for beginners to learn or maybe the language hasn't matured enough yet.

Daniel Shin
  • 5,086
  • 2
  • 30
  • 53
13

Extensions on Optional that return a String

As of Swift 3, you cannot directly constrain an extension method to an optional String. You can achieve the equivalent result with protocols as Daniel Shin's answer explains.

You can however, create an extension method on an Optional of any type and I've found some useful methods that have a String return value. These extensions are helpful for logging values to the console. I've used asStringOrEmpty() on a String optional when I want to replace a possible nil with empty string.

extension Optional {
    func asStringOrEmpty() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return ""
        }
    }

    func asStringOrNilText() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return "(nil)"
        }
    }
}

Example Use:

var booleanValue: Bool?
var stringValue: String?
var intValue: Int?

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

booleanValue = true
stringValue = "text!"
intValue = 41

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

Console Output:

booleanValue: (nil)
stringValue: (nil)
intValue: (nil)

booleanValue: true
stringValue: text!
intValue: 41

 

Optional different than nil pointer

These extensions illustrate that an Optional is different that a nil pointer. An Optional is a enum of a specified type (Wrapped) which indicates that it does or does not contain a value. You can write an extension on the Optional "container" even though it may not contain a value.

Excerpt from Swift Optional Declaration

enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    ...
}

In code, the absence of a value is typically written using the nil literal rather than the explicit .none enumeration case.

Mobile Dan
  • 6,444
  • 1
  • 44
  • 44
8

In Swift 4.1 I was getting a Optional is ambiguous for type lookup in this context build error. To fix, you have to explicitly add the Swift namespace to the type:

extension Swift.Optional where Wrapped == String {
    var isBlank: Bool {
        return self?.isBlank ?? true
    }
}
caukajun
  • 133
  • 1
  • 7
4

Since Xcode 9.3, you can use this slight modification of @Vladyslav's answer:

extension Optional where Wrapped == String {

    var isEmpty: Bool {
        return self?.isEmpty ?? true
    }

}
zero3nna
  • 2,770
  • 30
  • 28
2

Update: For a workaround that works with Swift 2 and above, see Daniel Shin’s answer


An optional String isn't in and of itself a type, and so you cannot create an extension on an optional type. In Swift, an Optional is just an enum (plus a bit of syntactic sugar) which can either be None, or Some that wraps a value. To use your String method, you need to unwrap your optionalString. You can easily use optional chaining to achieve this:

optionalString?.someFunc()

If optionalString is not nil, someFunc will be called on it. An alternative (less concise) way of doing this is to use optional binding to establish whether or not optionalString has a value before trying to call the method:

if let string = optionalString {
    string.someFunc()    // `string` is now of type `String` (not `String?`)
}

In your example from the comments below, you needn't nest multiple if statements, you can check if the optional string is an empty string in a single if:

if optionalString?.isEmpty == true {
    doSomething()
}

This works because the expression optionalString?.isEmpty returns an optional Bool (i.e. true, false or nil). So doSomething() will only be called if optionalString is not nil, and if that string is empty.

Another alternative would be:

if let string = optionalString where string.isEmpty {
    doSomethingWithEmptyString(string)
}
Community
  • 1
  • 1
Stuart
  • 36,683
  • 19
  • 101
  • 139
  • I was trying to create an extension to avoid optional binding. It seems like that won't be possible – vol7ron Apr 05 '15 at 22:54
  • @vol7ron Can I ask why? Is the optional chaining/binding inconvenient somehow? – Stuart Apr 05 '15 at 22:58
  • Consider an optional string that you want to check if it contains a character. To my knowledge, you have to use nested if's `if let str=optString { if str.isEmpty { doSomething(); } }` I'd rather this be an `isBlank` extension of an optional string – vol7ron Apr 05 '15 at 23:00
  • My example (in comment) wasn't complete, but *isBlank* is generally *nil* or *isEmpty* results to true. – vol7ron Apr 05 '15 at 23:40
  • Right, you're coming up with ways which are not nearly as readable as other languages. `if optStr.isBlank { … }` boom. For something as new as Swift, you would think the end goal would be to be readable because readable is maintainable. – vol7ron Apr 05 '15 at 23:47
  • I could continue to suggest alternatives (a top level `func isBlank(string: String?) -> Bool` springs to mind, to take the functional approach), but ultimately you can't do exactly what you're asking. Swift doesn't currently allow extensions on generics, otherwise you could extend ` Optional`, but perhaps we will be able to in the future - I'd recommend filing a feature request. – Stuart Apr 06 '15 at 00:11
  • I think it's a matter of getting used to the syntax of the language - obviously, every language is different. Swift and its mechanism for optionals allows for compile time type safety and prevents values from unexpectedly being nil, but in exchange you are required to be explicit about handling cases where a value _might_ be nil. For me, it's a trade-off I'm happy enough to work with. I'm sure readability was one goal of the development of Swift, but I think safety was even more important. – Stuart Apr 06 '15 at 00:16
  • But if you've built that safety into an extension, you solve both cases. Functions written for the expressed condition of handling nil and non-nil allows you to detach the logic from the flow of the rest of the code. – vol7ron Apr 06 '15 at 00:35
  • @Stuart Your answer is out of date. You can indeed create an extension for a specific optional type. See Daniel Shin's answer below for an example: http://stackoverflow.com/a/33717847/39155 – Dan Loewenherz May 13 '16 at 21:08
2

You can create an optional string extension. I did the following to set an optional string to empty if it was nil :

extension Optional where Wrapped == String {

    mutating func setToEmptyIfNil() {
        guard self != nil else {
            self = ""
            return
        }
    }

}
Anil Arigela
  • 436
  • 2
  • 8
1

found some trick swift 3

class A{
    var name:String!;
    init(_ name:String?){
        self.name = name;
    }
}

extension Optional where Wrapped == String {
    func compareText(_ other:String?)->Bool{
        switch (self,other){
        case let(a?,b?):
            return a < b;
        case (nil,_):
            return true;
        default:
            return false;
        }
    }
}

let words:[A] = [A("a"),A(nil),A("b"),A("c"),A(nil)];

// let sorted = words.sorted{ 0.name.compareText($1.name) }
// trick
let sorted = words.sorted{ ($0.name as String?).compareText($1.name) }

print(sorted.map{$0.name});
john07
  • 562
  • 6
  • 16