Swift - Passing custom objects as a parameter - ios

I started learning Swift today and in my first test app I am getting this error:
TestClass is not convertible to AnotherClass
The following is the TestClass:
class TestClass : NSObject {
var parameter1 : String = ""
var parameter2 : String = ""
override init() {
super.init()
}
func createJob(parameter1: String, parameter2: String) -> TestClass {
self.parameter1 = parameter1
self.parameter2 = parameter2
return self;
}
}
And this is the AnotherClass:
class AnotherClass: NSObject {
private struct internalConstants {
static let test1 = "testData"
static let test2 = "testData2"
}
var current : String
override init() {
self.current = internalConstants.test1
super.init()
}
func executeTask(testClass : TestClass) {
if testClass.parameter1 == "abc" {
return;
}
}
}
And this is the ViewController where I am getting the compiler error:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let obj = TestClass()
AnotherClass.executeTask(obj)
}
}
AnotherClass.executeTask line is giving the compiler error.
The obj variable sent as a parameter on this line is highlighted by Xcode with the error
"TestClass is not convertible to AnotherClass".
In C# or Objective C it is allowed to pass custom objects as a parameter to another methods. How can I do it in Swift?

Let's correct first the TestClass. This is how you should init a class like that:
class TestClass : NSObject {
....
init(parameter1: String, parameter2: String) {
....
}
}
Much simpler. Now, going back to your problem,
"TestClass is not convertible to AnotherClass".
Take a look at it again. The line you've mentioned in your question. You are trying to do this:
let obj = TestClass()
AnotherClass.executeTask(obj)
This line, AnotherClass.executeTask(obj), is giving you an error because indeed executeTask() is an instance method. You could do three ways for that.
add static keyword to the func executeTask... So it becomes like this: static func executeTask(testClass : TestClass) {
Instead of static keyword, you could add class. It becomes like so: class func executeTask(....
OR, better if you just instantiate the AnotherClass. Make a new object of AnotherClass. How to instantiate? You tell me. But here:
let anotherClass = AnotherClass()

Either implement executeTask as a class function
class func executeTask(testClass : TestClass) {
if testClass.parameter1 == "abc" {
return;
}
}
or instantiate AnotherClass in vieweDidLoad
let obj = TestClass()
let another = AnotherClass()
another.executeTask(testClass: obj)
Note the slightly different call to executeTask with the argument name.
And there is really no reason for you to subclass NSObject as I see it.

I think it's best to keep is simple. Create an instance of AnotherClass inside of ViewController.
class ViewController: UIViewController {
// Create an instance of AnotherClass which lives with ViewController.
var anotherClass = AnotherClass()
override func viewDidLoad() {
super.viewDidLoad()
let obj = TestClass()
// Use the instance of AnotherClass to call the method.
anotherClass.executeTask(testClass: obj)
}
}

Related

Sending a variable to be modified in another class?

How can one send a variable from a viewcontroller to be modified by another viewcontroller?
I've tried setting the variable in the performSegue, but it does not get modified.
Sample code:
class VC1: ViewController{
var myVar: MyVar
....
prepare(for segue:...) {
let nextVC = segue.destination as! VC2
nextVC.var = myVar
}
....
}
class VC2: ViewController {
var var: MyVar
....
override func viewDidLoad() {
super.viewDidLoad()
var = MyVar("newValue")
}
}
After this code is executed, the value of myVar in VC1 is not changed to the new value. I believe it only gets a shallow copy of myVar, not a deep copy.
Is what I want achievable in swift?
Classes in Swift are pass by reference, whereas structs are pass by value. Assuming that MyVar is a class, you need to modify the properties of the class, ie:
myVar.property = "xyz"
instead of defining a new instance of the class as you have done in your question.
When you set var = MyVar("newValue") it assign new instance to var.
Examine the results from the following code in a playground. It should give you more insight into what you should expect, without the complication of segues and controllers.
class Variable {
var name:String
init(_ nameString:String) {
name = nameString
}
}
class Sender {
var myVar = Variable("Start name")
func showChanges() {
let receiver = Receiver(myVar)
print(myVar.name)
receiver.modify()
print(myVar.name)
receiver.replace()
print(myVar.name)
}
}
class Receiver {
var received: Variable
init(_ variable:Variable) {
received = variable
}
func modify() {
received.name = "Changed name"
}
func replace() {
received = Variable("New variable")
}
}
let s = Sender()
s.showChanges()

Get Swift class name in "class func" method

I have a static method in Swift
class BaseAsyncTask: WebServiceClient {
class func execute(content : [String:AnyObject], cancelled:CustomBool)
{
// Print class name (BaseAsyncTask) here
}
}
And I want to know how to get the class name inside that method. I tried
self.dynamicType
but that gives error (I suppose because of the self inside a class function)
There are different methods to do that, if your method inherits from NSObject you can expose it to objective-c and do something like that.
#objc(BaseAsyncTask)
class BaseAsyncTask: WebServiceClient {
class func execute(content : [String:AnyObject], cancelled:CustomBool)
{
println("Class \(NSStringFromClass(self))")
}
}
For pure SWIFT introspection check here about MirrorType
I've found also this extension credits to ImpactZero
public extension NSObject{
public class var nameOfClass: String{
return NSStringFromClass(self).components(separatedBy: ".").last!
}
public var nameOfClass: String{
return NSStringFromClass(type(of: self)).components(separatedBy: ".").last!
}
}
[Xcode 8]
Alex suggested me that in the Xcode 8 version this code shows a warning. To avoid that we should prefix the method like that:
#nonobjc class var className: String{
return NSStringFromClass(self).components(separatedBy: ".").last!
}
You can use string interpolation to print self:
let className = "\(self)"
Sample code:
class BaseAsyncTask: WebServiceClient {
class func execute(content : [String:AnyObject], cancelled: CustomBool)
{
let className = "\(self)"
print(className)
}
}
class AnotherAsyncTask : BaseAsyncTask {
}
BaseAsyncTask.execute([:], cancelled: true) // prints "BaseAsyncTask"
AnotherAsyncTask.execute([:], cancelled: true) // prints "AnotherAsyncTask"
Another way to do this, when you don't have an instance of the class is this.
Swift 4
print(String(describing:BaseAsyncTask.self))
Swift 2
print(String(BaseAsyncTask))
Inspired here.
Get class name of object as string in Swift

Is there a way to dynamically instantiate a class type

I would like to do something on the lines of (pseudo code):
let class_name = "MyClass"
let RealClass = class_name()
I am familiar with http://ijoshsmith.com/2014/06/05/instantiating-classes-by-name-in-swift/
You can Use NSClassFromString to get class then cast to the type you want.
For example:
var dynamicClass = NSClassFromString("Test") as? Test.Type
if dynamicClass != nil{
var instance = dynamicClass!()
instance.test()
}
The class Test
#objc(Test)
class Test{
func test() {
println("dynamic")
}
required init() {
}
}

How to call static method provided by protocol in Swift

How to access to static protocol method within a instance
I have a list of Contact, the contact can be a FamilyContact that inherit from Contact and the GroupStatus protocol
I want to call the static method from GroupStatus but in vain...
Here is my code
protocol GroupStatus {
static func isPrivate() -> Bool // static method that indicates the status
}
protocol IsBusy {
func wizzIt()
}
class AdresseBook {
private var contacts = [Contact]()
func addOne(c: Contact) {
contacts.append(c)
}
func listNonPrivated() -> [Contact]? {
var nonPrivateContact = [Contact]()
for contact in contacts {
// here is I should call the static method provided by the protocol
if self is GroupStatus {
let isPrivate = contact.dynamicType.isPrivate()
if !isPrivate {
nonPrivateContact.append(contact)
}
}
nonPrivateContact.append(contact)
}
return nonPrivateContact
}
}
class Contact : Printable {
var name: String
init(name: String) {
self.name = name
}
func wizz() -> Bool {
if let obj = self as? IsBusy {
obj.wizzIt()
return true
}
return false
}
var description: String {
return self.name
}
}
class FamilyContact: Contact, GroupStatus {
static func isPrivate() -> Bool {
return true
}
}
I can't compile Contact.Type does not have a member named 'isPrivate'
How can I call it ? It works if I delete the static keyword, but I think is more logical to define it static.
If I replace
let isPrivate = contact.dynamicType.isPrivate()
by
let isPrivate = FamilyContact.isPrivate()
It works, but I can have more than 1 subclasses
If I remove the static keywork I can do it by this way :
if let c = contact as? GroupStatus {
if !c.isPrivate() {
nonPrivateContact.append(contact)
}
}
But I want to keep the static keyword
This looks like a bug or a non-supported feature. I would expect that
the following works:
if let gsType = contact.dynamicType as? GroupStatus.Type {
if gsType.isPrivate() {
// ...
}
}
However, it does not compile:
error: accessing members of protocol type value 'GroupStatus.Type' is unimplemented
It does compile with FamilyContact.Type instead of GroupStatus.Type. A similar problem is reported here:
Swift 1.1 and 1.2: accessing members of protocol type value XXX.Type' is unimplemented
Making isPrivate() an instance method instead of a class method is
the only workaround that I currently can think of, maybe someone comes
with a better solution ...
Update for Swift 2 / Xcode 7: As #Tankista noted below, this has
been fixed. The above code compiles and works as expected in Xcode 7 beta 3.
type(of: contact).isPrivate()
This should work in recent Swift.

Achieve dynamic initialisation based on Class passed as parameter in Swift

I would like to create objects based on their class passed into a function.
First, I have an interface every generatable object should conform to:
interface Generatable{
init(raw: NSDictionary)
}
and a function that would take the class as a parameter
func generateDynamicObjectFromClass(generatable: Generatable.Type){
var someJSONData : NSDictionary = NSDictionary()
var myGeneratedObject : Generatable = generatable(raw: someJSONData) //custom initialiser of Generatable class
}
and then, call it like that:
generateDynamicObjectFromClass(MyGeneratableObject.Type)
MyGeneratableObject class
class MyGeneratableObject : NSObject, Generatable{
init(raw: NSDictionary){
//some initialisation
}
}
However, MyGeneratableObject does not have a Type property, so the problem is to get the corresponding class of the underlying object during runtime. Is that possible ?
You have to define generateDynamicObjectFromClass as a generic function:
protocol Generatable {
init(raw: NSDictionary)
}
func generateDynamicObjectFromClass<T where T:Generatable>(generatable:T.Type, otherParam: NSString = "") -> T {
var someJSONData : NSDictionary = NSDictionary()
var myGeneratedObject = T(raw: someJSONData)
return myGeneratedObject
}
class MyGeneratableObject : NSObject, Generatable {
init(raw: NSDictionary){
println("MyGeneratableObject init")
}
}
var myObject1 = generateDynamicObjectFromClass(MyGeneratableObject.self, otherParam: "foo")
var myObject2 = generateDynamicObjectFromClass(MyGeneratableObject.self)
Alternatively, you can create the object as
var myObject = MyGeneratableObject(raw: NSDictionary())
without the need for a separate function.

Resources