Aggregating an array of swift types - swift

In Swift, I know that types are classes unto themselves, but I am having a hard time making this work.
I am trying to create a "factory," where I have an Array of types, each of which conform to a protocol (so it would actually be an Array of protocol), which specifies a static "factory" function.
This would allow me to associate a set of classes with an instance, so that the instance can cycle through the "factory," and instantiate the classes that are associated with that instance.
Here's what I mean:
protocol A {
static func makeAStat() -> A
func makeADyn() -> A
}
struct APrime: A {
static func makeAStat() -> A {
return Self()
}
func makeADyn() -> A {
return Self.makeAStat()
}
}
let instanceOne = APrime.makeAStat()
let instanceTwo = APrime().makeADyn()
let arrayOf1 = [APrime()]
let instance3 = arrayOf1[0].makeADyn()
//let arrayOf2: [A.Type] = [APrime]
//let instance4 = arrayOf2[0].makeAStat()
Note the two commented-out lines. They will break the playground, but I'd like to get something like that working, so I can just say something like "This is a WidgetPlus object, so it gets to pick from these three types as handlers."
Does anyone have a suggestion as to the best way for me to achieve this?

Just add .self
let arrayOf2: [A.Type] = [APrime.self]
let instance4 = arrayOf2[0].makeAStat()
And for more clarity I'd recommend to write
protocol A {
static func makeAStat() -> Self
func makeADyn() -> Self
}
struct APrime: A {
static func makeAStat() -> APrime {
return APrime()
}
func makeADyn() -> APrime {
return APrime.makeAStat()
}
}

Related

How can you get the shared instance from AnyClass using a protocol in Swift?

In the past we've used Objective-C to anonymously get the sharedInstance of a class this way:
+ (nullable NSObject *)sharedInstanceForClass:(nonnull Class)aClass
{
// sharedPropertyProvider
NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:#protocol(KVASharedPropertyProvider)]
? (NSObject<KVASharedPropertyProvider> *)aClass
: nil;
if (sharedPropertyProvider == nil)
{
return nil;
}
// return
return [sharedPropertyProvider.class sharedInstance];
}
It's protocol based. We put this protocol on every class we have with a shared instance where we need to do this.
#objc (KVASharedPropertyProvider)
public protocol KVASharedPropertyProvider: AnyObject
{
#objc (sharedInstance)
static var sharedInstance: AnyObject { get }
}
The above works fine in Objective-C (and when called from Swift). When attempting to write the same equivalent code in Swift, however, there appears to be no way to do it. If you take this specific line(s) of Objective-C code:
NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:#protocol(KVASharedPropertyProvider)]
? (NSObject<KVASharedPropertyProvider> *)aClass
: nil;
And attempt to convert it to what should be this line of Swift:
let sharedPropertyProvider = aClass as? KVASharedPropertyProvider
... initially it appears to succeed. The compiler just warns you that sharedPropertyProvider isn't be used. But as soon as you attempt to use it like so:
let sharedInstance = sharedPropertyProvider?.sharedInstance
It gives you the compiler warning back on the previous line where you did the cast:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider' always fails
Any ideas? Is Swift simply not capable of casting AnyClass to a protocol in the same way that it could be in Objective-C?
In case you're wondering why we need to do this, it's because we have multiple xcframeworks that need to operate independently, and one xcframework (a core module) needs to optionally get the shared instance of a higher level framework to provide special processing if present (i.e. if installed) but that processing must be initiated from the lower level.
Edit:
It was asked what this code looked like in Swift (which does not work). It looks like this:
static func shared(forClass aClass: AnyClass) -> AnyObject?
{
guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider else
{
return nil
}
return type(of: sharedPropertyProvider).sharedInstance
}
The above generates the warning:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider' always fails
It was suggested I may need to use KVASharedPropertyProvider.Protocol. That looks like this:
static func shared(forClass aClass: AnyClass) -> AnyObject?
{
guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider.Protocol else
{
return nil
}
return type(of: sharedPropertyProvider).sharedInstance
}
And that generates the warning:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider.Protocol' always fails
So, I assume you have something like this
protocol SharedProvider {
static var shared: AnyObject { get }
}
class MySharedProvider: SharedProvider {
static var shared: AnyObject = MySharedProvider()
}
If you want to use AnyObject/AnyClass
func sharedInstanceForClass(_ aClass: AnyClass) -> AnyObject? {
return (aClass as? SharedProvider.Type)?.shared
}
Better approach
func sharedInstanceForClass<T: SharedProvider>(_ aClass: T.Type) -> AnyObject {
return T.shared
}

Swift - Referring to a class's type within the class

When using static, type properties and methods from within the instance methods of the type, I'm often repeating the name of the type.
e.g.
class Foo
{
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(Foo.kBrandColor)")
print("The meaning of life is: \(Foo.kMeaning)")
let thing = Foo.makeThing()
print("Thing is \(thing)"
}
...
}
These repeated references to "Foo" can (and often do) lead to bugs when copy-pasting, refactoring. It's very easy to forget to change a "Foo", and the code will still compile.
So, I've been using a pattern like this:
class Foo
{
fileprivate typealias _ThisClass = Foo
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(_ThisClass.kBrandColor)")
print("The meaning of life is: \(_ThisClass.kMeaning)")
let thing = _ThisClass.makeThing()
print("Thing is \(thing)"
}
...
}
This approach has the advantage of some copy-and-paste safety, but at the expense of a bit of boilerplate.
Is there a better, cleaner solution to this issue? (I've attempted to search SO, but getting the search terms right for this kind of problem has been tricky.)
A protocol would work well here. You can define the properties the protocol requires, and then apply that to any class you want to use these in.
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
If you want to reuse the printStuff function, you can also put that in the protocol, and put a base implementation in an extension:
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
func printStuff()
}
extension Brandable {
func printStuff() {
print("Brand Color is: \(kBrandColor)")
print("The meaning of life is: \(kMeaning)")
}
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
class Bar: Brandable {
let kBrandColor: UIColor = .blue
let kMeaning: Int = 100
}
Foo().printStuff()
Bar().printStuff()
The same can be done with the makeStuff() function. Shared functionality goes in the protocol and its extension. If you need to change the behavior in some class, you only need to add your own printStuff or makeStuff function to override the protocol's default implementation.
Create a protocol like Identifiable with an identifier property. Then make any class that you want identifiable conform to it. It's the protocol oriented approach.
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String {
return String(describing: self)
}
}
class X: Identifiable {}
You also don't need to refer to the class name. Just call type(of: instance).identifier.

Blank constant when trying to get list of classes that have adopted a Protocol

I am trying to get a list of classes that have adopted a certain Protocol Migration: Preparation, and then to append those classes into an array. Here is the function in question:
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
var migrationsList = [Preparation.Type]()
var count = UInt32(0)
let classList = objc_copyClassList(&count)!
for i in 0..<Int(count) {
let classInfo = ClassInfo(classList[i])!
if let cls = classInfo.classObject as? Migration.Type {
migrationsList.append(cls)
print(cls.description)
}
}
return migrationsList
}
}
In principle all that should work, but when debugging I note that the classInfo variable is referring to each class in the iteration, but when assigning and casting in the if let as line, the constant cls is always blank - neither a value/class nor nil, just completely blank.
Any idea what I got wrong with that code?
I am also open to suggestions for any better way to get a list of all classes that have adopted a particular protocol...
EDIT: I forgot to provide the code for ClassInfo
import Foundation
struct ClassInfo: CustomStringConvertible, Equatable {
let classObject: AnyClass
let className: String
init?(_ classObject: AnyClass?) {
guard classObject != nil else { return nil }
self.classObject = classObject!
let cName = class_getName(classObject)!
self.className = String(cString: cName)
}
var superclassInfo: ClassInfo? {
let superclassObject: AnyClass? = class_getSuperclass(self.classObject)
return ClassInfo(superclassObject)
}
var description: String {
return self.className
}
static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool {
return lhs.className == rhs.className
}
}
I can't explain why cls is always blank, like I said in my comment it's something I run into every time I'm dealing with meta types. As for making the code work as intended, I found this q&a and updated it with Swift 3 to get this code which should cover your situation. It's important to stress that this will only work if you correctly expose Swift to the Objective-C runtime.
Drop this code anywhere and call print(Migrations.getMigrations()) from a convenient entry point.
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
return getClassesImplementingProtocol(p: Preparation.self) as! [Preparation.Type]
}
static func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
let classes = objc_getClassList()
var ret = [AnyClass]()
for cls in classes {
if class_conformsToProtocol(cls, p) {
ret.append(cls)
}
}
return ret
}
static func objc_getClassList() -> [AnyClass] {
let expectedClassCount = ObjectiveC.objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = ObjectiveC.objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyClass]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)] {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
class Migration: Preparation {
}
#objc
protocol Preparation {
}

Generic Return Type Based on Class

I'm trying to create factory method on a class that automatically casts to the class it's on.
extension NSObject {
// how can I get the return type to be the current NSObject subclass
// instead of NSObject?
class func create() -> NSObject {
return self.init()
}
// example: create(type: NSArray.self)
class func create<T:NSObject>(type:T.Type) -> T {
return T()
}
}
Example two works, but gets NO advantage from being a class method:
let result = NSArray.create(type: NSArray.self)
But I'd love to be able to just call:
let result = NSArray.create()
without having to cast afterwards. Is there a way to do this in Swift?
You can use the class-level Self for this:
extension NSObject {
class func create() -> Self {
return self.init()
}
}
let array = NSArray.create()
But I don't really see why you would, since you might as well just add an initializer.
The accepted answer does the trick, thanks!
However, I needed this for a case where I wasn't calling the init directly. Instead, I had an object that was of type NSObject and needed a forced downcast
As #Hamish pointed out from this other SO answer, you can use the generic inference on a class method if you're another layer deep (a method called by a class method).
class func create() -> Self {
return createInner()
}
class func createInner<T>() -> T {
// upcasting to NSObject to show that we can downcast
let b = self.init() as NSObject
return b as! T
}
let array = NSArray.create() // gives me an NSArray
An Example with CoreData
I still can't figure out how to get the fetch part to compile, so I'm using an external function still.
import CoreData
// callers use
// try fetch(type: SomeMO.self, moc: moc)
func fetch<T:NSManagedObject>(type:T.Type, moc:NSManagedObjectContext) throws -> [T] {
return try T.fetch(moc: moc) as! [T]
}
extension NSManagedObject {
class func makeOne(moc:NSManagedObjectContext) -> Self {
return makeOneInner(moc: moc)
}
private class func makeOneInner<T>(moc:NSManagedObjectContext) -> T {
let name = "\(self)"
let retVal = NSEntityDescription.insertNewObject(forEntityName: name, into: moc)
return retVal as! T
}
class func fetch(moc:NSManagedObjectContext) throws -> [NSManagedObject] {
let fetchReq:NSFetchRequest<NSManagedObject> = self.fetchRequest() as! NSFetchRequest<NSManagedObject>
let retVal = try moc.fetch(fetchReq) as [NSManagedObject]
return retVal
}
}

Implementing copy() in Swift

I want to be able to copy a custom class in Swift. So far, so good. In Objective-C I just had to implement the NSCopying protocol, which means implementing copyWithZone.
As an example, I have a basic class called Value which stores a NSDecimalNumber.
func copyWithZone(zone: NSZone) -> AnyObject! {
return Value(value: value.copy() as NSDecimalNumber)
}
In Objective-C I, could easily just call copy to copy my object. In Swift, there seems to be no way to call copy. Do I really need to call copyWithZone even if no zone is needed? And which zone do I need to pass as a parameter?
The copy method is defined in NSObject. If your custom class does not inherit from NSObject, copy won't be available.
You can define copy for any object in the following way:
class MyRootClass {
//create a copy if the object implements NSCopying, crash otherwise
func copy() -> Any {
guard let asCopying = ((self as AnyObject) as? NSCopying) else {
fatalError("This class doesn't implement NSCopying")
}
return asCopying.copy(with: nil)
}
}
class A : MyRootClass {
}
class B : MyRootClass, NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return B()
}
}
var b = B()
var a = A()
b.copy() //will create a copy
a.copy() //will fail
I guess that copy isn't really a pure Swift way of copying objects. In Swift it is probably a more common way to create a copy constructor (an initializer that takes an object of the same type).
Well, there is a really easy solution for this and you do not have to create root class.
protocol Copyable {
init(instance: Self)
}
extension Copyable {
func copy() -> Self {
return Self.init(instance: self)
}
}
Now, if you want to make your custom class be able to copy, you have to conform it to Copyable protocol and provide init(instance: Self) implementation.
class A: Copyable {
var field = 0
init() {
}
required init(instance: A) {
self.field = instance.field
}
}
Finally, you can use func copy() -> Self on any instance of A class to create a copy of it.
let a = A()
a.field = 1
let b = a.copy()
You can just write your own copy method
class MyRootClass {
var someVariable:Int
init() {
someVariable = 2
}
init(otherObject:MyRootClass) {
someVariable = otherObject.someVariable
}
func copy() -> MyRootClass {
return MyRootClass(self)
}
}
The benefit of this is when you are using subclasses around your project, you can call the 'copy' command and it will copy the subclass. If you just init a new one to copy, you will also have to rewrite that class for each object...
var object:Object
....
//This code will only work for specific class
var objectCopy = Object()
//vs
//This code will work regardless of whether you are using subClass or superClass
var objectCopy = object.copy()
In my case the object chain was large and nested so was looking for simpler solutions.
The core concept being simple enough... duplicate the data by new initialization, I used Encode and Decode to deep-copy the entire object since my objects were already conforming to Codable,
Simple Example:
class MyCodableObject: Codable, CustomStringConvertible {
var name: String
var description: String { name }
init(name: String) {
self.name = name
}
}
let originalArr = [MyCodableObject(name: "a"),
MyCodableObject(name: "b")]
do {
let data = try JSONEncoder().encode(originalArr)
let copyArr = try JSONDecoder().decode([MyCodableObject].self, from: data)
//modify if required
copyArr.forEach { obj in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
} catch {
fatalError(error.localizedDescription)
}
Refactor (Generic Solution):
To simplify future cases we can create a protocol that will provide a copy function.
For Non-Codable objects, you will have to implement your own copy function.
For Codable objects, we can provide a default implementation so it's ready-to-use. Like so:
protocol Copyable {
func copy() -> Self
}
extension Copyable where Self: Codable {
func copy() -> Self {
do {
let encoded = try JSONEncoder().encode(self)
let decoded = try JSONDecoder().decode(Self.self, from: encoded)
return decoded
} catch {
fatalError(error.localizedDescription)
}
}
}
We can now conform a Codable object to our Copyable protocol and start using it immediately.
extension MyCodableObject: Copyable {}
Example:
let a = MyCodableObject(name: "A")
let b = a.copy()
b.name = "B"
print(a.name, b.name) //-> "A B"
We can also conform an Array of Codable objects to Copyable and access the copy function instantly:
extension Array: Copyable where Element: Codable {}
Example:
let originalArr = [MyCodableObject(name: "a"),
MyCodableObject(name: "b")]
let copyArr = originalArr.copy()
copyArr.forEach { (obj) in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
IMO, the simplest way to achieve this is :
protocol Copyable
{
init(other: Self)
}
extension Copyable
{
func copy() -> Self
{
return Self.init(other: self)
}
}
Implemented in a struct as :
struct Struct : Copyable
{
var value: String
init(value: String)
{
self.value = value
}
init(other: Struct)
{
value = other.value
}
}
And, in a class, as :
class Shape : Copyable
{
var color: NSColor
init(color: NSColor)
{
self.color = color
}
required init(other: Shape)
{
color = other.color
}
}
And in subclasses of such a base class as :
class Circle : Shape
{
var radius: Double = 0.0
init(color: NSColor, radius: Double)
{
super.init(color: color)
self.radius = radius
}
required init(other: Shape)
{
super.init(other: other)
if let other = other as? Circle
{
radius = other.radius
}
}
}
class Square : Shape
{
var side: Double = 0.0
init(color: NSColor, side: Double)
{
super.init(color: color)
self.side = side
}
required init(other: Shape)
{
super.init(other: other)
if let other = other as? Square
{
side = other.side
}
}
}
If you want to be able to copy an array of Copyable types :
extension Array where Element : Copyable
{
func copy() -> Array<Element>
{
return self.map { $0.copy() }
}
}
Which then allows you to do simple code like :
{
let shapes = [Circle(color: .red, radius: 5.0), Square(color: .blue, side: 5.0)]
let copies = shapes.copy()
}
In my opinion, more Swifty way is to use associated type in Copyable protocol which allows define return type for method copy. Other ways don't allow to copy an object tree like this:
protocol Copyable {
associatedtype V
func copy() -> V
func setup(v: V) -> V
}
class One: Copyable {
typealias T = One
var name: String?
func copy() -> V {
let instance = One()
return setup(instance)
}
func setup(v: V) -> V {
v.name = self.name
return v
}
}
class Two: One {
var id: Int?
override func copy() -> Two {
let instance = Two()
return setup(instance)
}
func setup(v: Two) -> Two {
super.setup(v)
v.id = self.id
return v
}
}
extension Array where Element: Copyable {
func clone() -> [Element.V] {
var copiedArray: [Element.V] = []
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
let array = [One(), Two()]
let copied = array.clone()
print("\(array)")
print("\(copied)")
Copyable instances in swift
NOTE:
The great thing about this approach to copying Class instances is that it doesn't rely on NSObject or objc code, and most importantly it doesn't clutter up the "Data-Style-Class". Instead it extends the protocol that extends the "Data-Style-Class". This way you can compartmentalize better by having the copy code in another place than the data it self. The inheritance between classes is also taken care of as long as you model the protocols after the classes. Here is an example of this approach:
protocol IA{var text:String {get set}}
class A:IA{
var text:String
init(_ text:String){
self.text = text
}
}
extension IA{
func copy() -> IA {
return A(text)
}
}
protocol IB:IA{var number:Int {get set}}
class B:A,IB{
var number:Int
init(_ text:String, _ number:Int){
self.number = number
super.init(text)
}
}
extension IB{
func copy() -> IB {
return B(text,number)
}
}
let original = B("hello",42)
var uniqueCopy = original.copy()
uniqueCopy.number = 15
Swift.print("uniqueCopy.number: " + "\(uniqueCopy.number)")//15
Swift.print("original.number: " + "\(original.number)")//42
NOTE:
To see an implementation of this approach in real code: Then check out this Graphic Framework for OSX: (PERMALINK) https://github.com/eonist/Element/wiki/Progress2#graphic-framework-for-osx
The different shapes uses the same style but each style uses a style.copy() call to create an unique instance. Then a new gradient is set on this copy rather than on the original reference like this:
The code for the above example goes like this:
/*Gradients*/
let gradient = Gradient(Gradients.red(),[],GradientType.Linear,π/2)
let lineGradient = Gradient(Gradients.teal(0.5),[],GradientType.Linear,π/2)
/*Styles*/
let fill:GradientFillStyle = GradientFillStyle(gradient);
let lineStyle = LineStyle(20,NSColorParser.nsColor(Colors.green()).alpha(0.5),CGLineCap.Round)
let line = GradientLineStyle(lineGradient,lineStyle)
/*Rect*/
let rect = RectGraphic(40,40,200,200,fill,line)
addSubview(rect.graphic)
rect.draw()
/*Ellipse*/
let ellipse = EllipseGraphic(300,40,200,200,fill.mix(Gradients.teal()),line.mix(Gradients.blue(0.5)))
addSubview(ellipse.graphic)
ellipse.draw()
/*RoundRect*/
let roundRect = RoundRectGraphic(40,300,200,200,Fillet(50),fill.mix(Gradients.orange()),line.mix(Gradients.yellow(0.5)))
addSubview(roundRect.graphic)
roundRect.draw()
/*Line*/
let lineGraphic = LineGraphic(CGPoint(300,300),CGPoint(500,500),line.mix(Gradients.deepPurple()))
addSubview(lineGraphic.graphic)
lineGraphic.draw()
NOTE:
The copy call is actually done in the mix() method. This is done so that code can be more compact and an instance is conveniently returned right away.
PERMALINK for all the supporting classes for this example: https://github.com/eonist/swift-utils
Only if you are using ObjectMapper library :
do like this
let groupOriginal = Group(name:"Abc",type:"Public")
let groupCopy = Mapper<Group>().mapAny(group.toJSON())! //where Group is Mapable
Swift making copies of passed class instances
If you use the code in the accepted answer(the OP answered their own question) here, so long as your class is a subclass of NSObject and uses the Copying protocol in that post it will work as expected by calling the copyOfValues() function.
With this, no tedious setup or copy functions where you need to assign all the instance variables to the new instance.
I should know, I wrote that code and just tested it XD