Swift doesn't recognize Type variables - ios

In my project I am having an issue with Swift, that it doesn't recognize variables containing protocol types. This means I can't use a variable that stores a type, to check if the type of an instance matches it.
I attached problematic part of the code with some brief context.
Is this some kind of bug or am I overseeing something really badly?
Using XCode 7.3, Swift 2.2
//Context of Issue BEGIN
class TaskValueObject {
//ManyData, VeryComplexity, SoBig, wow..
}
typealias TaskListSorterBlock = (TaskValueObject, TaskValueObject) -> Bool
protocol TaskListSorter : class {
init()
var localizedTitle : String { get }
var sorterBlock : TaskListSorterBlock { get }
}
class PriorityTaskListSorter : TaskListSorter {
// ... Implementation ...
}
// Many other TaskListSorter implementation classes ...
//Context of Issue END
class TaskListContainer {
weak var currentSorter : TaskListSorter?
var sorters : [TaskListSorter]
init() {
self.sorters = [ PriorityTaskListSorter(), /* ... Instances created for <TaskListSorter> implementing class ... */ ]
loadDefaultSorter()
}
static var defaultSorterType : TaskListSorter.Type = PriorityTaskListSorter.self
private func loadDefaultSorter() {
let defaultSorterType = TaskListContainer.defaultSorterType
for sorter in self.sorters {
if sorter is defaultSorterType {
// ^ ERROR HERE : defaultSorterType is not a 'type'
self.currentSorter = sorter
}
}
}
}
Update 1: I get the same error if I replace the problematic line with the following:
if let defaultSorter = sorter as? defaultSorterType {
Update 2: Replacing the problematic line with the one below, makes the code work. However I am using here the 'dynamicType' which is not offered by code completion (must be a reason for that...). Also the question remains, why the first 2 attempts didn't work?
if sorter.dynamicType == defaultSorterType {

Related

Can we add static stored variable via extension, in Swift?

In the book Swift Programming Language 3.0, it says that we can't use extension to add stored property.
I tried it out with instance stored variable and Xcode displayed an error as expected.
But when I tried with static stored variable, everything compiled just fine.
Is there something that I'm missing or doing wrong?
class MyClass {}
extension MyClass {
static var one: Int {
return 1
}
static var two = 2 //compiled just fine
}
let myVariable = MyClass()
MyClass.two
You can't put stored properties in instances of an extension, you can cheat a little though and get the same effect with Objective-C associated objects. Give the following code a try:
private var associationKey: UInt8 = 0
var validationTypes: ValidationTypes {
get {
return objc_getAssociatedObject(self, &associationKey) as? ValidationTypes ?? []
}
set(newValue) {
objc_setAssociatedObject(self, &associationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
Obviously replacing ValidationTypes as appropriate.

How to prevent object construction in Swift [duplicate]

This question already has an answer here:
No more `private init` in Swift?
(1 answer)
Closed 6 years ago.
What ways do you know to prevent an object construction using Swift programming language?
In C++ I can simply make the constructor private like this:
struct A {
private:
A() {};
};
int main()
{
// Doesn't compile because the constructor is private.
A obj;
return 0;
}
When I do a similar thing in Swift (I tried it in playground) the code compiles just fine:
class A {
private init() {}
}
let obj = A()
UPDATE:
Ok, this question is marked as a duplicate. But I think this is a misunderstanding. What I'm asking about is what are the best practices you know to prevent object construction in Swift. All I want to achieve is to make it clear to the users of my class that it should not be constructible.
UPDATE 2:
As this question is still here, I think, it needs some more clarifications for those who still can't comprehend what I really want.
Given a class that is used as a wrapper for some useful constants such as the following:
class Constants {
static let someConstant1 = "CONSTANT_VALUE1"
static let someConstant2 = "CONSTANT_VALUE2"
//....etc...
}
what option can be considered as a best practice:
Leave it as is and don't worry about the possibility of objects creation outside this class;
Add private init() {} to prevent creation of the objects outside the current file;
Use init? and return nil to indicate that the objects must not be created as was suggested in the comments.
Hope the question is more clear now.
From Apple's guide to Swift:
Private access restricts the use of an entity to its own defining
source file. Use private access to hide the implementation details of
a specific piece of functionality.
Your playground file is all one file, so privacy is not enforced.
For instance, if you create a new project and add a file called Dog.swift to the project that looks like this:
import Foundation
class Dog {
private init() {
print("hello")
}
}
class Cat {
var d = Dog()
}
in ViewController.swift, you can write:
override func viewDidLoad() {
let c = Cat() //=>hello
}
But, if you try:
override func viewDidLoad() {
let d = Dog()
}
Xcode will flag that as an error before you even compile the program:
'Dog' cannot be constructed because it has no accessible initializers
Response to comment:
class A {
init?() {
return nil
}
func greet() {
print("hello")
}
}
let x = A()
if let x = x {
x.greet()
}
else {
print("nice try") //=> nice try
}
i tried this, hope this is what you want
private class My {
static var singletonObj = My()
}
let obj = My() // error
let obj1 = My.singletonObj

Inheriting initializer from generic class

I've seen some discussions about this problem, but have not read a satisfactory explanation. Can anybody tell me why this does not work?
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child : Parent<Int> {}
let c = Child(data: 4)
The last line gives the error:
'Child' cannot be constructed because it has no accessible initializers
Do I really need to implement the initializer just to call super?
Edit:
To give a bit of context, the real code looks closer to the below. I have an Action class which uses generics, because I have another bit of code which can chain actions together and I want to use Swift's type safety to ensure that actions can be chained. Then I have a bunch of subclasses classes (e.g. CustomAction). I am looking for a way to avoid overriding the init method in each of the subclasses. Or alternatively, I want to understand why that's not possible.
class Action<Input, Output> {
var cachedOutput:Output?
init(cachedOutput:Output?) {
self.cachedOutput = cachedOutput
}
}
protocol CustomInput {}
protocol CustomOutput {}
class CustomAction : Action<CustomInput, CustomOutput> {
}
yes, you really need to override init method ..
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<T> : Parent<T> {
override init(data: T) {
super.init(data: data)
}
}
let c = Child(data: 4) // Child<Int>
let c2 = Child(data: "alfa") // Child<String>
what are the errors ...
// what is the type T ? it is undeclared!
class Child2: Parent2<T> {}
// how to specialize non-generic type Parent ? how to create it?
// i need an initializer in class Child3 ... Hm ...
class Child3: Parent<Int> {}
// cannot specialize non-generic type 'Parent'
class Child3: Parent<Int> {
override init(data: Int) {
super.init(data: data)
}
}
// So, Child3 must be of the same specialized type as Parent!!
that is terrible, isn't it? so look at my final example !
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<Double> : Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4) // Child<Int> !!!!!
let d = Child(data: true) // Child<Bool> !!!
in your case it works like
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child: Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4)
print(c.dynamicType) // Child :-)
This now works in Swift 3. My original example now compiles. There is no mention of this in the Swift 3 Language changes, so I can only assume that this was a bug.

How to override mutable property from a superclass

class ArcaneCardVC: UIViewController {
var currentCard: ArcaneCardView?
}
class PostVC: ArcaneCardVC {
override var currentCard: PostCard?
// <===== This is what I want to do but cant
}
class ArcaneCardView: UIView {
}
class PostCard: ArcaneCardView {
}
Here is the error I get:
Cannot override mutable property 'currentCard' of type 'ArcaneCardView?' with covariant type 'PostCard?'
The other solution is explicitly doing this in code everytime I use currentCard:
var card = currentCard as! PostCard
When you override a variable, you can't change it's type. Why not? Well, suppose that you are able to do that, then the following scenario would be possible:
var A: PostVC = PostVC() // some initialization
var B: ArcaneCardVC = A // this is a valid state since `PostVC` is a subclass of `ArcaneCardVC`
What should be the type of B.currentCard? Hmm, this is a complicated question. You can answer that its type should be PostCard. Ok, lets add other classes to the party:
class OtherCard: ArcaneCardView {
}
class OtherVC: ArcaneCardVC {
override var currentCard: OtherCard?
}
Considerer now the following code:
var A: ArcaneCardVC = PostVC()
var B: ArcaneCardVC = OtherVC()
A.currentCard = B.currentCard // something will crash here!!!
To avoid this kind of behavior, you can't change the type of a property when you are subclassing.
The correct way to do it is the way you're doing with currentCard as! PostCard.
Another option would be to use a property getter like
// inside PostVC
// Note the camel case on the 'C' makes it a different variable that the super class
var CurrentCard: PostCard {
get { return self.currentCard as! PostCard }
}
Then you'd use self.CurrentCard instead of self.currentCard as! PostCard

collationStringSelector error : Unrecognized selector

I am trying to implement alphabetical sections in my application to learn Swift.
I found this tutorial to help me : http://www.pumpmybicep.com/2014/07/04/uitableview-sectioning-and-indexing/
First, I separate the class from the UITableViewController class for more readability.
But I have an error on the collationStringCollector, it does not recognize "m_titre" and I do not understand how collationStringSelector works and so, how to use it.
var lesCours: [Cours] = cours_array.map { eachCours in // cours_array contain all "Cours" which will be indexed
eachCours.section = self.collation.sectionForObject(eachCours, collationStringSelector: "title")
return eachCours
}
Here is my class "Cours"
class Cours {
var m_id : String
var m_titre : String
var m_content : String
var m_accept : Bool
var m_date : NSDate?
var section: Int?
init(titre:String, content:String)
{
self.m_titre = titre
self.m_content = content
self.m_id = ""
self.m_accept = false
self.m_date = nil
}
// ... getters, setters, specifics function
Thank you
PS : I apologize if my English is not perfect, it's not my native language, I hope that you will understand without difficulty in despite of this
Selectors use Objective-C message passing, therefore the property must be
"Objective-C compatible". The easiest way to achieve this is to make your
class a subclass of NSObject:
class Cours : NSObject {
// ...
}
And of course the selector must match the name of the property, in your
case "m_titre" instead of "title".

Resources