Cannot special non-generic type AnyObject - swift

public protocol Subscriber : class {
}
public struct Subscription {
private weak var subscriber:AnyObject<Subscriber>? = nil
}
Why can't I use AnyObject with a protocol for this var?

Swift doesn't allow combining a type and a protocol the way Objective-C does, although you can specify a combination of protocols. Since AnyObject is in fact a protocol, you can accomplish what you want to express above by writing:
public struct Subscription {
private weak var subscriber:protocol<Subscriber, AnyObject>? = nil
}
This requires subscriber to conform to both the Subscriber and the AnyObject protocols.
In your case above, you actually don't need the AnyObject part since you made the Subscriber protocol a class protocol which essentially guarantees that any conforming type is also an AnyObject. So you could just write:
public struct Subscription {
private weak var subscriber:Subscriber? = nil
}
But the protocol<Subscriber, AnyObject> approach would allow your Subscriber protocol to not be restricted to only class types, while making that specific subscriber weak var restricted to class types that implement Subscriber.

To refer to an object that conforms to a protocol, just use the protocol. Swift is not like Objective-C where you need to specify id<SomeProtocol>:
public struct Subscription {
private weak var subscriber: Subscriber? = nil
}
You can also declare your protocol like this if you want to restrict usage to AnyObject, rather than using class:
public protocol Subscriber : AnyObject {
// ...
}

Related

Cannot assign to a property: 'class' is a 'let' constant

I'm trying to update a property on a service that is defined as let subscriber: SubscriberContextProviding in the WatchlistViewModel by simply setting it directly like so:
subscriber.watchlist = watchlist
relevant subscriber definition:
final class Subscriber: SubscriberContextProviding {
var watchlist = [String]()
}
however I get an error saying: Cannot assign to property: 'subscriber' is a 'let' constant
. The subscriber service is declared as a let and is initialised in the client init.
here is the relevant protocol part & init.
protocol SubscriberContextProviding {
var watchlist: [String] { get set }
}
class WatchlistViewModel: NSObject {
let subscriber: SubscriberContextProviding
init(subscriber: SubscriberContextProviding){
self.subscriber = subscriber
super.init()
}
}
however If I change the protocol from the one above to
protocol SubscriberContextProviding {
func set(watchlist: [String])
}
and I simply define the function in the subscriber as
func set(watchlist: [String]){
self.watchlist = watchlist
}
and instead of setting the property directly now using the function like so
subscriber.set(watchlist: watchlist)
works no problem. Why the first approach doesn't work while the latter one does as the result is the same of both approaches?
The "issue" (though it's not an issue, really) is that you haven't restricted SubscriberContextProviding to being a class-bound protocol (with : AnyObject).
let subscriber: SubscriberContextProviding is declaring that your subscriber variable with contain an existential of any object whose type conforms to SubscriberContextProviding. Since that protocol isn't class-bound, it's possible that the concrete value you're dealing with is a value type (tuple, struct or enum), for which mutations are only allowed on mutable var variables. This existential is itself a value type, and abides the same rules of only allowing mutations on var variables.
Thus, you must either:
Declare SubscriberContextProviding as class-bound:
protocol SubscriberContextProviding: AnyObject { ... }
or
Keep your protocol as-is, but make your subscriber variable mutable, to account for the possibility that it contains a value type.
Add this to your protocol:
protocol SubscriberContextProviding: AnyObject{
var watchlist: [String] { get set }
}

How to add a protocol which contains associatedtype as a property in another protocol? [duplicate]

I am trying to create a Dictionary (actually a HashSet) keyed on a custom protocol in Swift, but it is giving me the error in the title:
Protocol 'myProtocol' can only be used as a generic constraint because it has Self or associated type requirements
and I can't make heads nor tails of it.
protocol Observing: Hashable { }
var observers = HashSet<Observing>()
Protocol Observing inherits from protocol Hashable, which in turn inherits from protocol Equatable. Protocol Equatable has the following requirement:
func ==(lhs: Self, rhs: Self) -> Bool
And a protocol that contains Self somewhere inside it cannot be used anywhere except in a type constraint.
Here is a similar question.
To solve this you could use generics. Consider this example:
class GenericClass<T: Observing> {
var observers = HashSet<T>()
}

Associated protocol in Swift

I'm trying to abstract views that are configured from a view model. I've been using associated types so far:
public protocol ViewModelProtocol: Equatable {}
public protocol ModeledView: class {
/// The type of the view model
associatedtype ViewModel: ViewModelProtocol
var viewModel: ViewModel? { get }
/// Sets the view model. A nil value describes a default state.
func set(newViewModel: ViewModel?)
}
Which I can use like:
struct MyViewModel: ViewModelProtocol {
let foo: String
static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
return lhs.foo == rhs.foo
}
}
class MyView: UIView, ModeledView {
typealias ViewModel = MyViewModel
private(set) var viewModel: MyViewModel?
public func set(newViewModel: MyViewModel?) {
print(newViewModel?.foo)
}
}
However I'd like to specify a protocol for my view models, and not a concretized type. The reason is that one struct / class could comply to several such view model protocols. I don't want to either convert this object to another type just to pass it to the view, or have the view have an associated type with more requirements than it needs. So I think I'd like to do something like:
protocol MyViewModelProtocol: ViewModelProtocol {
var foo: String { get }
}
class MyView: UIView, ModeledView {
typealias ViewModel = MyViewModelProtocol
private(set) var viewModel: MyViewModelProtocol?
public func set(newViewModel: MyViewModelProtocol?) {
print(newViewModel?.foo)
}
}
struct DataModel: MyViewModelProtocol {
let foo: String
let bar: String
static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
return lhs.foo == rhs.foo && lhs.bar == rhs.bar
}
}
let dataModel = DataModel(foo: "foo", bar: "bar")
let view = MyView()
view.set(newViewModel: dataModel)
This doesn't work. The compiler says that MyView doesn't conform to the ModeledView protocol, and hints that
Possibly intended match 'MyView.ViewModel' (aka 'MyViewModelProtocol') does not conform to 'ViewModelProtocol'
I don't really get what's bothering the compiler as MyViewModelProtocol is defined as extending ViewModelProtocol
MyViewModelProtocol is defined as extending ViewModelProtocol
Correct. MyViewModelProtocol extends ViewModelProtocol. It doesn't conform to ViewModelProtocol. This is a classic case of "protocols do not conform to themselves." Your associated type requires that ViewModel be a concrete type that conforms to ViewModelProtocol, and MyViewModelProtocol is not a concrete type and it does not conform to anything (protocols do not conform to protocols).
The way to attack this problem is to start with the calling code, and then construct protocols that support what you want the calling code to look like. In the code you've given, the correct solution is to get rid of ModeledView entirely. It isn't doing anything; nothing relies on it, and it doesn't provide any extensions. I assume in your "real" code, something does rely on it. That's the key piece of code to focus on. The best way to achieve that is to write a few concrete examples of the kinds of conforming types you'd like to exist. (If you have trouble writing several concrete implementations, then reconsider whether there's anything to abstract.)
Just to elaborate a bit more why this won't work in your specific case (it won't work in more general cases either, but your case layers additional restrictions that are important). You've required that types that conform to ViewModelProtocol also conform to Equatable. That means they must implement:
static public func == (lhs: Self, rhs: Self) -> Bool {
This does not mean that some type conforming to ViewModelProtocol is Equatable to some other type conforming to ViewModelProtocol. It means Self, the specific, concrete type implementing the protocol. ViewModelProtocol is not itself Equatable. If it were, which code above would you expect dataModel == myViewModel to call? Would it check bar or not? Remember, == is just a function. Swift doesn't know that you're asking "are these equal." It can't inject things like "if they're different concrete types they're not equal." That's something the function itself has to do. And so Swift needs to know which function to call.
Making this possible in Swift will be a major addition to the language. It's been discussed quite a lot (see the link I posted in the earlier comments), but it's at least several releases away.
If you remove the Equatable requirement, it still won't work today, but the language change required is much smaller (and may come sooner).

Swift associated types and protocol inheritance

I'm using Swift 2.2 and I declared a protocol with associated type as follow:
protocol CollectionViewModeling {
associatedtype CellType
func cellAtIndexPath(indexPath: NSIndexPath) -> CellType
}
Now I have a view model protocol conform to the above protocol :
enum MyItemCell {
case MyItemCell1, MyItemCell2
}
protocol ItemsListViewModeling: CollectionViewModeling {
associatedtype CellType = MyCell
}
Finally, somewhere else, I want to declare a var that is conform to le protocol ItemsListViewModeling :
var viewModel: ItemsListViewModeling
And I'm getting this error :
Protocol 'ItemsListViewModeling' can only be used as a generic constraint because it has Self or associated type requirements
But I can easily create a class that implements this protocol.
Is it possible to declare a var to an associated typed protocol ? As I'm giving the final type of the associated type in the protocol ItemsListViewModeling, I don't understand why am I seeing this error from the compiler.
Thanks
See there stackoverflow.com
You can't treat protocols with associated types like regular protocols
and declare them as standalone variable types.

Conform to a protocol misunderstanding

class SuperDelegate <T: AnyObject> {
func addDelegate(delegate: T)
{
}
}
My question is about T key, does it mean the same as id in Objective-c? I mean about case of uses.
how to understand the first line class SuperDelegate <T: AnyObject> Sorry I am new in Swift.
As Objective C program for me this line means that we make class to conform a protocol that has to implement all required method. But I don't understand func addDelegate(delegate: T) is this the same like
- (void)addDelegate:(id)delegate which is a property id <T> delegate.
Yes you are correct in your assumptions that AnyObject behaves like id:
You can call any Objective-C method and access any property on an
AnyObject value without casting to a more specific class type. This
includes Objective-C compatible methods and properties marked with the
#objc attribute.
but you have used it here as a generic type rather than as a concrete type that should be cast to. The class is requiring a type that adheres to the AnyObject protocol but it isn't forcing it to be AnyObject (see header files: cmd + click on AnyObject inside Xcode).
So your instance could be instantiated SuperDelegate<AnyObject> but it could also be instantiated SuperDelegate<NSDate>. This means that the whole subset of ObjC methods and properties cannot be guaranteed as they can with a cast to AnyObject as a concrete type because at runtime T might represent NSDate or NSNumber or any other class.
To achieve what you want you would need to write:
class SuperDelegate {
func addDelegate(delegate: AnyObject)
{
}
}
But Swift is a strongly-typed language and it would normally be the case that you had a delegate protocol and that the delegate for your type adhered to the delegate protocol:
protocol DelegateProtocol {
func somethingHappened()
}
struct MyTypeDelegate:DelegateProtocol {
func somethingHappened() {
print("Thanks for telling me!")
}
}
struct MyType {
var delegate:DelegateProtocol?
func tellDelegateSomethingHappened() {
delegate?.somethingHappened()
}
}
let del = MyTypeDelegate()
var type = MyType()
type.delegate = del
type.tellDelegateSomethingHappened()