Swift equivalent of Java toString() - ios

What is the Swift equivalent of Java toString() to print the state of a class instance?

The description property is what you are looking for. This is the property that is accessed when you print a variable containing an object.
You can add description to your own classes by adopting the protocol CustomStringConvertible and then implementing the description property.
class MyClass: CustomStringConvertible {
var val = 17
public var description: String { return "MyClass: \(val)" }
}
let myobj = MyClass()
myobj.val = 12
print(myobj) // "MyClass: 12"
description is also used when you call the String constructor:
let str = String(myobj) // str == "MyClass: 12"
This is the recommended method for accessing the instance description (as opposed to myobj.description which will not work if a class doesn't implement CustomStringConvertible)

If it is possible to use the struct instead of class, then nothing additional to do.
struct just prints fine itself to the output
print("\(yourStructInstance)")
or with class like this:
print(String(describing: yourClassInstance))

You should use String(obj).
Direct from the documentation for CustomStringConvertible:
NOTE
String(instance) will work for an instance of any type, returning its
description if the instance happens to be CustomStringConvertible.
Using CustomStringConvertible as a generic constraint, or accessing a
conforming type's description directly, is therefore discouraged.

How it's done with NSObject extended classes
If your model class is extended from NSObject, you have to override the Variable description as follows:
public override var description: String {
return "\n{\n index: \(self.index),\n"
+ " country: \(self.name),\n"
+ " isoCountryCode: \(self.isoCountryCode),\n"
+ " localeId: \(self.localeId),\n"
+ " flagImageName: \(self.flagImageName!)\n}"
}
You can check how I have done it here within the Country class, in the "CountryPicker iOS Swift library".
Or, to make it simpler for you to understand, your class and description method should look like following:
public class MyClass: NSObject {
public var memberAttribute = "I'm an attribute"
public override var description: String {
return "My Class member: \(self.memberAttribute)"
}
}
Note:
Since you are extending your Modal class from NSObject it doesn't require for your class to comply with CustomStringConvertible class anymore, and you are overriding description variable from the NSObject class itself. Always remember, CustomStringConvertible is mostly the pure Swift way of achieving this.

Related

Why Swift protocol conforming values are treated as Value types by default?

I have a protocol, which only describes an interface.
protocol SampleProtocol {
var message: String? { get set }}
Why does the compiler treat the conforming value/object always as a Value type?
Sample Example,
// Protocol value usage
import Foundation
protocol SampleProtocol {
var message: String? { get set }
}
final class SampleObject: SampleProtocol {
var message: String?
}
final class Controller {
var sampleValue: SampleProtocol! {
didSet {
print("New value has been set")
}
}
}
let controller = Controller()
controller.sampleValue = AlphaObject() // Correctly prints "New value has been set"
controller.sampleValue.message = "New message" // Prints again "New value has been set", like for value types
Your protocol SampleProtocol could be adopted by a class or a struct. Swift is using the behavior of the value type which is the more restrictive type until you tell it that the protocol will only be used by a class reference type.
Add conformance to AnyObject to your protocol to get reference type behavior:
protocol SampleProtocol: AnyObject {
var message: String? { get set }
}
See The Swift 5.1 Programming Guide - Class-Only Protocols for more details.
The guide notes:
Use a class-only protocol when the behavior defined by that protocol’s
requirements assumes or requires that a conforming type has reference
semantics rather than value semantics.
Historical note: Using class keyword:
Prior to Swift 4.0, this was written using the class keyword:
protocol SampleProtocol: class {
var message: String? { get set }
}
This still works for the time being, but it is currently just a type alias for AnyObject and will likely be removed in a later version of Swift.
It doesn’t create a copy of the object. It is the same object in both calls – if you put print(Unmanaged.passUnretained(sampleValue as AnyObject).toOpaque()) in didSet you will see that the address is the same. Compiler doesn’t know it’s dealing with a value type or a reference type. If you put a class keyword in protocol declaration like so:
protocol SampleProtocol: class { ... }
the didSet will be called once, because compiler knows it's a reference type and will not reassign reference.
I guess different setters generated depends on type of property

Can I initialize a Codable Model from a different model in a Swift extension?

I have a codable model
public final class MyCodableModel: Codable {
public let id: Int
}
I also have another model that happens to have the same variables inside it.
public final class MyOtherModel {
public let id: Int
}
Now, I want to instantiate MyCodableModel using MyOtherModel in an extension. I don't want to modify MyCodableModel directly because of dependency issues.
I first tried to use a normal initializer in my extension, but it said I needed to use a convenience initializer, so then I ended up with this:
extension MyCodableModel {
convenience init?(myOtherModel: MyOtherModel) {
id = myOtherModel.id
}
}
But the error says 'let' property 'id' may not be initialized directly; use "self.init(...)" or "self = ..." instead. I assume this is because I'm not using the designated initializer of init(from: Decoder).
Is there another way to do this? Or will I not be able to convert MyOtherModel to a MyCodableModel?

How to use generics in own created model class in iOS?

How to use generics in own created model class?
I have one FeatureListModel class and other have FavoriteModel class. Both store the same properties, the only difference is the different class model name.
I need to display model properties value in ProductDetail controller.
How could I manage this stuff using generics?
Here is my code (Swift 4.2):
1st Model: FavoriteListModel
class FavoriteListModel {
var categoryID: Int?
var item_name: String?
var MRP: String?
}
2nd Model: FeatureListModel
class FeatureListModel {
var categoryID: Int?
var item_name: String?
var MRP: String?
}
I have 8-10 more properties, but this is just some stuff in my code.
Controller - ProductDetailTableViewController
class ProductDetailTableViewController : UITableViewController {
var productDetails: FavoriteListModel!
var productFeatureList: FeatureListModel!
fileprivate func displayProduct() {
if productDetails != nil {
title = productDetails.item_name
categoryID = productDetails.categoryID!
}else if productFeatureList != nil {
categoryID = productFeatureList.categoryID!
title = productFeatureList.item_name
}
}
and in my Product Detail Table Controller, I am accessing model objects and display on the screen.
I don't want if-else check.
You are mixing up generics and protocols. In your case a protocol is preferable.
In ProductDetailTableViewController there is an object which responds to the getter of item_name (by the way please conform to the camelCased naming convention itemName) and categoryID. The type of the object as well as the existence of other properties and functions is not significant.
Create a protocol
protocol Listable {
var itemName : String { get }
var categoryID : Int { get }
}
Then adopt the protocol in your classes (do you really need a class?) and declare at least categoryID as non-optional since you are force unwrapping the value later anyway. Don't use optionals as an alibi not to write an initializer.
class FavoriteListModel : Listable { ...
class FeatureListModel : Listable { ...
In ProductDetailTableViewController rather than two properties declare one property as Listable and instead of objective-c-ish nil checking use optional binding:
var details: Listable!
fileprivate func displayProduct() {
if let productDetails = details {
title = productDetails.itemName
categoryID = productDetails.categoryID
}
}
What you have here is not a use case for the Generics. Generics are used when you have for example a function that does exact same thing but can be used with two different parameter types. That's when you use generics.
Another concept is super class (parent class or base class) which is used when you have a class with common properties and then other classes with those properties and then extra and different unique properties which in this case, each class subclasses the parent class.
What you have here is neither of them. A good architecture for this case is just a single model type (class or struct) and using two different collections (array or set) in your view controller.
You can also create a favorite class or featured class which holds an array with your models.

Immutable class accessors in swift 3

I am trying to implement objects similar to CNContact and CNMutableContact such that I can retrieve my immutable objects from a custom store and make editions only to mutable copies of the objects.
To tackle this I have taken a look at the CNContact source available through Xcode to find this:
open class CNContact : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
/*! The identifier is unique among contacts on the device. It can be saved and used for fetching contacts next application launch. */
open var identifier: String { get }
open var contactType: CNContactType { get }
...
}
However when I try something similar I get an error:
class MyModel: NSObject, NSCoding, NSSecureCoding {
// MARK: - Properties
var name: String { get }
...
}
The error being Expected '{' to start a getter definition. What am I doing wrong here?
The next step then would be to have a mutable subclass, very similar to CNMutableContact which would looks like this:
class MutableMyModel: MyModel {
// MARK: - Properties
var name: String
...
}
If my approach to this problem is sensible then why am I seeing this syntax error, and why is it valid within the Apple written code but not my own?
Finally am I going to hit any issues within my subclass where I am essentially redefining the name property?
var name: String { get } is only a definition in the protocol. A conforming class, such as MyModel needs to implement the definition, like so:
class MyModel: NSObject, NSCoding, NSSecureCoding {
var name: String { return "String Value here" }
}

A static field inherited from the base class or protocol - how?

I want to be able to have the classes which have a static property (field) which is either inherited from the base class or "mixed" from a protocol. And every class should have it's own implementation of that property. Is it possible? Preferably, it to be immutable.
class C1 {
static let stProperty = "my prorepty1"
}
class C2 {
static let stProperty = "my prorepty2"
}
It's possible, but it's really hard to make this useful in Swift. How do you plan to refer to this property? Let's start with a super-simple implementation:
protocol SomeProtocol {
static var prop: String { get }
}
class C1: SomeProtocol {
static let prop = "This is One"
}
Great. So now I want a function that uses this:
func useProp(x: SomeProtocol) -> String {
return x.prop
// 'SomeProtocol' does not have a member named 'prop'
}
That doesn't work. x is an instance, but I want the type.
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
func useProp(x: SomeProtocol.Type) -> String {
return x.prop
}
This is probably how it will work some day given the word "unimplemented." But it doesn't work today.
func useProp(x: SomeProtocol) -> String {
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
return x.dynamicType.prop
}
Same thing.
Today, you really have to hang this on the object itself and not use static or class:
protocol SomeProtocol {
var prop: String { get }
}
class C1: SomeProtocol {
let prop = "This is One"
}
func useProp(x: SomeProtocol) -> String {
return x.prop
}
That's not so terrible in many cases, since the value for the class is probably also the value for any given instance of the class. And it's really all we can do today.
Of course your problem might be that you don't have an instance yet and you need this information to build an instance. That's really hard today and you should probably rethink your design. You'll generally have to use some other pattern like a Builder. See Generic Types Collection for more.
Now you also said:
or "mixed" from a protocol
I wouldn't say "mixed" here. If you really mean this like a Ruby "mixin", there is no such thing in Swift today. Swift folks often refer to this feature as "default implementation," and it's not currently possible (though I do expect it to come eventually). The only thing you can do in the protocol is say that the implementor has to provide this method somehow. You can't provide it for them.
Sure you can do that with a protocol:
protocol SomeProtocol {
static var foo: String { get }
}
class One: SomeProtocol {
class var foo: String {
get {
return "This is One"
}
}
}
Btw I agree with Rob Napier below that this is a bit off a oddball feature. I do think there are probably use-cases for it, but I also think those can be better implemented with other language features
protocol P {
class var stProperty: String { get }
}
class C1 {
class var stProperty: String {
return = "my property1"
}
}
class C2 {
class var stProperty: String {
return = "my property2"
}
}
Usage:
C2.prop //"my property2"
If you try:
C2.prop = "new value" //"cannot assign to the result of this expression"

Resources