Swift Keypath from String - swift4

Is there a way to create a Keypath from a String in Swift 4 to access a value in a struct by its path or variable name
Finally I found out that I should use CodingKeys instead of KeyPaths
to access the a value of a variable of a struct by String
Thanks in advance,
Michael

consider you have something like this,
struct foo {
var test: doo
}
struct doo {
var test: Int
}
//How to use it
let doo = Doo(test: 10)
let foo = Foo(test: doo)
let mykeyPath = \Foo.test.test
let result = foo[keyPath: mykeyPath]
print(result)

Related

Swift structs defined in separate files do they need initialisers?

I have defined a simple struct in a separate swift file as follows:
import AppKit
//Declaring a new struct for Company
public struct CompanyStruct {
var idCompany: Int
var company: String
var compType: String
}
However, when I try to use this struct it finds the struct if do:
var c = CompanyStruct
and I can select it but I get no parameters prompted when I open the bracket. If I initialise the struct as:
import AppKit
//Declaring a new struct for Company
public struct CompanyStruct {
var idCompany: Int
var company: String
var compType: String
init(idCompany: Int, company: String, compType: String) {
self.idCompany = idCompany
self.company = company
self.compType = compType
}
}
Then it works fine if I use the struct in say View Controller
I thought you did not have to initialise structs? Is it because I define the struct in an separate file?
No you don't need to add initialiser for struct. It has member wise initialiser by default. This is an xCode bug. When it happens just use YourStruct.init and it will show autocompletion. After that you can remove init part and it will work for the rest of the structs.
Structs have a default initializer hence you can create an object without passing parameters.
You can create custom initialiers which you have done.
One thing about structs is you can't create a convenience initializer.
When you use ( ) after structure name when define instance for it you are making initialization automatically.
Let companyStruct = CompanyStruct ( )
But you should give some default values in struct like;
struct CompanyStruct {
var idCompany: Int = 1
}

Swift - Declare nested variable names using dot

I'll keep it short. I'm trying to accomplish the following:
class Media {
var likes.count : Int? = 0
}
Obviously the complier throws me an error:
Consecutive declarations on a line must be separated by ';'
Is there a way to work around this? I know that i can eventually do some kind of String Replace using Mirror(reflecting:object) but i'd like to keep it efficient. Would love any help. Thanks.
UPDATE:
I wasn't clear enough, sorry. The issue is that the complier won't let me use . inside the variable declaration name.
The issue is that the complier won't let me use . inside the variable declaration name.
Exactly, a property name in Swift cannot contain the . character.
A possible approach
Now, if you want to be able to write something like this
let media = Media()
media.likes.count = 1
then you need to define your class like shown below
class Media {
class Likes {
var count = 0
}
var likes = Likes()
}
or
class Likes {
var count = 0
}
class Media {
var likes = Likes()
}
A few suggestions
PLEASE don't use implicitly unwrapped optionals like this one
var likes.count : Int! = 0
They are like a gun ready to fire and crash your entire app!
And finally the class keyword begins with a lowercase character: class not Class.
I recommend using a Struct. A Struct is basically the same as a class that is referenced by value. So you can have as many Structs as you want with their own nested variables and functions, just like a class! And the best part is, you never have to use the Struct as a functional piece of code, just as something to namespace your variables in. I do this frequently with a Constants swift file.
struct Constants {
struct MainMenu {
static var height:CGFloat = 420
static var width:CGFloat = 240
static var backgroundColor:UIColor = .red
}
struct MainViewController {
static var toolBarHeight:CGFloat = 49
static var backgroundColor:UIColor = .blue
}
}
Usage:
func getRemainingHeight() ->CGFloat {
let viewHeight = self.view.bounds.size.height
let menuHeight = Constants.MainMenu.height
let toolBarHeight = Constants.MainViewController.toolBarHeight
return viewHeight - (menuHeight + toolBarHeight)
}

Swift Conversion using a variable

Is there anyway to use conversion using a variable? I am using object stacking using type of "AnyObject" and I've been able to take the class type and populate a variable. Now I need to populate an array using conversion.
var myString = "Hello World"
var objectStack = [AnyObject]()
objectStack.append(myString)
let currentObject = String(describing: objectStack.last!)
var objectType = String()
let range: Range<String.Index> = currentObject.range(of: ":")!
objectType = currentObject.substring(to: range.lowerBound)
let range2: Range<String.Index> = objectType.range(of: ".")!
objectType = objectType.substring(from: range2.upperBound)
The code above will evaluate the class and set the value of "objectType" to "String". Now I'm trying to go the other way. Something like this:
for obj in objectStack{
obj = newObject as! objectType //this doesn't work
}
Is something like this possible?
There is a simpler, safer way to get the type:
let type = type(of: objectStack.last!) // String.Type
let typeString = String(describing: type) // "String"
The other way around is not possible because the type of the object is not known at compile time. Do you have a number of known types you want to try to cast to? In that case, use optional binding to check if the cast is successful:
let object = objectStack.last!
if let string = object as? String {
// do String stuff
}
else if let i = object as? Int {
// do Int stuff
}
// and so on
If you have a large number of possible types that share some common functionality: Use Protocols. See Swift Documentation for a nice introduction.
You define a protocol for some common functionality that different types can implement:
protocol Stackable {
func doStuff()
// (more methods or properties if necessary)
}
The protocol provides a contract that all types conforming to this protocol have to fulfill by providing implementations for all declared methods and properties. Let's create a struct that conforms to Stackable:
struct Foo: Stackable {
func doStuff() {
print("Foo is doing stuff.")
}
}
You can also extend existing types to make them conform to a protocol. Let's make String Stackable:
extension String: Stackable {
func doStuff() {
print("'\(self)' is pretending to do stuff.")
}
}
Let's try it out:
let stack: [Stackable] = [Foo(), "Cat"]
for item in stack {
item.doStuff()
}
/*
prints the following:
Foo is doing stuff.
'Cat' is pretending to do stuff.
*/
This worked for me:
var instance: AnyObject! = nil
let classInst = NSClassFromString(objectType) as! NSObject.Type
instance = classInst.init()

Swift array of nested struct type (count, repeatedValue)

Relatively new to Swift and struggling with the simplest of things. I wish to preallocate an array of structs set to default values. This works just as long as the struct is not nested inside another type. Any ideas? Here is simplified example:
struct PlainStruct
{
var yo:Float = 0.0
}
class WrapperClass
{
struct NestedStruct
{
var yo:Float = 0.0
}
}
// Works just fine
var a = [PlainStruct](count:2, repeatedValue:PlainStruct())
// Error - Cannot call value of non-function type '[WrapperClass.NestedStruct.Type]'
var b = [WrapperClass.NestedStruct](count:2, repeatedValue:WrapperClass.NestedStruct())
Thanks
It works if you create a typealias. Example:
struct PlainStruct
{
var yo:Float = 0.0
}
class WrapperClass
{
struct NestedStruct
{
var yo:Float = 0.0
}
}
typealias Nested = WrapperClass.NestedStruct
var b = [Nested](count:2, repeatedValue:WrapperClass.NestedStruct())
As for why we have to do this... I don't like to say this without proof but I think it's a compiler bug, I believe we should be able to use your original version.

convert String to AnyObject in swift

I have a static string variable
struct numb {
static var selectedNumber: String = String()
}
I am trying to unwrap ( while casting it as AnyObject) the value and assign it to messageComposeViewController
if let textMessageRecipients :AnyObject = numb.selectedNumber
{
messageComposeVC.recipients = textMessageRecipients as? [AnyObject]
messageComposeVC.body = "Testing 123!"
}
the compiler is throwing an error
bound value in a conditional binding must be of Optional type
How do I convert my string to AnyObject and assign it to the message view controller?
From your examples and the error you see, you are attempting to unwrap a value that isn't optional. You don't need to use if let when there is a value. You can force a cast using if let like this:
if let myValue:AnyObject = numb.selectedNumber as? AnyObject
This will produce a warning saying that casting a String to AnyObject will always succeed, again you don't need the if let, your casts will always succeed.
Your final example should look something like:
messageComposeVC.recipients = [numb.selectedNumber] as [AnyObject]
messageComposeVC.body = "Testing 123!"
You need to make your selectedNumber to optional like this:
struct numb {
static var selectedNumber: String?
}
Tested in Swift 2.1, Xcode 7. works !
var myItems : String?
myItems = ItemsTextfield.text
myItems as! AnyObject