Adding Swift Closures as values to Swift dictionary - ios

I want to create a Swift dictionary that holds String type as its keys and Closures as its values. Following is the code that I have but it gives me the error:
'#lvalue is not identical to '(String, () -> Void)'
class CommandResolver {
private var commandDict:[String : () -> Void]!
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
println("I am inside closure");
}
}
}
I tried looking at other question on StackOverflow regarding closures in dictionaries but it does not give me any satisfactory answer. So I would really appreciate some help here.

Here is the way to go. I am not sure exactly why your implementation does not work though.
class CommandResolver {
typealias MyBlock = () -> Void
private var commandDict:[String : MyBlock] = [String:MyBlock]()
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
print("I am inside closure");
}
}
}

If you initialize your dictionary in your init before calling your setup function, it should work:
class CommandResolver {
private var commandDict: [String: () -> Void]
init() {
commandDict = [:]
setUpCommandDict()
}
func setUpCommandDict() {
commandDict["OpenAssessment_1"] = {
println("I am inside closure")
}
}
}

Related

Pass a closure to replace delegation in swift

I have used blocks to get callbacks from other classes. I am a beginner in swift. So I need to find a way to define a closure in one class and assign it to a closure variable in another class. I will be calling this closure to get callback of second class in first class.
What I want is something like this,
Class A {
func viewdidload() {
let b:B = B()
b.closure(string:NSString) = {
print string
}
}
}
class B {
var closure(NSString);
func () {
closure(string)
}
}
Here, but you should really learn Swift first
class A {
func viewdidload() {
let b = B()
b.closure = { str in
print(str)
}
}
}
class B {
var closure : ((String) -> Void)?
func t() {
closure?("hi")
}
}
Try this in a Playground:
class B {
var closure: (NSString) -> NSString
init() {
closure = { input in
return "NSString from closure: \(input)"
}
}
}
class A {
init() {
let b:B = B()
print(b.closure("hello from A"))
b.closure = { input in
return "NSString from another closure: \(input)"
}
print(b.closure("hello from A"))
}
}
A();
will print
NSString from closure: hello from A
NSString from another closure: hello from A

(ObjC -> Swift) Convert function "init" & "textureNamed"

I get some errors when I try to convert 2 functions in Swift!
ObjC
#property (nonatomic, strong) NSMutableDictionary *textureCache;
- (instancetype)init {
if (self = [super init])
{
self.textureCache = [NSMutableDictionary dictionary];
}
return self;
}
- (SKTexture *)textureNamed:(NSString *)textureName {
return _textureCache[textureName];
}
Swift
var textureCache: NSMutableDictionary?
public func init() -> Self {
return textureCache = NSMutableDictionary()
}
public func textureNamed(textureName: String!) -> SKTexture! {
return textureCache(textureName)
}
In the "init" function :
Expected identifier in function declaration -> (public func init ->
Self)
Cannot assign a value of type 'NSMutableDictionary' to a value of type 'NSMutableDictionary?' -> (return textureCache = NSMutableDictionary())
And for the "textureNamed" function :
Cannot invoke 'textureCache' with an argument list of type '(String!)'
-> (return textureCache(textureName))
If someone could help me, it would be awesome.
I would try something like this:
class Test: NSObject {
var textureCache = [String:SKTexture]()
override init() {
super.init()
}
func textureNamed(name: String) -> SKTexture? {
return self.textureCache[name]
}
}
Ok, let's decompose the problem.
Swift doesn't need a return type for init and if you have a superclass, you need to call super.init():
public func init() {
super.init() //required if you have a superclass as the override after public
textureCache = NSMutableDictionary()
}
Second problem, the method looks correct, the problem is related to the fact that textureCache is an Optional, so you have two solutions here.
Unwrap the variable and return the cached texture cached:
public func textureNamed(textureName: String!) -> SKTexture! {
return textureCache?(textureName)
}
Or declare textureCache as non-optional value, because you are actually initializing it in the init method:
var textureCache: NSMutableDictionary
public func textureNamed(textureName: String!) -> SKTexture! {
return textureCache(textureName)
}
Thanks for your answers, it doesn't work in the both cases ! ^^
But you helped me a lot cuz I "found" a solution :
public init() {
textureCache = NSMutableDictionary()
}
public func textureNamed(textureName: String!) -> SKTexture! {
return textureCache?[textureName] as! SKTexture!
}
But I'm still not sure sure, the errors disappeared hoping it will not crash later...

Find/Remove Closure in Array

I'm developing first app using Swift.
in one of my Class I need to store closure in an Array. Like an event manager :
typealias eventCallback = (eventName:String, data:[MasterModel]?) -> Void;
class MHttpLoader: NSObject
{
var eventRegister : [String: [eventCallback]];
class var instance : MHttpLoader
{
struct Static {
static let instance : MHttpLoader = MHttpLoader(baseURL:NSURL(string:"http://192.168.0.11:3000"));
}
return Static.instance;
}
class func registerEvent(eventName:String, callback:eventCallback)
{
if var tab = MHttpLoader.instance.eventRegister[eventName]
{
tab.append(callback);
}
else
{
MHttpLoader.instance.eventRegister[eventName] = [callback];
}
}
func fireEvent(eventName: String, data:[MasterModel]?)
{
if let tab = self.eventRegister[eventName]
{
for callback in tab
{
callback(eventName:eventName, data:data);
}
}
}
}
All this code work pretty well, the problem is when i want to remove a closure from my array.
For exemple :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
if let index = find(tab, callback) as? Int
{
tab.removeAtIndex(index);
}
}
}
I have the error which says that my closure is not conform to protocol "Equatable"
I also tried :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
tab = tab.filter({ (currentElement) -> Bool in
return currentElement != callback;
});
}
}
But I have the error : Cannot invoke '!=' with an argument list of type '((eventCallback), eventCallback)'
Here is my question how can i find the index of closure in array or simply compare closure?
Thank you

Custom sequence for Swift Dictionary

I have a container class that has an underlying dictionary. I have implemented subscripts for this class to access member of the underlying dictionary. Now, I am trying to create a sequence on this class so that I could iterate over all the elements of the underlying dictionary by using 'for-in' loop on the class instance itself. I have been looking to find some examples for Sequences for Swift Dictionary but could not find anything that explains the stuff well. I have seen some custom sequence examples for Swift Array but none for the Swift Dictionary. I would really appreciate if anyone could explain how I can achieve that. Following is the code for the class (no sequence code yet as I am not sure where to begin)
import Foundation
class STCQuestionList : GeneratorType, SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if (key != nil) {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if (key != nil) {
self.questionDict[key!] = newValue;
}
}
}
func generate() -> GeneratorType {
}
func next() -> (String, STCQuestion)? {
if (self.questionDict.isEmpty) {
return .None
}
}
}
If I'm understanding correctly, how about just forwarding on the generate?
func generate() -> DictionaryGenerator<String, STCQuestion> {
return questionDict.generate()
}
(You don't need to implement GeneratorType, just SequenceType should do. It's generate() itself that returns a GeneratorType, and that's what has to implement next(), which the existing generate() implementation in Dictionary already does for you.)
Full worked example based on your code:
// Playground - noun: a place where people can play
import Foundation
class STCQuestion {
let foo: String
init(_ foo: String) {
self.foo = foo
}
}
class STCQuestionList : SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if key != nil {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if key != nil {
self.questionDict[key!] = newValue;
}
}
}
func generate() -> DictionaryGenerator<String, STCQuestion> {
return questionDict.generate()
}
}
var list = STCQuestionList()
list["test"] = STCQuestion("blah")
list["another"] = STCQuestion("wibble")
list["third"] = STCQuestion("doodah")
for (key, value) in list {
println("Key: \(key) Foo: \(value.foo)")
}
// Output:
// Key: test Foo: blah
// Key: another Foo: wibble
// Key: third Foo: doodah
(Note: I re-thought this -- original answer via the edited page...)
Swift has a generic GeneratorOf type that you can use to create a generator. You just provide a closure that returns the next value in the initializer:
class STCQuestionList : SequenceType {
private var questionDict: [String : STCQuestion] = [ : ];
subscript(key : String?) -> STCQuestion? {
get {
if (key != nil) {
return self.questionDict[key!];
}
return nil;
}
set(newValue) {
if (key != nil) {
self.questionDict[key!] = newValue;
}
}
}
/// Creates a generator for each (key, value)
func generate() -> GeneratorOf<(String, STCQuestion)> {
var index = 0
return GeneratorOf<(String, STCQuestion)> {
if index < self.questionDict.keys.array.count {
let key = self.questionDict.keys.array[index++]
return (key, self.questionDict[key]!)
} else {
return nil
}
}
}
}
If you don't care about the order, can't you just call the same methods of dictionary or make your class a subclass of a dictionary? For example:
func generate() -> GeneratorType {
return self.questionDict.generate()
}
func next() -> (String, STCQuestion)? {
return self.questionDict.next()
}

Swift Passing Closure With Params

Currently I'm passing in a closure as a property on an object that accepts no params and has no return value as follows:
class MyClass {
var myClosureProperty: (() -> ())? {
didSet {
doSomeActionWhenClosureIsSet()
}
}
}
var instanceOfMyClass = MyClass()
instanceOfMyClass.myClosureProperty = {
// do some things here...
}
and so far this is working great. I want to be able to pass in a parameter when setting this closure to be used inside the instance of MyClass. I'm looking for SOMETHING like below, although the syntax I'm sure is incorrect:
class MyClass {
var myClosureProperty: ((newString: String) -> ())? {
didSet {
doSomeActionWhenClosureIsSet(newString)
}
}
func doSomeActionWhenClosureIsSet(stringParam: String) -> () {
// create a button with the stringParam title...
}
}
var instanceOfMyClass = MyClass()
instanceOfMyClass.myClosureProperty = {("Action")
exampleFunction()
}
How would I go about passing in a parameter to this closure that can be used inside of MyClass - i.e. a value that can be used inside the didSet portion of the property itself as in the second example?
EDIT: Here's what ended up working for me:
class MyClass {
var myClosurePropertyWithStringAndAction: (buttonName: String, closure: (() -> ()))? {
didSet {
let buttonTitle = myClosurePropertyWithStringAndAction!.buttonName
createButtonWithButtonTitle(buttonTitle)
}
}
func createButtonWithButtonTitle(buttonTitle: String) -> () {
// here I create a button with the buttonTitle as the title and set
// handleButtonPressed as the action on the button
}
func handleButtonPressed() {
self.myClosurePropertyWithStringAndAction?.closure()
}
}
}
And here is how I call it on the instance:
instaceOfMyClass.myClosurePropertyWithStringAndAction = ("Done", {
// do whatever I need to here
})
Since you are trying to set pass 2 things, a closure AND a button name, you won't be able to do that with a simple setter to the closure.
The constraint you have is that these 2 things are dependent on each other, so you must either set both or none.
First of all, adding newString to your closure isn't doing what you think it does. Its a parameter so you can pass a string to your closure when you call it, it isn't a way to pass in a string when you define the closure.
A "Swift way" to do what you want might be to define it as a tuple. You can name the values inside a tuple so it would work how you want it. Try it like this:
class MyClass {
var stringAndClosure: (buttonName: String,closure: (() -> ()))? {
didSet {
//create button named buttonName
let name = stringAndClosure!.buttonName
}
}
}
let instanceOfMyClass = MyClass()
instanceOfMyClass.stringAndClosure = ("Action",{ exampleFunction() })
You should use the in keyword to pass parameters in a closure
{ (someString: String) -> Bool in
//do something with someString
return true
}
Don't think is possible... You can do like this instead :
class MyClass{
var myClosureProperty: (() -> String)?
{
didSet
{
doSomeActionWhenClosureIsSet(myClosureProperty!())
}
}
func doSomeActionWhenClosureIsSet(stringParam: String) -> ()
{
println("it worked: " + stringParam) // hopefully this would print "it worked: SUCCESS"
}
}

Resources