How to Add an Optional String Extension

How to add an optional string extension?

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
}
}

Optional extension for any types

The answer to your basic question is to just remove the where clause:

extension Optional { 
// ... the rest is the same
func isNil<T>(value: T) -> T {
if self != nil {
return self as! T
}
return value
}
}

Now it applies to all Optionals.

But this code is quite broken. It crashes if T is not the same as Wrapped. So you would really mean a non-generic function that works on Wrapped:

extension Optional {
func isNil(value: Wrapped) -> Wrapped {
if self != nil {
return self! // `as!` is unnecessary
}
return value
}
}

But this is just an elaborate way of saying ?? (as matt points out)

extension Optional {
func isNil(value: Wrapped) -> Wrapped { self ?? value }
}

Except that ?? is much more powerful. It includes an autoclosure that avoids evaluating the default value unless it's actually used, and which can throw. It's also much more idiomatic Swift in most cases. You can find the source on github.

public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}

But I can imagine cases where you might a method-based solution (they're weird, but maybe there are such cases). You can get that by just rewriting it as a method:

extension Optional {
public func value(or defaultValue: @autoclosure () throws -> Wrapped) rethrows -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
}

tempInt.value(or: 2)

How to declare an extension on Optional where Wrapped is a Result?

You can achieve your goals by adding the generic type constraints to newly declared generic method rather than trying to add it to the extension, which is not possible. You also need to change publisher to a function rather than computed property, since you cannot add generic type constraints to a computed property, only to a function.

extension Optional {
func publisher<Success, Failure>() -> AnyPublisher<Success, Failure> where Wrapped == Result<Success, Failure> {
switch self {
case .none:
return Empty<Success, Failure>()
.eraseToAnyPublisher()
case let .some(result):
return result.publisher
.eraseToAnyPublisher()
}
}
}

Optional extension with custom binding in SwiftUI

You may try extending Binding instead of Optional:

extension Binding where Value == String? {
var optionalBinding: Binding<String> {
.init(
get: {
self.wrappedValue ?? ""
}, set: {
self.wrappedValue = $0
}
)
}
}

And use it like this:

class ViewModel: ObservableObject {
@Published var text: String?
}

struct ContentView: View {
@ObservedObject var model = ViewModel()

var body: some View {
VStack {
TextField("Text", text: $model.text.optionalBinding)
Text(String(describing: model.text))
}
}
}

Extension for Array where Element is Optional

This is kind of tricky but you can create an AnyOptional protocol that requires an associatedtype (Wrapped) and a computed property to return the optional type. Then you can return the element unwrapped if the index is valid otherwise return nil.

protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}

extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}


extension Collection {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}


extension Collection  {
subscript(safe index: Index) -> Element.Wrapped? where Element: AnyOptional {
indices.contains(index) ? self[index].optional ?? nil : nil
}
}


var myArray: [String?] = ["2", "banana", nil, "31"]
var myStringArray: [String] = ["2", "3"]

let item = myArray[safe: 1] // item is String?
let strItem = myStringArray[safe: 99] // strItem is String?


Related Topics



Leave a reply



Submit