Optional Variables in protocol is possible? - swift

protocol AProtocol: BProtocol {
/// content to be shown on disclaimer Label of cell
var disclaimer: String {get set}
var cellDisclaimerAttributed: NSAttributedString {get}
var showSelection: Bool {get set}
var isReadMore: Bool {get}
}
I want to make variables optional so that I need not implement all variables every time after conforming protocol. Like in Objective-C we did for methods:
protocol AProtocol: BProtocol {
/// content to be shown on disclaimer Label of cell
optional var disclaimer: String {get set}
optional var cellDisclaimerAttributed: NSAttributedString {get}
optional var showSelection: Bool {get set}
optional var isReadMore: Bool {get}
}
Is it possible?

protocol TestProtocol {
var name : String {set get}
var age : Int {set get}
}
Provide a default extension for the protocol. Provide the default implementation for all the variables set and get which u want them to be optional.
In below protocol, name and age are optional.
extension TestProtocol {
var name: String {
get { return "Any default Name" } set {}
}
var age : Int { get{ return 23 } set{} }
}
Now if I am conforming above protocol to any other class, like
class TestViewController: UIViewController, TestProtocol{
var itemName: String = ""
**I can implement the name only, and my objective is achieved here, that the controller will not give a warning that "TestViewController does not conform to protocol TestProtocol"**
var name: String {
get {
return itemName ?? ""
} set {}
}
}

If you want to conform to Swift's documentation, you'd have to implement it like this :
#objc protocol Named {
// variables
var name: String { get }
#objc optional var age: Int { get }
// methods
func addTen(to number: Int) -> Int
#objc optional func addTwenty(to number: Int) -> Int
}
class Person: Named {
var name: String
init(name: String) {
self.name = name
}
func addTen(to number: Int) -> Int {
return number + 10
}
}

Related

Swift function call and protocol

In swift is it possible to declare a method property not as a type but as a protocol?
like:
protocol myProtocol {
var data1: String {get set}
var node: Int {get set}
}
class myData: myProtocol {
var data1: String = "Boo"
var node: int = 10
}
class myClass {
func myFunc(data: myProtocol) {
data.data1 = "Hello"
}
}
Basically I want to say to the method look I don't care about the type. As long as the object conforms to the protocol its ok
Yes this is fine, but to modify data you will have to declare a class only protocol.
protocol MyProtocol: AnyObject {
var data1: String {get set}
var node: Int {get set}
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
data.data1 = "Hello"
}
}
I've also fixed the capitalisation of your classes.
You can use associatedtype.
protocol MyProtocol {
associatedtype CustomData
var data1: String { get set }
var node: Int { get set }
func myFunc(data: CustomData)
}
class MyData: MyProtocol {
func myFunc(data: String) {
print(data)
}
var data1: String = "Boo"
var node: Int = 10
}
Also, you should use PascalCase for both protocols and classes, and Int is the integer type for swift.
EDIT:
I misunderstood your question. You can also specify a function parameter by an abstract protocol, not just a class or a struct!
protocol MyProtocol {
var data1: String { get set }
var node: Int { get set }
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
print(data.data1)
}
}
let data = MyData()
let instance = MyClass()
instance.myFunc(data: data) // Boo

Function accepting generic parameters

I have a subclass of NSManagedObject. I'm using a protocol to create a "wrapper" class. In my controller the data can be either: Items or Item1. To be able to use my function I'll have to add the protocol ItemInfo to Items but that means I'll have to add
var items: Items { return self }
in Items, which seems a bit redundant. I've tried creating a base class but that didn't work.
Question:
Is there a better way to let my function accept both Items and Item1 as parameter like using generics?
NSManagedObject:
class Items: NSManagedObject {
#NSManaged var name: String
#NSManaged var code: String
}
Protocol:
protocol ItemInfo {
var item: Items { get }
}
extension ItemInfo {
var name : String { return item.name }
var code : String { return item.code }
}
Wrapper:
class Item1: ItemInfo {
let item: Items
init(item: Items) { self.item = item }
}
function:
func handleItem(item: ItemInfo) {
print(item.name)
print(item.code)
}
I could use:
func handleItem<T>(item: T) {
if let a = item as? Items {
print(a.name)
print(a.code)
}
if let a = item as? ItemInfo {
print(a.name)
print(a.code)
}
}
But this doesn't seem the right way ...
If I understand correctly what you are trying to achieve (function accepting two kind of items), I would use protocol as type accepted by function, refer the code below
class Items: NSManagedObject, ItemInfo {
#NSManaged var name: String
#NSManaged var code: String
}
class Item1: NSManagedObject, ItemInfo {
#NSManaged var name: String
#NSManaged var code: String
}
protocol ItemInfo {
var name: String {get set}
var code: String {get set}
}
and function would look like this
func handle(item: ItemInfo) {
print(item.code)
print(item.name)
}

Method cannot be marked #objc because the type of the parameter 2 cannot be represented in Objective-C [duplicate]

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}

Variable that conforms to a protocol that has a generic function

I have a protocol that looks like this:
protocol MyProtocol {
associatedtype SpeedType
var name: String {get set}
func forward(_: SpeedType)
}
I made 2 simple classes that conform to this protocol:
class A: MyProtocol {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MyProtocol {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
What I want to achieve is to be able to declare a variable of type MyProtocol, and initialize it later like so:
let x: Bool = true
var person: MyProtocol
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}
Before I made forward() method "generic" I was able to do this, however now I am getting the next error:
Protocol "MyProtocol" can only be used as generic constraint because it has Self or associated type requirement.
So my goal is to have a method forward() that can take as an argument parameter of a type that I specify, and also be able to declare a variable of a type that conforms to my protocol.
Swift doesn't allow this.
Here's why: you don't know anything about the type of argument person.forward(_:) takes. There is no way to call it. MyProtocol essentially defines an open-ended set of independent types.
If you don't want to be able to call person.forward(_:), and you just want to be able to access the non-generic person.name property, then split your protocol into a base, non-generic protocol defining name, and a sub-protocol that adds the generic forward(_:) method.
protocol NamedThing {
var name: String {get set}
}
protocol MovableNamedThing: NamedThing {
associatedtype SpeedType
func forward(_: SpeedType)
}
class A: MovableNamedThing {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MovableNamedThing {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
let x: Bool = true
var person: NamedThing
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}

Swift 2.0 Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}