Is there a way to create accessors of inner object properties without having boilerplate?
Here's an example of a class with the boilerplate
public class Foo {
internal let bar: Bar
internal init(bar: Bar) {
self.bar = bar
}
private struct Bar: Codable {
let id: Int
let name: String
}
// MARK: - Boilerplate
public var id: Int {
return self.bar.id
}
public var name: String {
return self.bar.name
}
}
// Usage
do {
let bar: Bar = try data.decoded()
let foo = Foo(bar: bar)
print(foo.id)
print(foo.name)
} catch {
...
}
Is there a way to do this without having to write the boilerplate? For larger objects with more properties this could be really helpful
Take Note: The Access Control Modifiers are important
New in Swift 5.1, you can use dynamic member lookup with a key path. This works brilliantly for this sort of "has-a" situation, because key paths maintain full type checking. Here's a simplified example (with no attempt to mirror your actual situation):
struct Dog {
let name : String
}
#dynamicMemberLookup
struct Kennel {
let dog : Dog
subscript(dynamicMember kp:KeyPath<Dog,String>) -> String {
self.dog[keyPath:kp]
}
}
The result is that, given a Kennel k, we can get k.name as a way of getting k.dog.name.
But saying k.xxx would be illegal, because a Dog has no String xxx property; that's what I mean when I say that full type checking is maintained.
The old way would be to use protocols. That way you can use protocol extension to inject the boilerplate.
protocol HasNameAndId {
var id: Int {get}
var name: String {get}
}
protocol WrapperOfHasNameAndId {
associatedtype T : HasNameAndId
var bar: T {get}
}
extension WrapperOfHasNameAndId { // boilerplate
var id: Int {
return self.bar.id
}
var name: String {
return self.bar.name
}
}
// ==============
struct Bar: HasNameAndId {
let id: Int
let name: String
}
class Foo : WrapperOfHasNameAndId {
let bar: Bar
init(bar: Bar) {
self.bar = bar
}
}
// =======
let f = Foo(bar: Bar(id: 1, name: "howdy"))
print(f.id) // 1
print(f.name) // "howdy"
Related
How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol
I would like to create a class with a static property that subclasses can override, which would be used to initialize instances. So far, I've tried to accomplish this like this:
import Cocoa
class A: NSObject {
class var staticProperty: String {
return "A"
}
var property: String = A.staticProperty
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
This does not work, since B().property still returns "A". How could I change this code so that property contains the value specified by the subclass? Any help would be appreciated!
Edit
I would like to initialize property with the value of staticProperty, so this could also look like this:
var property: SomeClass = SomeClass(A.staticProperty)
But then, this initialization should still use "A" for class A, and "B" for class B.
Edit 2 (After #RakeshaShastri's comment)
For my specific use-case, I need property to be stored (so not computed) and non-lazy.
Edit 3
In short, I'm trying to build a Realm model class which has a few to-many relationships to other models. For these models (which are quite similar), I'm trying to create a superclass which contains the shared functionality, amongst which is also the inverse relationship. Therefore, I want to have a static property which contains the key in the first model to either of the other models, and then initialize a LinkingObjects property using this key name. Since Realm does not allow this to be lazy or computed, I cannot use these functionalities here.
If you inherit from NSObject why not using it ?
import Cocoa
class A: NSObject {
var property: String
public override init() {
let str = type(of: self).perform(#selector(getter: type(of: self).staticProperty))?.takeUnretainedValue() as! String
property = str
}
#objc class var staticProperty: String {
return "A"
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
You can do this with this aproach
class A {
var prop: String{
return "A"
}
}
class B: A {
override var prop: String{
return "B"
}
}
print(A().prop) // "PRINTS A"
print(B().prop) // "PRINTS B"
A.staticProperty will use static dispatch and will always point to A's class property. You need dynamic dispatch here, aka type(of: self).
However, self needs an instance to work with, thus var property: String = type(of: self.staticProperty won't compile.
However, lazy properties can work around this limitation, so you could declare property as a lazy one:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private(set) lazy var property: String = { type(of: self).staticProperty }()
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
print(B().property) // B
P.S. the private(set) part is just something I usually do, I rarely allow extrinsic factors to change my object.
Update As #MartinR has pointed out, lazy is not a choice for the OP. An alternative solution that doesn't use a lazy var is to use a "shadowing" property:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private var _property: String?
var property: String {
get {
return _property ?? type(of: self).staticProperty
}
set {
_property = newValue
}
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
let b = B()
print(b.property) // B
b.property = "B'"
print(b.property) // B'
I am bit confused if we can create computed property which is read-only Somethig like:
extension ToMyClass {
private(set) var isEmpty: Bool {
return head == nil
}
}
While trying to create I got following error:
error: 'private(set)' modifier cannot be applied to read-only properties
You are trying to set a modfier for a computed property, which is always read-only
The code below was taken from: The Swift Programming Language (Swift 4)
struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits += 1
}
}
}
It should be a stored property
I got the same error but for a totally different reason. My code was as such:
protocol Foo {
var bar: String { get }
}
class Baz: Foo {
private (set) let bar: String // Error
init(bar: String) {
self.bar = bar
}
}
I just had to change:
private (set) let bar: String
to:
private (set) var bar: String
let makes properties immutable and that was causing issues.
I've recently came across a case where it would be very convenient to convert an instance of a class to a subclass, while the instance has been created within the parent class. But I've never seen such thing. So is there a way to do something like:
class Foo {
var name: String
}
class Bar: Foo {
var friendName: String
}
let foo = Foo(name: "Alice")
foo.toBar(friendName: "Bob")
// foo now of type Bar, as if I'd done
// foo = Bar(name: "Alice", friendName: "Bob")
If that's not possible, is there some reasons this would be impossible from a design perspective?
===edit=== description of a use case where it could make sense
Let say there's two views representing what correspond to the same database record for a book, on is a just a preview of the book and another is a more complex view. Models could be:
protocol BookMetaDelegate {
func onReadStatusUpdate()
}
/// describe a book
class BookMeta {
var delegate: BookMetaDelegate?
private var _hasBeenRead: Bool
var hasBeenRead: Bool {
get {
return _hasBeenRead
}
set {
guard newValue != _hasBeenRead else { return }
_hasBeenRead = newValue
delegate?.onReadStatusUpdate()
}
}
var title: String
}
/// contains all the content of a book
class Book: BookMeta {
var content: BookContent
var lastPageRead: Int
/// some logic that only makes sense in a Book instance
func getLastPageRead() {
return content.getPage(lastPageRead)
}
}
and views could look like:
class BookPreview: UIView, BookMetaDelegate {
var book: BookMeta
init(book: BookMeta) {
book.delegate = self
}
func onReadStatusUpdate() {
print("read status has changed! UI should update")
}
}
class BookView: UIView {
var book: Book
init(book: Book) {
book.hasBeenRead = true
}
}
Then things could happen like
fetch(bookMetaWithId: 123).then { bookMeta in // bookMeta is of type BookMeta
let preview = BookPreview(book: bookMeta)
...
fetch(contentOf: bookMeta).then { content, lastPageRead in
bookMeta.asBook(content: content, lastPageRead: lastPageRead)
let bookView = BookView(book: bookMeta) // doing so will change the hasBeenRead flag and message the instance's delegate, ie the preview
...
}
}
Thinking more about it, it sounds like that if such thing was possible, it'd break things like:
class Foo {
var name: String
}
class Bar: Foo {
var friendName: String
}
class Bla: Foo {
var surname: String
}
func something(foo: Foo) {
foo.toBla(surname: "Will")
}
let bar = Bar(name: "Alice", friendName: "Bob")
something(foo: bar) // what does that do ??? is bar a Bla now ?
so that'd be a good reason for making such casting impossible.
In Objective-C, one can add a description method to their class to aid in debugging:
#implementation MyClass
- (NSString *)description
{
return [NSString stringWithFormat:#"<%#: %p, foo = %#>", [self class], foo _foo];
}
#end
Then in the debugger, you can do:
po fooClass
<MyClass: 0x12938004, foo = "bar">
What is the equivalent in Swift? Swift's REPL output can be helpful:
1> class MyClass { let foo = 42 }
2>
3> let x = MyClass()
x: MyClass = {
foo = 42
}
But I'd like to override this behavior for printing to the console:
4> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)
Is there a way to clean up this println output? I've seen the Printable protocol:
/// This protocol should be adopted by types that wish to customize their
/// textual representation. This textual representation is used when objects
/// are written to an `OutputStream`.
protocol Printable {
var description: String { get }
}
I figured this would automatically be "seen" by println but it does not appear to be the case:
1> class MyClass: Printable {
2. let foo = 42
3. var description: String { get { return "MyClass, foo = \(foo)" } }
4. }
5>
6> let x = MyClass()
x: MyClass = {
foo = 42
}
7> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)
And instead I have to explicitly call description:
8> println("x = \(x.description)")
x = MyClass, foo = 42
Is there a better way?
To implement this on a Swift type you must implement the CustomStringConvertible protocol and then also implement a string property called description.
For example:
class MyClass: CustomStringConvertible {
let foo = 42
var description: String {
return "<\(type(of: self)): foo = \(foo)>"
}
}
print(MyClass()) // prints: <MyClass: foo = 42>
Note: type(of: self) gets the type of the current instances instead of explicitly writing ‘MyClass’.
Example of using CustomStringConvertible and CustomDebugStringConvertible protocols in Swift:
PageContentViewController.swift
import UIKit
class PageContentViewController: UIViewController {
var pageIndex : Int = 0
override var description : String {
return "**** PageContentViewController\npageIndex equals \(pageIndex) ****\n"
}
override var debugDescription : String {
return "---- PageContentViewController\npageIndex equals \(pageIndex) ----\n"
}
...
}
ViewController.swift
import UIKit
class ViewController: UIViewController
{
/*
Called after the controller's view is loaded into memory.
*/
override func viewDidLoad() {
super.viewDidLoad()
let myPageContentViewController = self.storyboard!.instantiateViewControllerWithIdentifier("A") as! PageContentViewController
print(myPageContentViewController)
print(myPageContentViewController.description)
print(myPageContentViewController.debugDescription)
}
...
}
Which print out:
**** PageContentViewController
pageIndex equals 0 ****
**** PageContentViewController
pageIndex equals 0 ****
---- PageContentViewController
pageIndex equals 0 ----
Note: if you have a custom class which does not inherit from any class included in UIKit or Foundation libraries, then make it inherit of NSObject class or make it conform to CustomStringConvertible and CustomDebugStringConvertible protocols.
Just use CustomStringConvertible and var description: String { return "Some string" }
works in Xcode 7.0 beta
class MyClass: CustomStringConvertible {
var string: String?
var description: String {
//return "MyClass \(string)"
return "\(self.dynamicType)"
}
}
var myClass = MyClass() // this line outputs MyClass nil
// and of course
print("\(myClass)")
// Use this newer versions of Xcode
var description: String {
//return "MyClass \(string)"
return "\(type(of: self))"
}
The answers relating to CustomStringConvertible are the way to go. Personally, to keep the class (or struct) definition as clean as possible, I would also separate out the description code into a separate extension:
class foo {
// Just the basic foo class stuff.
var bar = "Humbug!"
}
extension foo: CustomStringConvertible {
var description: String {
return bar
}
}
let xmas = foo()
print(xmas) // Prints "Humbug!"
class SomeBaseClass: CustomStringConvertible {
//private var string: String = "SomeBaseClass"
var description: String {
return "\(self.dynamicType)"
}
// Use this in newer versions of Xcode
var description: String {
return "\(type(of: self))"
}
}
class SomeSubClass: SomeBaseClass {
// If needed one can override description here
}
var mySomeBaseClass = SomeBaseClass()
// Outputs SomeBaseClass
var mySomeSubClass = SomeSubClass()
// Outputs SomeSubClass
var myOtherBaseClass = SomeSubClass()
// Outputs SomeSubClass
As described here, you can also use Swift's reflection capabilities to make your classes generate their own description by using this extension:
extension CustomStringConvertible {
var description : String {
var description: String = "\(type(of: self)){ "
let selfMirror = Mirror(reflecting: self)
for child in selfMirror.children {
if let propertyName = child.label {
description += "\(propertyName): \(child.value), "
}
}
description = String(description.dropLast(2))
description += " }"
return description
}
}
struct WorldPeace: CustomStringConvertible {
let yearStart: Int
let yearStop: Int
var description: String {
return "\(yearStart)-\(yearStop)"
}
}
let wp = WorldPeace(yearStart: 2020, yearStop: 2040)
print("world peace: \(wp)")
// outputs:
// world peace: 2020-2040