Subclass Type as closure parameter - swift

Usecase
I have a superclass (FirebaseObject) with subclasses for most data items in my Firebase (ex: RecipeItem, User). I made a function in the superclass that automatically updates the data that is in the subclass, now I am trying to make a function with closures that get called when the object is updated.
Code
class FirebaseObject {
private var closures: [((FirebaseObject) -> Void)] = []
public func didChange(completion: #escaping (((FirebaseObject) -> Void))) {
// Save closures for future updates to object
closures.append(completion)
// Activate closure with the current object
completion(self)
}
//...
}
This calls the closure with the initial object and saves it for later updates. In my Firebase observer I can now activate all the closures after the data is updated by calling:
self.closures.forEach { $0(self) }
To add these closures that listen for object changes I need to do:
let recipeObject = RecipeItem(data)
recipeObject.didChange { newFirebaseObject in
// Need to set Type even though recipeObject was already RecipeItem
// this will never fail
if let newRecipeObject = newFirebaseObject as? RecipeItem {
// Do something with newRecipeObject
}
}
Question
Is there a way to have the completion handler return the type of the subclass so I don't have to do as? Subclass even though it won't ever fail? I tried to do this with generic type but I can't figure it out and I am not sure if this is the correct solution.
I would like to keep most code in the FirebaseObject class so I don't need to add a lot of code when creating a new subclass.
Edit
Based on this article I tried to add the type when creating a subclass:
class RecipeItem: FirebaseObject<RecipeItem> {
//...
}
class FirebaseObject<ItemType> {
private var handlers: [((ItemType) -> Void)] = []
public func didChange(completion: #escaping (((ItemType) -> Void))) {
//...
This compiles but it crashes as soon as RecipeItem is initialised. I also tried
class RecipeItem: FirebaseObject<RecipeItem.Type> {
//...
}
But this gives an interesting compiler error when I try to access RecipeItem data in didChange closure:
Instance member 'title' cannot be used on type 'RecipeItem'

Ok, so I've been working on this for a day and I have found a way to do it using the method in this answer for the didChange and initObserver functions and taking inspiration from this way of saving data in extensions.
First off, all the functions that need to use the type of the subclass are moved to a protocol.
protocol FirebaseObjectType {}
extension FirebaseObjectType where Self: FirebaseObject {
private func initObserver(at ref: DatabaseReference) {
//...
}
mutating func didChange(completion: #escaping (((Self) -> Void))) {
if observer == nil {
// init Firebase observer here so there will be no Firebase
// observer running when you don't check for changes of the
// object, and so the Firebase call uses the type of whatever
// FirebaseObject this function is called on eg:
// RecipeItem.didChange returns RecipeItem
// and NOT:
// RecipeItem.didChange returns FirebaseObject
initObserver(at: ref)
}
if closureWrapper == nil {
// init closureWrapper here instead of in init() so it uses
// the class this function is called on instead of FirebaseObject
closureWrapper = ClosureWrapper<Self>()
}
// Save closure for future updates to object
closures.append(completion)
// Activate closure with current object
completion(self)
}
}
To save the closures I now use a wrapper class so I can do type checking on that. In FirebaseObject:
class ClosureWrapper<T> {
var array: [((T) -> Void)]
init() {
array = []
}
}
fileprivate var closureWrapper: AnyObject?
Now I can get the closures with the right type in FirebaseObjectType protocol:
private var closures: [((Self) -> Void)] {
get {
let closureWrapper = self.closureWrapper as? ClosureWrapper<Self>
return closureWrapper?.array ?? []
}
set {
if let closureWrapper = closureWrapper as? ClosureWrapper<Self> {
closureWrapper.array = newValue
}
}
}
I can now use didChange on a FirebaseObject subclass without checking its type every time.
var recipe = RecipeItem(data)
recipe.didChange { newRecipe in
// Do something with newRecipe
}

Related

Executiong closure on array modification

I have the following code:
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
This prints "hi" if I add or remove an item from the array or if I do
Global.notes = []
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
Thanks for your answers
Without changing the class to a struct, I have two basic ways to handle this.
This is the object you asked about
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Wrap Notes in a wrapper that is a struct to get the struct behavior.
extension Note {
struct Wrapper { let note: Note }
}
extension Global {
static var wrappedNotes = [Note.Wrapper]() {
didSet {
print("hi")
}
}
}
Global.wrappedNotes.append(Note.Wrapper(note: Note()))
Global.wrappedNotes[0] = Note.Wrapper(note: Note())
Global.wrappedNotes.remove(at: 0)
The other way is to create a note manager to wrap access to the array.
class NoteManager {
subscript(index: Int) -> Note {
get {
return values[index]
}
set {
defer { onUpdate() }
values[index] = newValue
}
}
func append(_ newNote: Note) {
defer { onUpdate() }
values.append(newNote)
}
func remove(at index: Int) -> Note {
defer { onUpdate() }
return values.remove(at: index)
}
private func onUpdate() {
print("hi")
}
private var values = [Note]()
}
extension Global {
static var managedNotes = NoteManager()
}
Global.managedNotes.append(Note())
Global.managedNotes[0] = Note()
Global.managedNotes.remove(at: 0)
As per #staticVoidMan comment , If you make your model , a struct, rather than a class, then the property observer didSet will work for your Note model's own properties as well.
import Foundation
struct Note {
var name: String
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Global.notes.append(Note(name: "Shubham"))
Global.notes.append(Note(name: "Bakshi"))
Global.notes[0].name = "Boxy"
This will print the following on the console :
hi
hi
hi
Swift Array is a struct, and structs are value-type which means they change completely when elements are added/removed/replaced. Hence when you add/remove/replace a Note, the didSet property observer gets called as the array has been set again.
However, as per you question:
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
By this I am assuming that you want to do something when an element within this array is accessed and an internal property is modified.
This would have been fine if you were dealing with only value-type objects, i.e. had your Note object also been a struct, then changing anything inside one Note would have caused the array to change as well.
But your Note object is a class, i.e. reference-type, and stays as the same object even if it's internal elements change. Hence your array doesn't need to update and didSet does not get called.
Read: Value and Reference Types
KVO Solution:
Now... Since your Note is subclassing NSObject, you can use the KVO concept
As per the following working example, we observe only one property of the Note class.
If you want to observe more properties then you will need to observe those many more keypaths.
Example:
class Note: NSObject {
#objc dynamic var content = ""
init(_ content: String) {
self.content = content
}
}
class NoteList {
var notes: [Note] = [] {
didSet {
print("note list updated")
//register & save observers for each note
self.noteMessageKVOs = notes.map { (note) -> NSKeyValueObservation in
return note.observe(\Note.content, options: [.new, .old]) { (note, value) in
print("note updated: \(value.oldValue) changed to \(value.newValue)")
}
}
}
}
//array of observers
var noteMessageKVOs = [NSKeyValueObservation]()
}
let list = NoteList()
list.notes.append(Note("A")) //note list updated
list.notes.append(Note("B")) //note list updated
list.notes[0].content = "X" //note updated: A changed to X
list.notes[1].content = "Y" //note updated: B changed to Y
Notes:
NSObject is required for KVO
#objc dynamic is required to make a property observable
\Note.message is a keypath
noteMessageKVOs are required to keep the observers alive

Why can't closures close over instance members

I really liked Sulthan's answer (in Anonymous class in swift)
which describes building an object which conforms to a protocol but whose class is hidden inside a closure. This would be nice for building singletons and not polluting the name-space with classes like Recorder1, Recorder2...
However, when I try to do anything useful with it, I fail because the closure will not close over the outer class' instance members inside the inner class.
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Recorder {
static var events = [Int]() // static is forced
var listener: EventListener = {
class R : EventListener {
func handleEvent(event: Int) {
events.append(event)
print("Recorded: \(event)")
}
}//
return R()
}()
}// Recorder
class Distributor {
var listeners = [EventListener]()
func register(listener: EventListener){
listeners.append(listener)
}
func distribute(event: Int){
for listener in listeners {
listener.handleEvent(event)
}
}
}
var d = Distributor()
var r1 = Recorder()
var r2 = Recorder()
d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)
print(Recorder.events) // [10, 10] Same event recorded twice.
The above compiles and runs. But I want events in Recorder to be an instance member so that each Recorder has its own record. Removing static throws the compiler error: instance member 'events' cannot be used.
I have tried defining an instance func record(event) in Recorder for handleEvent(event) to call, but I get the same error.
Marius's answer (in Instance member cannot be used on type | Closures) suggests you can't access instance members while properties are being defined so I also tried to compute listener later like this.
class Recorder {
var events = [Int]()
var listener: EventListener {
class R : EventListener {
func handleEvent(event: Int) {
events.append(event) // error: can't access events
print("Recorded: \(event)")
}
}
return R()
}
}// Recorder
But the compiler says it can't access the the outer self.
Closures seem pretty impotent if they can't access outer selfs. In Java, you can get to outer selfs with something like Recorder.self.events. And Recorder.self. might only be necessary if there are name clashes(?)
Is Swift designed to be this way or what am I missing?
How would you write it so Recorder gives Distributor an object that can do nothing but receive handleEvent messages?
Thanks so much.
I don't believe that there is a way to close over events in a partially init'ed Recorder. However you can create a two stage initialization process for Recorder. Stage one creates the Recorder object with a default, empty listener. Stage two creates a proper closure over events and assigns it to listener.
This implementation does not have an EventListener protocol. I am sure there is a simple way to do that but for nested classes it seems overkill. So instead of Distributor containing an array of objects, containing closures. We are letting Distributor contain the array of closures directly.
Except for the additional call to initialize, your API does not change.
typealias EventListener = (Int) -> ()
class Distributor {
var listeners = [EventListener]()
func register(listener: EventListener){
listeners.append(listener)
}
func distribute(event: Int){
for listener in listeners {
listener(event)
}
}
}
class Recorder {
var events = [Int]()
var listener: EventListener = {
(event: Int) in
fatalError("Listener not initialized")
}
func initialize() {
listener = {
(event: Int) in
self.events.append(event)
print("Recorded: \(event)")
}
}
}
var d = Distributor()
// 1st stage of Recorder instantiation
var r1 = Recorder()
var r2 = Recorder()
// 2nd stage
// r1.listener can now have a closure that includes an init'ed events
r1.initialize()
r2.initialize()
d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)
print(r1.events)
print(r2.events)
Using Price Ringo's 2 stage initialisation, and having Recorder pass self into the inner class, I have gotten the class hiding I was looking for. It is a little more verbose than I was hoping but has the advantage of making the life-cycle of Recorder's instance members clear. For example it avoids copy-vs-reference confusion surrounding events. self also makes it easy to pass data the other way: adjustment.
Price Ringo's solution is more elegant and appropriate to this specific problem. This solution is more general and will work with any existing protocol. In general, I am trying to incorporate protocol-oriented patterns into my code. This solution creates objects which have no type other than the protocol they conform to.
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Recorder {
var events = [Int]() // example of data out
let adjustment: Int // example of data in
var listener: EventListener = {
class __: EventListener {
func handleEvent(event: Int) { }
}
return __()
}()
init(adjustment: Int){
self.adjustment = adjustment
initializeListener()
}
private func initializeListener() {
listener = {
class __: EventListener {
unowned var recorder: Recorder
init(recorder: Recorder){
self.recorder = recorder
}
func handleEvent(event: Int) {
let adjustedEvent =
recorder.adjustment * event
recorder.events.append(adjustedEvent)
// print("Recorded: \(adjustedEvent)")
}
}
return __(recorder: self)
}()
}
}// Recorder
class Distributor {
var listeners = [EventListener]()
func addListener(listener: EventListener){
listeners.append(listener)
}
func distributeEvent(event: Int){
for listener in listeners {
listener.handleEvent(event)
}
}
}
var d = Distributor()
var r1 = Recorder(adjustment: 2)
var r2 = Recorder(adjustment: 3)
d.addListener(r1.listener)
d.addListener(r2.listener)
d.distributeEvent(10)
print(r1.events) // [20]
print(r2.events) // [30]
If you are still reading, I have a question. I have made instance member __>recorder unowned to avoid retention. Do I also need to make Recorder>listener unowned?

How to create an array of instances of a subclass from a superclass

From this answer, I know that I can create an instance of a subclass from a superclass. Yet, I can't figure out how to create an array of the subclass from the superclass.
Drawing on the above example, here's my best shot so far:
class Calculator {
func showKind() { println("regular") }
required init() {}
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("\(model) - Scientific") }
required init() {
super.init()
}
}
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> T {
let subclass: T.Type = T.self
var calculators = [subclass]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
let scis: [ScientificCalculator] = ScientificCalculator.createMultiple(2)
for sci in scis {
sci.showKind()
}
With that code, the line var calculators = [subclass]() shows the error Invalid use of '()' to call a value of non-function type '[T.Type]'.
How can I return an array of ScientificCalculators from Calculator.createMultiple?
You were on the right track but you've made some mistakes.
First you need to return a array of T and not just a single element. So you need to change the return type from T to [T]:
class func createMultiple<T:Calculator>(num: Int) -> [T] {
Also you can just use T to initialize new instances of your subclass like that:
var calculators:[T] = [T]()
But the other parts are correct. So you final method would look like that:
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
var calculators = [T]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
Edit
If you are using Swift 1.2 you don't have to deal with subclass anymore and you will be able to use T instead like shown in Airspeeds answer.
calculators.append(T())
EDIT: this behaviour appears to have changed in the latest Swift 1.2 beta. You shouldn’t need to use T.self. T is the type you want to create. But if you are using 1.1, it appears not to work (even if T is the subtype, it creates the supertype), and using the metatype to create the type works around this problem. See end of answer for a 1.1 version.
You don’t need to mess with subclass: T.Type = T.self. Just use T – that itself is the type (or rather, a placeholder for whatever type is specified by the caller):
extension Calculator {
// you meant to return an array of T, right?
class func createMultiple<T: Calculator>(num: Int) -> [T] {
// declare an array of T
var calculators = [T]()
for i in 0..<num {
// create new T and append
calculators.append(T())
}
return calculators
}
}
btw, you can replace that for loop with map:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
return map(0..<num) { _ in T() }
}
If you are still on Swift 1.1, you need to use T.self to work around a problem where the subtype is not properly created:
extension Calculator {
// only use this version if you need this to work in Swift 1.1:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
return map(0..<num) { _ in subclass() }
}
}

Array of classes which implement a specific protocol in Swift

Having problem calling class methods on an array of classes implementing a protocol. Swift think array contains protocols so it gives the following compile error.
Accessing member of protocol type value 'TransactionHandler.Type' is unimplemented.
I DO NOT want to save actual instances in the array
protocol TransactionHandler {
class func canHandleTransaction(transaction: SKPaymentTransaction) -> Bool
func handleTransaction(transaction: SKPaymentTransaction)
}
class InAppPurchaseManager: NSObject, SKPaymentTransactionObserver {
var handlers = [TransactionHandler.Type]()
override init() {
super.init()
// TransactionHandler implementations
// handlers.append(Product1TransactionHandler.self)
// handlers.append(Product2TransactionHandler.self)
// handlers.append(Product3TransactionHandler.self)
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
for transaction in transactions {
for handler in handlers {
// Don't want to save instances in the array, that's waste of memory
// Compile errors all over the place, can be run in playground
if handler.canHandleTransaction(transaction as SKPaymentTransaction) {
let handlerInstance = handler()
handlerInstance.handleTransaction(transaction)
}
}
}
}
}
Why did you named your protocol with the same name of your object TransactionHandler ?
1 / suffix your protocolName : TransactionHandlerProtocol or TransactionHandlerDelegate
2 / and your Class TransactionHandler
Edit: Sorry, i read too fast
Edit 2 :
Remove your .Type
var handlers = [TransactionHandler]()
handlers array of class conform to protocol TransactionHandler

Anonymous class in swift

Is there an equivalent syntax or technique for Anonymous class in Swift?
Just for clarification Anonymous class in Java example here - http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
Thanks!
There is no equivalent syntax, as far as I know.
Regarding equivalent techniques, theoretically you could use closures and define structs and classes inside them. Sadly, I can't get this to work in a playground or project without making it crash. Most likely this isn't ready to be used in the current beta.
Something like...
protocol SomeProtocol {
func hello()
}
let closure : () -> () = {
class NotSoAnonymousClass : SomeProtocol {
func hello() {
println("Hello")
}
}
let object = NotSoAnonymousClass()
object.hello()
}
...currently outputs this error:
invalid linkage type for global declaration
%swift.full_heapmetadata* #_TMdCFIv4Test7closureFT_T_iU_FT_T_L_19NotSoAnonymousClass
LLVM ERROR: Broken module found, compilation aborted!
Command /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 1
You can also create a basic empty class that acts like a bare protocol, and pass a closure to the init function that overrides anything you want, like this:
class EmptyClass {
var someFunc: () -> () = { }
init(overrides: EmptyClass -> EmptyClass) {
overrides(self)
}
}
// Now you initialize 'EmptyClass' with a closure that sets
// whatever variable properties you want to override:
let workingClass = EmptyClass { ec in
ec.someFunc = { println("It worked!") }
return ec
}
workingClass.someFunc() // Outputs: "It worked!"
It is not technically 'anonymous' but it works the same way. You are given an empty shell of a class, and then you fill it in or override whatever parameters you want when you initialize it with a closure.
It's basically the same, except instead of fulfilling the expectations of a protocol, it is overriding the properties of a class.
For example, Java listener/adapter pattern would be translated to Swift like this:
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Adapter : EventListener {
func handleEvent(event: Int) -> () {
}
}
var instance: EventListener = {
class NotSoAnonymous : Adapter {
override func handleEvent(event: Int) {
println("Event: \(event)")
}
}
return NotSoAnonymous()
}()
instance.handleEvent(10)
(Crashing the compiler on Beta 2)
The problem is, you always have to specify a name. I don't think Apple will ever introduce anonymous classes (and structs etc.) because it would be pretty difficult to come with a syntax that doesn't collide with the trailing closures.
Also in programming anonymous things are bad. Naming things help readers to understand the code.
No anonymous class syntax in Swift. But, you can create a class inside a class and class methods:
class ViewController: UIViewController {
class anonymousSwiftClass {
func add(number1:Int, number2:Int) -> Int {
return number1+number2;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
class innerSwiftClass {
func sub(number1:Int, number2:Int) -> Int {
return number1-number2;
}
}
var inner = innerSwiftClass();
println(inner.sub(2, number2: 3));
var anonymous = anonymousSwiftClass();
println(anonymous.add(2, number2: 3));
}
}
This is what I ended up doing (Observer pattern). You can use closures in a similar way you would use anonymous classes in Java. With obvious limitations of course.
class Subject {
// array of closures
var observers: [() -> Void] = []
// #escaping says the closure will be called after the method returns
func register(observer: #escaping () -> Void) {
observers.append(observer)
}
func triggerEvent() {
observers.forEach { observer in
observer()
}
}
}
var subj = Subject()
// you can use a trailing closure
subj.register() {
print("observerd")
}
// or you can assign a closure to a variable so you can maybe use the reference to removeObserver() if you choose to implement that method
var namedObserver: () -> Void = {
print("named observer")
}
subj.register(observer: namedObserver)
subj.triggerEvent()
// output:
// observerd
// named observer
If you want to inline a click handler in Java style fashion, first define your closure as a variable in your button class:
var onButtonClick: () -> Void = {}
Then add a method to accept the closure as parameter and store it in the variable for later use:
func onClick(label: String, buttonClickHandler: #escaping () -> Void) {
button.label = label
onButtonClick = buttonClickHandler
}
Whenever the closure should be executed, call it in your button class:
onButtonClick()
And this is how to set the action that should occur on click:
newButton.onClick(label: "My button") { () in
print("button clicked")
}
You can also accept multiple parameters. For example, a toggle button may be handled like this:
var buttonClicked: (_ isOn: Bool) -> Void { set get }
Simply use a struct for defining the interface via function values and then anonymously implement it from a function, as is a very common way to write objects in JavaScript.
The function is only required for creating a private scope for the object returned.
import Foundation
struct Logger {
let info: (String) -> ()
let error: (String) -> ()
}
func createSimpleLogger() -> Logger {
var count = 0
func number() -> Int {
count += 1
return count
}
return Logger(
info: { message in
print("INFO - #\(number()) - \(message)")
},
error: { message in
print("ERROR - #\(number()) - \(message)")
}
)
}
let logger = createSimpleLogger()
logger.info("Example info log message")
logger.error("Example error log message")
Output:
INFO - #1 - Example info log message
ERROR - #2 - Example error log message