I've created the following extension:
import Foundation
extension Collection {
/// Returns `nil` if empty
var nonEmptyValue: Self? {
isEmpty ? nil : self
}
}
Now I'd like to make it a property wrapper so I could use it like this:
final class MyClass {
#NonEmpty
var string: String? = "test"
}
The idea is that whenever an empty string is assigned to the property, it gets replaced with nil.
Is it even possible to create such a property wrapper (since String? and String are of different type) and how would I go about it?
I'm using your extension:
import Foundation
#propertyWrapper
struct NonEmpty<T: Collection> {
var wrappedValue: T? {
didSet {
self.wrappedValue = wrappedValue?.nonEmptyValue
}
}
init(wrappedValue: T?) {
self.wrappedValue = wrappedValue?.nonEmptyValue
}
}
extension Collection {
/// Returns `nil` if empty
var nonEmptyValue: Self? {
isEmpty ? nil : self
}
}
and the result is just like image below:
Related
I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"
I have seen couple of questions but Didn't find solution and a reason
Here is struct
struct MovementFormattedData {
... Other properties ...
lazy var timeAsDate:Date? = {
return MovementFormattedData.getUTCDate(movementTime: movementTime)
}()
static func getUTCDate(movementTime:String?) -> Date? {
// return date
}
}
Now I have array
var movements :[MovementFormattedData] = []
When I try
self?.movements.firstIndex(where: {$0.timeAsDate > Date() })
I am getting
Cannot use mutating getter on immutable value: '$0' is immutable
I am not modifying $0 anywhere. I am just access the property
Please help
I think initialising timeAsDate counts as mutating so a lazy variable might not be what you need.
You could try a computed variable instead so long as you don't need to manually change it.
var timeAsDate: Date? {
return MovementFormattedData.getUTCDate(movementTime: movementTime)
}
No necessary to use lazy var
var timeAsDate: Date? {
return MovementFormattedData.getUTCDate(movementTime: movementTime)
}
lazy property is mutating getter and you cant use it with $0 because $0 is immutable
see following code block
struct MovementFormattedData {
... Other properties ...
var timeAsDate: Date? {
return MovementFormattedData.getUTCDate(movementTime: movementTime)
}
static func getUTCDate(movementTime:String?) -> Date? {
// return date
}
}
var movements: [MovementFormattedData] = []
self?.movements.firstIndex(where: { $0.timeAsDate ?? Date() > Date() })
Let's say I have two non-generic protocols (1)
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
I want to have a generic version of the second one (2)
protocol TypedStringProvider: StringProvider { // inherits from StringProvider
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
And extension with default implementation of non-generic version to have a code free conformance to StringProvider (doesn't work, pls read below) (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Now I want several classes to conform both generic TypedStringProvider and non-generic StringProvider protocols (4)
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "Some String" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
And get compiler error: Type 'UIView' does not conform to protocol 'StringProvider'.
Seemingly extension (3) doesn't work like I want because TypedStringValue is not a StringValue in despite of this constraint associatedtype TypedStringValue: StringValue from (2)
The question is how to conform to non-generic StringProvider while keeping value typed
Example:
0.5.value.lowercased()
Of course StringValue doesn't have lowercased method from String so it won't compile.
What I have tried:
First is to add untyped property to extension (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
var value: StringValue { return typedValue }
}
Doesn't work because of Invalid redeclaration of 'value' error
Second is to extend my classes and add untyped property there (5)
extension UIView {
var value: StringValue { return typedValue }
}
extension Double {
var value: StringValue { return typedValue }
}
It works without compiler errors but
No autocompletion for lowercased in our example.
With extensions (5) I need to write a lot of code for every class conforming StringProvider and every property this protocol has
Any ideas?
value is defined as type StringValue, so this is the type you should specify in your extension of TypedStringProvider in order to complete the protocol conformance:
extension TypedStringProvider {
var value: StringValue {
return typedValue
}
}
Problem
On StringProvider you are defining value to a StringValue:
protocol StringProvider {
var value: StringValue { get }
}
but here you are defining the type to TypedStringValue which is not the same as StringValue. (underlying value can be TypedStringValue, however it needs to be typecasted from StringValue to TypedStringValue when using it)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Solution
There are two solutions that I can come up for this scenario right now:
1. Generic Approach
If you want to make value generic and change the type based on TypedStringProvider you can:
protocol StringProvider {
associatedtype StringProviderValue: StringValue
var value: StringProviderValue { get }
}
Conform to the protocol by defining StringProviderValue to TypedStringValue
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
2. StringValue approach
Keep the StringProvider as it is:
protocol StringProvider {
var value: StringValue { get }
}
Conform to the protocol by using the correct StringValue type, and inside it return typedValue: TypedStringValue which can be downcasted to StringValue
extension TypedStringProvider {
var value: StringValue { return typedValue }
}
Output
Both solutions give the same output:
Found solution
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
With this extension there's no need to write similar extensions to every class and autocompletion works too.
Full code:
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
protocol TypedStringProvider: StringProvider {
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "some string" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
let doubleValue = 0.5.value.lowercased()
I have the following piece of code, the protocol MyDisplayable has three optional Strings, and I have a default implementation of the protocol via extension. My question is, since I'm sure the extension returns the three strings, is there a way I can use them as non-optional and is there any risk if some other implementation overwrites it? (see the question points 1 and 2 in code below)
Thanks a lot!
protocol MyDisplayable {
var displayName: String? { get }
var shortDescription: String? { get }
var longDescription: String? { get }
}
protocol MyObject : MyDisplayable, CustomStringConvertible {
}
extension MyObject {
var displayName: String? {
return "noname"
}
var shortDescription: String? {
return "something can't be described"
}
var longDescription: String? {
return "no way to describe it further"
}
var description: String {
// **1. is there a way to use the strings as if they are non-optional?**
// **2. is it a problem if another class implements the protocol and returns `nil` for any of the strings, but here they are force unwrapped?**
return "\(displayName!): \(shortDescription!)\n\(longDescription!)"
}
}
class Something : MyObject {
}
let something = Something()
print("Something: \(something)")
Unfortunately, it's not possible to treat a declared optional as a non-optional.
You have declared those strings as optional in your protocol, thus when you implement that protocol they stay optional.
However, you can use getter-setter to ensure that your variables always store some value even when they are declared as optional.
I'll elaborate with some code :
protocol MyDisplayable {
var displayName: String? { get set }
var shortDescription: String? { get set }
var longDescription: String? { get set }
}
protocol MyObject : MyDisplayable, CustomStringConvertible {
}
extension MyObject {
var displayName: String? {
get {
return "noname"
}
set {
newValue ?? ""
}
}
var shortDescription: String? {
get {
return "something can't be described"
}
set {
newValue ?? ""
}
}
var longDescription: String? {
get {
return "no way to describe it further"
}
set {
newValue ?? ""
}
}
var description: String {
// **1. is there a way to use the strings as if they are non-optional?**
// **2. is it a problem if another class implements the protocol and returns `nil` for any of the strings, but here they are force unwrapped?**
return "\(displayName!): \(shortDescription!)\n\(longDescription!)"
}
}
class Something : MyObject {
}
let something = Something()
print("Something: \(something)")
Now even if some other class overwrites a nil value to those strings they will return empty string "".
They will still be optional as they are declared optional, but now they will always have a non-nil value.
How about conditional unwrapping in your default implementation?
return "\(displayName ?? "" ): \(shortDescription ?? "" )\n\(longDescription ?? "")"
I got stuck by the question in swift. Suppose I have one object, how to check whether it is from struct or class in swift.
In Swift 3.0, you can call Mirror(reflecting:x).displayStyle where x is your value of interest. The result will be class, struct, enum, dictionary, set... see the documentation https://developer.apple.com/reference/swift/mirror.displaystyle
Code sample:
struct SomeStruct {
var name: String
init(name: String) {
self.name = name
}
}
var astruct = SomeStruct(name:"myname")
Mirror(reflecting:astruct).displayStyle == .struct // will be true
Mirror(reflecting:astruct).displayStyle == .class; // will be false
class MyClass {
var name:String
init(name: String) {
self.name=name
}
}
var aclass = MyClass(name:"fsdfd")
Mirror(reflecting:aclass).displayStyle == .struct // will be false
Mirror(reflecting:aclass).displayStyle == .class // will be true
Of course, it would be best handled using a switch-case statement in practice.
This approach has been working for me in Swift 3:
class TestClass { }
struct TestStruct { }
var mystery:Any
mystery = TestClass()
// Is mystery instance a class type?
print(type(of:mystery) is AnyClass ? "YES" : "NO") // prints: "YES"
mystery = TestStruct()
// Is mystery instance a class type?
print(type(of:mystery) is AnyClass ? "YES" : "NO") // prints: "NO"
Note that this approach tells you only if an instance is a class type or not. The fact that it's not a class doesn't necessarily mean it's a struct (could be an enum, closure, tuple, etc.) But for most purposes and contexts this is enough to know if you are dealing with a reference type or a value type, which is usually what is needed.
There is is operator.
if someInstance is SomeType {
// do something
}
And there is as? operator.
if let someInstance = someInstance as? SomeType {
// now someInstance is SomeType
}
In swift4, checking class or struct
class TClass {}
struct TStruct {}
func who(_ any: Any) -> String {
if Mirror(reflecting: any).displayStyle == .class {
return "Class"
} else {
return "Struct"
}
}
print(who("Hello")) // Struct
print(who(TClass())) // Class
print(who(TStruct())) // Struct
print(who(1)) // Struct
You can do this by below given way and for more information on this please follow this link.
class Shape {
class func className() -> String {
return "Shape"
}
}
class Square: Shape {
override class func className() -> String {
return "Square"
}
}
class Circle: Shape {
override class func className() -> String {
return "Circle"
}
}
func getShape() -> Shape {
return Square() // hardcoded for example
}
let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className()
A simple example for this:
var name = "Big Hero"
if name.isKindOfClass(NSString){
println("this is this class")
}else{
println("this is not this class")
}