How Swift Vars automatically convert to My Any Type - swift

I was doing some research on AnyXXX struct like AnyHashable, then I write a similar container named MyAnyHashable.
There's some issues
when I using the original AnyHashalbe is Swift, it works fine:
func usingAppleAnyHashable() {
var dict = Dictionary<AnyHashable, Any>()
// using literal as key always works
dict[1] = 1
dict["efg"] = "efg_value"
// using some var as key still works
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
print(dict)
}
it can automatically convert Int's var into AnyHashable, what a Magic!
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
Then, I using MyAnyHashable as the Key's Type
func usingMyAnyHashable() {
var dict = Dictionary<MyAnyHashable, Any>()
// using literal as key always works
dict[1] = 1
dict["efg"] = "efg_value"
// it show errors: Cannot convert value of type 'Int' to expected argument type 'MyAnyHashable'...
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
print(dict)
}
I must write like this to manually create a MyAnyHashable instance
dict[MyAnyHashable(any1)] = 100
What is the Magic in Apple's AnyHashable?
How to make it automatically convert non-Literal var into MyAnyHashable?
Here is MyAnyHashable implementation, I even wrote a ExpressibleByXXXXLiteral which can automatically transform literalType to MyAnyHashable.
struct MyAnyHashable : Hashable, Equatable {
private let baseHash: Int
private let base: Any
init<H: Hashable>(_ base: H) {
self.base = base
self.baseHash = base.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.baseHash)
}
static func == (lhs: MyAnyHashable, rhs: MyAnyHashable) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
extension MyAnyHashable : ExpressibleByStringLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
typealias StringLiteralType = String
init(stringLiteral value: StringLiteralType) {
self.init(value)
}
typealias IntegerLiteralType = Int
init(integerLiteral value: IntegerLiteralType) {
self.init(value)
}
typealias FloatLiteralType = Float
init(floatLiteral value: FloatLiteralType) {
self.init(value)
}
}
screenshots

Related

Swift - Return type of struct parameters without default values

I'm very new in Swift so i might be missing some basics.
I have struct:
struct MyStruct {
var a: Int
var b: String
var c: Bool
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
}
and function, that should iterate through given struct properties and return their types in json:
func structProps(){
let elm = MyStruct()
let mirror = Mirror(reflecting: elm)
var exampleDict: [String: String] = [:]
for child in mirror.children {
exampleDict[child.label!] = String(describing:type(of: child.value)) as String
}
if let theJSONData = try? JSONSerialization.data(
withJSONObject: exampleDict,
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
}
}
it kinda return what i need:
JSON string = {"a":"Int","b":"String","c":"Bool"}
Because i'm having a lot of structs and i want to export json from all of them, i'm wondering if there is a way to have generic initializer. Without passing default values.
It means without
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
If I understand correctly , you can create a base protocol and add as an extension to your structures like
protocol BaseFunction {
func getElements() -> [String : String]
func getDict() -> String
}
extension BaseFunction {
func getElements() -> [String : String] {
let mirror = Mirror(reflecting: self)
let propertiesRemoveNil = mirror.children.filter({!(($0.value as AnyObject) is NSNull)})
let properties = propertiesRemoveNil.compactMap({$0.label})
var types = [String]()
_ = mirror.children.forEach({
types.append(String(describing:type(of: $0.value)))
})
return Dictionary(uniqueKeysWithValues: zip(properties, types))
}
func getDict() -> String{
if let theJSONData = try? JSONSerialization.data(
withJSONObject: getElements(),
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
return theJSONText ?? ""
}
return ""
}
}
And usage :
func structProps(){
let elm = MyStruct()
print(elm.getDict())
}
OUTPUT :
{"a":"Int","b":"String","c":"Bool"}
I was going to write a comment about using a protocol but I thought it would be easier to understand as an answer with some code.
To make the usage more generic so you don't need specific code for each type of struct you should use a protocol. Instead of having an init that might clash with already existing init in the struct I prefer a static method that returns a new object, also known as a factory method
protocol PropertyExtract {
static func createEmpty() -> PropertyExtract
}
Then we can make use of the default init for the struct or any supplied to create an object with some initial values by letting the struct conform to the protocol in an extension
extension MyStruct: PropertyExtract {
static func createEmpty() -> PropertyExtract {
MyStruct(a: 0, b: "", c: false)
}
}
And instead of hardcoding or passing a specific type of object to the encoding function we pass the type of it
func structProps(for structType: PropertyExtract.Type)
and use the protocol method to get an instance of the type
let object = structType.createEmpty()
The whole function (with some additional changes)
func structProps(for structType: PropertyExtract.Type) -> String? {
let object = structType.createEmpty()
let mirror = Mirror(reflecting: object)
let exampleDict = mirror.children.reduce(into: [String:String]()) {
guard let label = $1.label else { return }
$0[label] = String(describing:type(of: $1.value))
}
if let data = try? JSONEncoder().encode(exampleDict) {
return String(data: data, encoding: .utf8)
}
return nil
}
And this is then called with the type of the struct
let jsonString = structProps(for: MyStruct.self)
According to Creating a Swift Runtime Library, there is a way to access meta data types without initializer.
Solution for what i was asking for is possible with Runtime library.
Mirror(reflecting:) expects an instance of a type and not a type itself, ref.
One idea is to have a generic function that works with all types conforming to a protocol that provides an empty init. Something like:
protocol EmptyInitializable {
init()
}
struct StructOne {
let a: Bool
let b: String
}
struct StructTwo {
let c: Int
let d: Float
}
extension StructOne: EmptyInitializable {
init() {
a = false
b = ""
}
}
extension StructTwo: EmptyInitializable {
init() {
c = 1
d = 1.0
}
}
func print(subject: EmptyInitializable) -> String? {
let dictionary = Dictionary(uniqueKeysWithValues:
Mirror(reflecting: subject).children.map {
($0.label!, String(describing: type(of: $0.value)))
}
)
return (try? JSONSerialization.data(withJSONObject: dictionary)).flatMap {
String(data: $0, encoding: .utf8)
}
}
print(subject: StructOne()) // "{"a":"Bool","b":"String"}"
print(subject: StructTwo()) // "{"d":"Float","c":"Int"}"
You still have to implement the empty init and assign some values and I'm afraid there's no way currently to avoid that.

guard let number as NSString and NSNumber

I am getting data from different sources, the variable could be a number or a string of number. How do I make sure that "(number as? NSString)" or "(number as? NSNumber)" always success? Something similar to Java optInt, which will never fail even if the number is a String. See example below:
func testNumber()
{
var number = 123
guard let a = (number as? NSNumber)?.intValue else { print("1");return; }
}
func testNumberString()
{
var number = "123"
guard let a = (number as? NSNumber)?.intValue else { print("2");return; } // this failed.
}
func testNumberToString()
{
var number = 123
guard let a = (number as? NSString)?.intValue else { print("2");return; } // this sometimes failed too depend on datasource.
}
As I understand from your question, you want an integer value at the end, no matter if the input type is string or integer.
You can achieve this by using ExpressibleByStringLiteral.
Here is the demo
extension Int: ExpressibleByStringLiteral {
public typealias StringLiteralType = String
public init(stringLiteral value: StringLiteralType) {
self = Int(value) ?? 0
}
}
This Int extension allows you to accept string value as Int and return int value. If it did not convert it will give you 0 by default.
Example
func testInt() {
let numberOne: Int = "5656"
let numberTwo: Int = 1234
print(numberOne)
print(numberTwo)
}
Or another way is to create your own ExpressibleByStringLiteral, which helps you to give default value as you want.
struct StringInt: ExpressibleByStringLiteral {
var value: Int?
init(stringLiteral value: String) {
self.value = Int("\(value)")
}
func wrapped(with defaultValue: Int) -> Int {
return self.value ?? defaultValue
}
}
Example
func testInt() {
var numberThree: StringInt = "5656"
print(numberThree.value as Any) // with nil or optional value
numberThree = "asf"
print(numberThree.wrapped(with: 15)) // with default value
/**
Output
Optional(5656)
15
*/
}

Init a FloatingPoint from a string

I am trying to convert a 2d array of Strings into my custom generic type Matrix:
func convert(_ arr: [[String]]) -> Matrix<Element> {
var m: Matrix<Element> = Matrix()
for row in arr {
var v: [Element] = []
for e in row {
let convE: Element = Element(string: e) // right here I'd like to implement something like that: Element(string: e)
v.append(convE)
}
m.vectors.append(Vector(v))
}
return m
}
The Matrix.Element does conform to the FloatingPoint protocol. Please tell me if you wish to see the Matrix struct but I think I haven't implemented anything that's important for this question other than that the generic Element type of Matrix does conform to the FloatingPoint protocol.
My problem is I want Element to be something like Float, Double... (any of the FloatingPoint types) but how can I initialize a FloatingPoint from a string? I tried:
extension FloatingPoint {
init(string: String) {
self.init(Int(string)!)
}
}
which obviously only works for strings like "1", "2"... and not "1.2", "3.541" and so on which I want.
Edit:(#Leo Dabus)
protocol DArray: Sequence {
associatedtype Component: FloatingPoint
}
extension DArray {
static func * <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? rhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? rhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(*).reduce(0, +) })
}
static func / <T: DArray>(lhs: Self, rhs: T) -> Vector<Component> {
let v = lhs as? Vector<Component> ?? lhs as! Vector<Component>
let m = lhs as? Matrix<Component> ?? lhs as! Matrix<Component>
return Vector(m.map { zip(v, $0).map(/).reduce(0, +) })
}
}
struct Vector<Component: FloatingPoint>: DArray {
var components: [Component]
var count: Int {
return components.count
}
init(_ Components: [Component] = []) {
self.components = Components
}
subscript(i: Int) -> Component {
get {
return components[i]
} set {
components[i] = newValue
}
}
static func + (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(+))
}
static func - (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(-))
}
static func * (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(*))
}
static func / (lhs: Self, rhs: Self) -> Self {
return Vector(zip(lhs, rhs).map(/))
}
func empty(of length: Int) -> Self {
return Vector(Array(repeating: 0, count: length))
}
}
struct Matrix<Component: FloatingPoint>: DArray {
var vectors: [Vector<Component>]
var nRows: Int {
return vectors.count
}
var nColumns: Int {
guard !vectors.isEmpty else { return 0 }
return vectors[0].count
}
var count: Int {
return vectors.count
}
init(_ vectors: [Vector<Component>] = []) {
self.vectors = vectors
}
subscript(r: Int) -> Vector<Component> {
get {
return vectors[r]
}
set {
vectors[r] = newValue
}
}
subscript(r: Int, c: Int) -> Component {
get {
return vectors[r][c]
}
set {
vectors[r][c] = newValue
}
}
}
Additionally I have my two structs conform to the Sequence protocol.
(Note: I am the OP)
What I came up with now is:
extension FloatingPoint {
public init?(string: String) {
if Self.self == Double.self {
self = Double(string) as! Self
} else if Self.self == Float.self {
self = Float(string) as! Self
} else if Self.self == Float80.self {
self = Float80(string) as! Self
} else {
return nil
}
}
}
It works for my use case but I was wondering whether it is a good way of achieving what I am looking for. So I'd be happy for someone to evaluate my solution. (#Leo Dabus)
You can extend FloatingPoint protocol and constrain the generic type to LosslessStringConvertible:
extension StringProtocol {
func floatingPoint<T: FloatingPoint>() -> T? where T: LosslessStringConvertible {
T(String(self))
}
}
Note that CGFloat does NOT conform to LosslessStringConvertible so you would need to implement a custom String initializer:
extension CGFloat: LosslessStringConvertible {
private static let formatter = NumberFormatter()
public init?(_ description: String) {
guard let number = CGFloat.formatter.number(from: description) as? CGFloat else { return nil }
self = number
}
}
let double: Double? = "2.7".floatingPoint() // 2.7
let float: Float? = "2.7".floatingPoint() // 2.7
let float80: Float80? = "2.7".floatingPoint() // 2.7
let cgfloat: CGFloat? = "2.7".floatingPoint() // 2.7
There is already an initializer for FloatingPoint types but to make your code work you need to conform your Matrix Component to LosslessStringConvertible.
Can you try something like this:
extension FloatingPoint where Self == Double {
init(string: String) {
self.init(Double(string)!)
}
}
extension FloatingPoint where Self == Float {
init(string: String) {
self.init(Float(string)!)
}
}
this should work as well
extension FloatingPoint {
init(string: String) {
self.init(Self(string)!)
}
}

How to overcome the error of "Generic parameter 'T' is not used in function signature"?

I'm trying to convert the following to be generic.
extension RLMOrganization: DataProvider {
func getLastSyncToken() -> String {
let lastUpdated: RLMOrganization? = self.findAll(sortedBy: "syncToken").last
if let syncToken = lastUpdated?.syncToken {
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
}
And have tried this:
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll<T: Object>(sortedBy key: String) -> [T]
}
extension DataProvider {
func findAll<T: Object>(sortedBy key: String) -> [T] {
let database = self.getDatabase()
if let allObjects = database?.objects(T.self) {
let results = allObjects.sorted(byKeyPath: key, ascending: true)
return Array(results)
}
return []
}
func getLastSyncToken<T: Object>() -> String {
let lastUpdated = self.findAll(sortedBy: "syncToken").last as? T
if let value = lastUpdated?.value(forKey: "syncToken") { // get value from object by string name
let syncToken = value as! String
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
...
But can't seem to overcome the error of:
Generic parameter 'T' is not used in function signature
I would think the compiler has everything it needs to determine type usage.
Below works for me, I don't know how findAll is defined but the problem is the reference to self as I see it so you need to define T there using associatedtype.
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll(sortedBy: String) -> T?
}

Swift dynamically cast nil to optional

The following code works:
nil as! String?
"muffin" as! String?
Therefore I also expect this to work:
var magicArray: [Any?] = ["Muffin", nil, 3]
class Box<T> {
var index: Int
init(index: Int){
self.index = index
}
func get() -> T {
return magicArray[index] as! T //crash
}
}
But neither
let box = Box<String?>(index: 0)
box.get()
nor
let box = Box<String?>(index: 1)
box.get()
works as expected. The program crashes at the cast in get(). This however works:
let box = Box<Int>(index: 2)
box.get()
I need to be able to cast a Any? value to T inside my class where T can be any type, including optionals. The Any? actually comes from an array of Any? so there is no other way of verifying it’s of the correct type T.
Is this possible?
If you need a generic wrapper, this should work for you:
final class Wrapper<T> {
final let wrappedValue: T
init(theValue: T) {
wrappedValue = theValue
}
}
You can replace let with var to make it mutable.
Edit:
This is the code I used to test it on a playground.
var s : Any?
s = "some string"
let w = Wrapper(theValue: s)
w.wrappedValue
let int = 3
let w2 = Wrapper(theValue: int)
w2.wrappedValue
w2.wrappedValue = 3
let array : [Any?] = [s, int, nil, "abc", NSObject()]
for obj in array {
let wrapper = Wrapper(theValue: obj)
}