Convenience inits in class hierarchy cause infinite recursion - swift

I have a hierarchy of 2 classes in Swift 2.0. Both classes can be instantiated by passing the arguments or by passing a JSON dictionary ([String: AnyObject]) which contains the arguments.
The inits that directly take the arguments are designated inits, while those that take the JSON are convenience inits with the same signature.
class Thing {
let name : String
init(name: String){
self.name = name
}
convenience init(jsonNamed: String){
// read the json, parse and extract
// the name
self.init(name: "got this from JSON")
}
}
class SubThing : Thing{
var surname : String
init(name: String, surname: String){
self.surname = surname
super.init(name: name)
}
convenience init(jsonNamed: String){
self.init(jsonNamed: "got this from JSON")
// extract surname
self.surname = "Got this from json"
}
}
The convenience init in SubThing is not allowed to call the same init in super, and if I call it in self, it will cause an infinite recursion, as both methods have the same signature.
If I make both json inits designated ones, I can't call self.init(name:) in Thing, and I would have to repeat the same code in both initialisers in Thing.
What's the best way to get around this kind of situation?

You're doing a loop in SubThing calling the same designated initializer from itself. I see it rewritten this way:
class Thing {
private var name : String?
private init() {}
convenience init(name: String){
self.init()
self.name = name
}
convenience init(jsonNamed: String){
self.init()
self.name = getNameFromJSON()
}
private func getNameFromJSON() -> String {
// read the json, parse and extract
// the name
return "got this from JSON"
}
}
class SubThing : Thing {
private var surname : String?
convenience init(name: String, surname: String){
self.init(name: name)
self.surname = surname
}
convenience init(jsonNamed: String){
self.init()
self.name = getNameFromJSON()
// extract surname
self.surname = "Got this from json"
}
}
Tested and working. UPDATE: added private inits, so it couldn't be initialized empty.

Related

How to decode a JSON string according to the type of received argument?

I'm having this snippet of code (specially crafted for this question):
let data = "[{\"name\": \"Smith\", \"surname\": \"John\"}]".data(using: .utf8)!
class BaseClass: Decodable {
var name: String?
}
class ExtraClass: BaseClass {
var surname: String?
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
init(name: String, surname: String) {
super.init()
self.name = name
self.surname = surname
}
}
func mainFunction<T: BaseClass>(_ obj: T) {
var results = try! JSONDecoder().decode([T].self, from: data)
results.append(obj)
print(results)
}
func proxyFunction(_ obj: BaseClass) {
mainFunction(obj)
}
proxyFunction(ExtraClass(name: "Sullivan", surname: "Jane"))
I would expect that the results array will contain two ExtraClass objects, but instead it contains a BaseClass object and a ExtraClass one, as you may see here. Seems like T is inferred from the type received by the caller function (proxyFunction), which is kind of expected, and, consequently, JSONDecoder is decoding the json string as such.
Is there any way to get JSONDecoder to decode the right kind of class (without altering proxyFunction's signature) or, more precisely, the kind of class obj is?

I need help to Swift with function

There is this job in Swift 5.0:
The class is presented below. In the body of this class, create a function that will print the parameters of this class for a specific object. Create such an object of class Student, call it this function and display the result on the screen:
Job class
class Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
init(name: String, surname: String, yearOfBorn: Int, mark: Double) {
self.name = name
self.surname = surname
self.yearOfBorn = yearOfBorn
self.mark = mark
}
}
How i can make it?
I trying:
func printStudent() {
if name == name {
print(name)
} else if surname == surname {
print(surname)
} else if yearOfBorn == yearOfBorn {
print(yearOfBorn)
} else if mark == mark {
print(mark)
}
}
I’m not sure what your intent was with these if statements. Perhaps you are thinking of:
if let foo = foo { ... }
But that technique is only used if foo was an optional. But your properties are not optionals, so if let syntax is unnecessary.
Needless to say, you could just do:
func printStudent() {
print(name)
print(surname)
print(yearOfBorn)
print(mark)
}
FWIW, if your intent is just to print this out for your own purposes, you might want to make your class conform to CustomStringConvertible:
extension Student: CustomStringConvertible {
var description: String { return "<Student name=\(name); surname=\(surname); yearOfBorn=\(yearOfBorn); mark=\(mark)>" }
}
Then you don’t need to write your own printStudent method at all, but can use print directly:
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
And that will produce:
<Student name=Rob; surname=Ryan; yearOfBorn=2000; mark=4.0>
Alternatively, if you’re OK with struct value type instead, you don’t need the init method or the CustomStringConvertible protocol, at all. Then you can define Student as simply:
struct Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
}
And then
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
Will produce:
Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4.0)
If you want to print all the attributes of the object you don’t need this if statements; as a matter of fact if you pass name == name as the parameter the first if statement will be always entered and thus the other ones skipped.
You just need to create a function like this where you print each attribute:
func printStudent() {
print(self.name)
print(self.surname)
print(self.yearOfBorn)
...
}
You just need to print the variables:
func printStudent() {
print("Name: \(self.name), Surname: \(self.surname), Year Of Born: \(self.yearOfBorn)")
}
try this code:
func printStudent () {
print("name: \(self.name), surname: \(self.surname), yearOfBorn: \ .
(self.yearOfBorn), mark: \(self.mark)")
}

self.name = name in Swift . I don't understand why this codes are needed

class person {
var name : String
init(name: String) {
self.name = name
}
}
I am learning Swift class chapter
I don't understand why init(name:String) self.name = name code is needed
what the purpose of this code is.
I declared var name: String
and again init(name: String), why is self.name = name needed?
what's different between just var name and self.name = name?
Look into something called variable scope. In your code - there are two "name variables - a class (or instance) variable and a "parameter in your init.
The latter - init(name:) only is in use within your initialization function, meaning the instance has no name without the self.name = name once you've created the instance.
The former is available - to the instance - as long as your instance of the class person is.
To explain further, try this. Change your code to be:
class Person {
var myName : String
var myOtherName : String
init(name: String) {
self.myName = name
}
}
In your app or view controller, do this:
let myPerson = Person(name: "john")
print(myPerson.myName) // prints "jihoon"
print(myPerson.myOtherName) // prints nothing
print(myPerson.name) // generates a build error because name doesn't exist
One last note - in Swift class names are capitalized, so the best name is Person, not person.
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
class person {
var name : String // undetrmined state
init(name: String) {
self.name = name
}
}
class person2 {
var name : String = "default value" // detrmined state
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
// Aother example for optional stored property
class person3 {
var name : String? // detrmined state, because default value for optional is nil
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
For more info read this one Apple Doc

How to call a class method in a convenience initializer in Swift

I am trying to consume an API where every object names its ID field differently. Example: Group.groupid, Team.teamid, etc.
I have a BaseAPIObject that has a required initializer that accepts a parsed JSON dictionary and a convenience initializer that takes just the ID field (the only required property of my class).
I've dealt with the changing id field names by adding a static aka "class" method that returns the ID field name and subclasses override that function to return their own field name.
The problem I have is that in my base class' convenience initializer I can't call self.dynamicType before I've called self.init() but I need the results of that static class function before I can properly construct my object.
public class BaseAPIObject {
var apiID: String!
var name: String?
var createdBy: String?
var updatedBy: String?
//Construct from JSONSerialization Dictionary
required public init(_ data: [String: AnyObject]) {
name = data["name"] as String?
createdBy = data["created_by"] as String?
updatedBy = data["updated_by"] as String?
super.init()
let idName = self.dynamicType.idFieldName()
apiID = data[idName] as String?
}
/// Creates an empty shell object with only the apiID set.
/// Useful for when you have to chase down a nested object structure
public convenience init(id: String) {
// THIS is what breaks! I can't call self.dynamicType here
// How else can I call the STATIC CLASS method?
// in ObjC this would be as simple as self.class.getIdFieldName()
let idFieldName = self.dynamicType.getIdFieldName()
let data = [idFieldName: id]
self.init(data)
}
//This gets overridden by subclasses to return "groupid" or whatever
class func idFieldName() -> String {
return "id"
}
}
Question: How can I solve the problem of calling a subclass' class function before I run init on the instance itself?
Instead of creating a class function for figuring out the id, create init functions instead. Since you already have to create one of these functions per subclass, you're not really losing anything. The subclasses init function then calls the super's init with the id name.
Here's an example, I changed some of the properties of your group just for the sake of making the example simple to illustrate the concept.
public class BaseAPIObject {
var objData: [String:String]
required public init(_ data: [String: String]) {
println("Data: \(data)")
self.objData = data
}
public convenience init(id: String, idFieldName: String) {
let data = [idFieldName: id]
self.init(data)
}
}
And then in your subclass, just conceptually something like this:
public class GroupObject: BaseAPIObject {
public convenience init (id: String) {
self.init(id: id, idFieldName: "group")
}
}
let go = GroupObject(id: "foo")
println(go.objData["group"]!) //prints "foo"

Access properties via subscripting in Swift

I have a custom class in Swift and I'd like to use subscripting to access its properties, is this possible?
What I want is something like this:
class User {
var name: String
var title: String
subscript(key: String) -> String {
// Something here
return // Return the property that matches the key…
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
myUser = User(name: "Bob", title: "Superboss")
myUser["name"] // "Bob"
Update: The reason why I'm looking for this is that I'm using GRMustache to render from HTML templates. I'd like to be able to just pass my model object to the GRMustache renderer…
GRMustache fetches values with the keyed subscripting objectForKeyedSubscript: method and the Key-Value Coding valueForKey: method. Any compliant object can provide values to templates.
https://github.com/groue/GRMustache/blob/master/Guides/view_model.md#viewmodel-objects
This is a bit of a hack using reflection. Something along the lines of the following could be used.
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
for child in m.children {
if child.label == key { return child.value }
}
return nil
}
}
struct Person {
let name: String
let age: Int
}
extension Person : PropertyReflectable {}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
You could modify the subscript to always return an interpolated string of the property value.
Adding some syntax sugar to Benzi's answer:
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
return m.children.first { $0.label == key }?.value
}
}
struct Person: PropertyReflectable {
let name: String
let age: Int
}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
Using valueForKey should enable you to access properties using their names. Be sure that you're working with a object that inherit NSObject
class people: NSObject {
var age: NSString = "44"
var height: NSString = "153"
}
let person:people = people()
let stringVariable = "age"
person.valueForKey("age")
// Print "44"
person.valueForKey("\(stringVariable)")
// Print "44"
(GRMustache author here)
Until a swift-oriented Mustache library is out, I suggest having your classes inherit from NSObject (so that they have the valueForKey: method). GRMustache will then fetch values with this method.
In case this would still not work (blank values in the rendering), you may try to disable GRMustache security features (see https://github.com/groue/GRMustache/blob/master/Guides/security.md#disabling-safe-key-access)
Should you experience any other trouble, please open an issue right into the repository: https://github.com/groue/GRMustache/issues
EDIT February 2, 2015: GRMustache.swift is out: http://github.com/groue/GRMustache.swift
Shim's answer above doesn't work anymore in Swift 4. There are two things you should be aware of.
First of all, if you want to use value(forKey:) function, your class must inherit NSObject.
Secondly, since Objective-C doesn't know anything about value type, you have to put the #objc keyword in front of your value type properties and Swift will do the heavy-lifting for you.
Here is the example:
import Foundation
class Person: NSObject {
#objc var name: String = "John Dow"
#objc var age: Int = 25
#objc var height: Int = 180
subscript(key: String) -> Any? {
return self.value(forKey: key)
}
}
let person: Person = Person()
person["name"] // "John Dow"
person["age"] // 25
person["height"] // 180
I suppose you could do:
class User {
let properties = Dictionary<String,String>()
subscript(key: String) -> String? {
return properties[key]
}
init(name: String, title: String) {
properties["name"] = name
properties["title"] = title
}
}
Without knowing your use case I would strongly advise against doing this.
Another approach:
class User {
var name : String
var title : String
subscript(key: String) -> String? {
switch key {
case "name" : return name
case "title" : return title
default : return nil
}
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
It might be worth noting that Swift doesn't appear to currently support reflection by names. The reflect function returns a Mirror whose subscript is Int based, not String based.