Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 months ago.
Improve this question
When the getBadge() method is called from the view, why it uses the function from the extension without Status == OpenAccessPointState condition, if the Status.self is OpenAccessPointState in runtime?
Here is the code:
protocol BadgeStateViewRepresentable: Statusable {
func getBadge() -> BadgeWithText?
}
protocol Statusable {
associatedtype Status: AccessPointState
var status: Status { get }
}
extension View where Self: BadgeStateViewRepresentable {
func getBadge() -> BadgeWithText? {
return nil
}
}
extension View where Self: BadgeStateViewRepresentable, Status == OpenAccessPointState {
func getBadge() -> BadgeWithText? {
return BadgeWithText()
}
}
struct SomeDeviceDetailsView: View, BadgeStateViewRepresentable {
var status: some AccessPointState {
return OpenAccessPointState()
}
var body: some View {
getBadge()
}
}
Is there way to make this working?
If you actually declare the type of status explicitly, then it works as expected:
var status: OpenAccessPointState {
return OpenAccessPointState()
}
This is because opaque types are opaque. SomeDeviceDetailsView.Status is some AccessPointState, and you cannot "see through" what specific AccessPointState it is. Therefore, it does not fulfil the requirement Status == OpenAccessPointState.
This has nothing to do with what happens "at runtime". SomeDeviceDetailsView.Status is a type, not a variable, and it doesn't become something different at runtime. compared to at compile time.
A simpler example:
protocol Foo {
associatedtype MyType
func foo() -> MyType
}
extension Foo where MyType == Int {
func bar() { }
}
struct Impl: Foo {
func foo() -> some CustomStringConvertible {
1
}
}
// Referencing instance method 'bar()' on 'Foo' requires the types 'some CustomStringConvertible' and 'Int' be equivalent
Impl().bar()
If you are trying to dynamically dispatch getBadge, you should put it in each of the status types, and get rid of the Statusable protocol whose purpose becomes rather unclear. Example:
protocol AccessPointState {
func getBadge() -> BadgeWithText?
}
extension AccessPointState {
func getBadge() -> BadgeWithText? {
nil
}
}
struct OpenAccessPointState: AccessPointState {
func getBadge() -> BadgeWithText? {
BadgeWithText()
}
}
struct SomeOtherState: AccessPointState {}
struct SomeDeviceDetailsView: View {
var status: AccessPointState {
if Bool.random() {
return OpenAccessPointState()
} else {
return SomeOtherState()
}
}
var body: some View {
status.getBadge()
}
}
Related
I have a class attribute that points to one of the class functions. However when I try to initialize this variable with one of the functions, I get the following error:
'self' used in method call before all stored properties are initialized.
I'm able to initialize any other variable to those functions, but the error makes it sound like I'm calling the function even though I'm not.
import UIKit
import AudioToolbox
class BeatMaker {
// iPhone 7 and up use beat function, iPhone 6s use beatFallback
let hapticFunction: () -> ()
let impactGenerator = UIImpactFeedbackGenerator.init(style: .heavy)
init(supportsImpactGenerator: Bool) {
// error 1: 'self' used in method call 'beat' before all stored properties are initialized
// error 2: 'self' used in method call 'beatFallback' before all stored properties are initialized
self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
}
private func beat() {
impactGenerator.impactOccurred()
}
private func beatFallback() {
AudioServicesPlaySystemSound(1520)
}
func makeABeat() {
hapticFunction()
}
}
In this specific case I want to make use of the Taptic Engine and simulate a click through the UIImpactFeedbackGenerator. The iPhone 6s doesn't support this engine, so I want to call a fallback function that produces a similar effect.
I also tried initializing the variable on the spot:
// this works
var hapticFunction: (BeatMaker) -> () -> () = beat
init(supportsImpactGenerator: Bool) {
if !supportsImpactGenerator {
// error: Cannot assign value of type '() -> ()' to type '(BeatMaker) -> () -> ()'
self.hapticFunction = beatFallback
// produces same error
self.hapticFunction = beatFallback.self
}
}
I know that I could make everything static or put everything out of the class, but this feels like it should work yet it doesn't. Am I missing something?
EDIT
Setting the type of type of hapticFunction to an optional seems to work, but this doesn't make any sense to me. What's the difference?
// this works
var hapticFunction: (() -> ())?
init(supportsImpactGenerator: Bool) {
self.hapticFunction = (supportsImpactGenerator) ? beat : beatFallback
}
It might be better to not use a Bool, but rather a nested Enum, which is also more extendible if you wanna add some other modes of haptic feedback later on.
I have a generalized solution for a generalized problem of your question. So either you do:
public class FunctionOwner {
private let mode: Mode
public init(`do` mode: Mode = .default) {
self.mode = mode
}
}
public extension FunctionOwner {
enum Mode {
case foo, bar
}
func fooOrBar() {
switch mode {
case .foo: foo()
case .bar: bar()
}
}
}
private extension FunctionOwner {
func foo() {
print("doing foo")
}
func bar() {
print("doing bar")
}
}
public extension FunctionOwner.Mode {
static var `default`: FunctionOwner.Mode {
return .foo
}
}
// USAGE
FunctionOwner(do: .bar).fooOrBar() // prints "doing foo"
FunctionOwner(do: .foo).fooOrBar() // prints "doing bar"
Or if you for some reason do want to keep the stored Mode, you can do this (might be relevant for your actual question on how you do a workaround of referencing self in the init.):
public class FunctionOwner {
private let _function: (FunctionOwner) -> Void
public init(`do` mode: Mode = .default) {
_function = { functionOwner in
switch mode {
case .foo: functionOwner.foo()
case .bar: functionOwner.bar()
}
}
}
}
public extension FunctionOwner {
enum Mode {
case foo, bar
}
func fooOrBar() {
_function(self)
}
}
// The rest of the code is the same as the example above
There are two method to fix this
You can:
Give hapticFunction a initial value like {}
or fix like:
class BeatMaker {
let impactGenerator = UIImpactFeedbackGenerator.init(style: .heavy)
let supportsImpactGenerator: Bool
init(supportsImpactGenerator: Bool) {
self.supportsImpactGenerator = supportsImpactGenerator
}
private func beat() {
if supportsImpactGenerator {
impactGenerator.impactOccurred()
} else {
AudioServicesPlaySystemSound(1520)
}
}
func makeABeat() {
beat()
}
}
I was previously using a class that could be simplfied down to this:
class Whatever {
var someArray = [Int]()
func unchangingFunction {
print("test")
}
func functionForOverride() {}
}
I was asking of ways to improve this, and I got told to favour composition over inheritance, using something like the following:
protocol Implementation {
func functionForOverride()
}
final class Whatever {
var someArray = [Int]() // How can I access this?
let implementation: Implementation
init(implementation: Implementation) {
self.implementation = implementation
}
func unchangingFunction() {
print("test")
}
func functionForOverride() {
implementation.functionForOverride()
}
}
However, with this, I can't find a way to do anything with the someArray array:
struct Something: Implementation {
func functionForOverride() {
print(someArray) // This cannot work
}
}
With the original code I am able to access and alter someArray however I want, but with this new way, I can't think of an easy solution.
I think we should use a "real" example in order to make things clearer.
Inheritance (and why it's wrong)
We have the following classes
class Robot {
var battery = 0
func charge() {
print("⚡️")
battery += 1
}
}
class Human {
func eat() {
print("🍽")
}
}
class RobotCleaner: Robot {
func clean() {
print("💧")
}
}
class HumanCleaner: Human {
func clean() {
print("💧")
}
}
Code duplication!!!
As you can see the clean() method is duplicated in RobotCleaner and HumanCleaner. Can you find a way (using inheritance) to remove code duplication?
Ok think about that, I'll wait on the next paragraph... :)
...
Oh, here you are! There's no way to fix that with inheritance right? Well, let's see what we can do with composition.
Composition (the classic way)
Let's define the following 3 protocols and related components
protocol Robot {
mutating func charge()
}
struct RobotComponent: Robot {
var battery = 0
mutating func charge() {
print("⚡️")
battery += 1
}
}
protocol Human {
func eat()
}
struct HumanComponent: Human {
func eat() {
print("🍽")
}
}
protocol Cleaner {
func clean()
}
struct CleanerComponent: Cleaner {
func clean() {
print("💧")
}
}
Now we can build any combination of the previous 3 elements
struct RobotCleaner: Robot, Cleaner {
var robotComponent = RobotComponent()
let cleanerComponent = CleanerComponent()
mutating func charge() {
robotComponent.charge()
}
func clean() {
cleanerComponent.clean()
}
}
struct HumanCleaner: Human, Cleaner {
let humanComponent = HumanComponent()
let cleanerComponent = CleanerComponent()
func eat() {
humanComponent.eat()
}
func clean() {
cleanerComponent.clean()
}
}
Protocol Oriented Programming: Composition the Swifty way
Swift offers a very neat way of doing composition.
First of all let's define the following 3 protocols (and related extensions).
protocol Robot {
var battery: Int { get set }
}
extension Robot {
mutating func charge() {
print("⚡️")
battery += 1
}
}
protocol Human { }
extension Human {
func eat() {
print("🍽")
}
}
protocol Cleaner { }
extension Cleaner {
func clean() {
print("💧")
}
}
Now we can create a Type which has any combination of the previous 3 entities. Let's see how.
struct HumanCleaner: Human, Cleaner { }
struct RobotCleaner: Robot, Cleaner {
var battery: Int = 0
}
If 'Implementation' requires 'someArray' to do what it is intended to do, then you should have 'Implementation' require any object conforming to it to also declare 'someArray'
Like this:
protocol Implementation {
var someArray: [Int]
}
And if you know what you want to do with 'someFunction', then you could give a default implementation of it with a protocol extension like so:
extension Implementation {
func someFunction() {
//you can do some stuff with someArray here
}
}
Then when you conform to 'Implementation' you need to declare 'someArray' but not 'someFunction', unless you want to override the default function.
E.g.
class MyClass: Implementation {
var someArray: [Int]!
init() {}
}
Note that MyClass now has access to 'someFunction', which can be freely overridden in your class, and that you can add as many functions as you want to 'Implementation's extension.
I'm trying to create a simple protocol that says whether or not an object is in an "on" state or an "off" state. The interpretation of what that is depends on the implementing object. For a UISwitch, it's whether the switch is on or off (duh). For a UIButton, it could be whether the button is in the selected state or not. For a Car, it could be whether the car's engine is on or not, or even if it is moving or not. So I set out to create this simple protocol:
protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
Now I can extent the aforementioned UI controls like so:
extension UISwitch: OnOffRepresentable {
func isInOnState() -> Bool { return on }
func isInOffState() -> Bool { return !on }
}
extension UIButton: OnOffRepresentable {
func isInOnState() -> Bool { return selected }
func isInOffState() -> Bool { return !selected }
}
Now I can make an array of these kinds of objects and loop over it checking whether they are on or off:
let booleanControls: [OnOffRepresentable] = [UISwitch(), UIButton()]
booleanControls.forEach { print($0.isInOnState()) }
Great! Now I want to make a dictionary that maps these controls to a UILabel so I can change the text of the label associated with the control when the control changes state. So I go to declare my dictionary:
var toggleToLabelMapper: [OnOffRepresentable : UILabel] = [:]
// error: type 'OnOffRepresentable' does not conform to protocol 'Hashable'
Oh! Right! Silly me. Ok, so let me just update the protocol using protocol composition (after all, the controls I want to use here are all Hashable: UISwitch, UIButton, etc):
protocol OnOffRepresentable: Hashable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
But now I get a new set of errors:
error: protocol 'OnOffRepresentable' can only be used as a generic constraint because it has Self or associated type requirements
error: using 'OnOffRepresentable' as a concrete type conforming to protocol 'Hashable' is not supported
Ok... So I do some stack overflow digging and searching. I find many articles that seem promising, like Set and protocols in Swift, Using some protocol as a concrete type conforming to another protocol is not supported, and I see that there are some great articles out there on type erasure that seem to be exactly what I need: http://krakendev.io/blog/generic-protocols-and-their-shortcomings, http://robnapier.net/erasure, and https://realm.io/news/type-erased-wrappers-in-swift/ just to name a few.
This is where I get stuck though. I've tried reading through all these, and I've tried to create a class that will be Hashable and also conform to my OnOffRepresentable protocol, but I can't figure out how to make it all connect.
I don't know if I'd necessarily make the OnOffRepresentable protocol inherit from Hashable. It doesn't seem like something that you'd want to be represented as on or off must also be hashable. So in my implementation below, I add the Hashable conformance to the type erasing wrapper only. That way, you can reference OnOffRepresentable items directly whenever possible (without the "can only be used in a generic constraint" warning), and only wrap them inside the HashableOnOffRepresentable type eraser when you need to place them in sets or use them as dictionary keys.
protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
extension UISwitch: OnOffRepresentable {
func isInOnState() -> Bool { return on }
func isInOffState() -> Bool { return !on }
}
extension UIButton: OnOffRepresentable {
func isInOnState() -> Bool { return selected }
func isInOffState() -> Bool { return !selected }
}
struct HashableOnOffRepresentable : OnOffRepresentable, Hashable {
private let wrapped:OnOffRepresentable
private let hashClosure:()->Int
private let equalClosure:Any->Bool
var hashValue: Int {
return hashClosure()
}
func isInOnState() -> Bool {
return wrapped.isInOnState()
}
func isInOffState() -> Bool {
return wrapped.isInOffState()
}
init<T where T:OnOffRepresentable, T:Hashable>(with:T) {
wrapped = with
hashClosure = { return with.hashValue }
equalClosure = { if let other = $0 as? T { return with == other } else { return false } }
}
}
func == (left:HashableOnOffRepresentable, right:HashableOnOffRepresentable) -> Bool {
return left.equalClosure(right.wrapped)
}
func == (left:HashableOnOffRepresentable, right:OnOffRepresentable) -> Bool {
return left.equalClosure(right)
}
var toggleToLabelMapper: [HashableOnOffRepresentable : UILabel] = [:]
let anySwitch = HashableOnOffRepresentable(with:UISwitch())
let anyButton = HashableOnOffRepresentable(with:UIButton())
var switchLabel:UILabel!
var buttonLabel:UILabel!
toggleToLabelMapper[anySwitch] = switchLabel
toggleToLabelMapper[anyButton] = buttonLabel
Creating a protocol with an associatedType (or conforming it to another protocol that has an associatedType like Hashable) will make that protocol very unfriend with generics.
I'll suggest you a very simple workaround
OnOffRepresentable
First of all we don't need 2 functions that say exactly the opposite right? ;)
So this
protocol OnOffRepresentable {
func isInOnState() -> Bool
func isInOffState() -> Bool
}
becomes this
protocol OnOffRepresentable {
var on: Bool { get }
}
and of course
extension UISwitch: OnOffRepresentable { }
extension UIButton: OnOffRepresentable {
var on: Bool { return selected }
}
Pairing a OnOffRepresentable to a UILabel
Now we can't use OnOffRepresentable as Key of a Dictionary because our protocol must be Hashable. Then let's use another data structure!
let elms: [(OnOffRepresentable, UILabel)] = [
(UISwitch(), UILabel()),
(UIButton(), UILabel()),
]
That's it.
Is it possible to return a type of generic that conforms to protocol for later use with class functions using Swift 1.2? Take a look:
protocol SomeProtocol
{
static func start(kind: Kind)
}
class A: SomeProtocol
{
class func start(kind: Kind)
{
print("A started")
}
}
class B: SomeProtocol
{
class func start(kind: Kind)
{
print("B started")
}
}
enum Kind {
case Akind
case Bkind
private func classKind<T: SomeProtocol>() -> T.Type
{
switch self {
case .Akind: return A.self
case .Bkind: return B.self
}
}
func doSomething() {
self.classKind().start(self)
}
}
I tried various methods but every of them ended with some errors. Currently I got 'A' is not a subtype of 'T' in classKind method (same for 'B') and cannot invoke 'start' with an argument list of type '(Kind)' in doSomething.
I'm sure I'm pretty close but can't solve it...
If you're using Swift 2, to achieve what you want you only need to change:
private func classKind<T: SomeProtocol>() -> T.Type { ... }
to
private func classKind() -> SomeProtocol.Type { ... }
Now back to the not-working code to see where the errors were coming from. You don't need to make the changes I'm now detailing, this is just to explain the errors.
First examine your doSomething method:
func doSomething() {
self.classKind().start(self)
// Error: Argument for generic parameter 'T' could not be inferred.
//
// (I'm using Xcode 7 b6, which may explain the differing error messages)
}
For the type returned by classKind to be inferred, you'd have to do:
let type: A.Type = self.classKind() // Or you could use `B.Type`.
type.start(self)
Which obviously defeats the point of your goal, since you have to specify the type you want.
Secondly, the errors in classKind:
private func classKind<T: SomeProtocol>() -> T.Type
{
switch self {
case .Akind: return A.self
// Cannot convert return expression of type 'A.Type' to return type 'T.Type'.
case .Bkind: return B.self
// Cannot convert return expression of type 'B.Type' to return type 'T.Type'.
}
}
To see why this doesn't work consider the following example, in which I have another type that conforms to SomeProtocol:
struct C: SomeProtocol { ... }
Then in doSomething:
func doSomething() {
let type: C.Type = self.classKind()
type.start(self)
}
The errors you're getting can now be read as: Cannot convert return expression of type 'A.Type'/'B.Type' to return type 'C.Type'.
I have an extension on UIView implementing a protocol
protocol SomeProtocol {
var property : Int
}
extension UIView : SomeProtocol {
var property : Int {
get {
return 0
}
set {
// do nothing
}
}
}
in a concrete subclass I want to override this extension method:
class Subclass : UIView, SomeProtocol {
var _property : Int = 1
var property : Int {
get { return _property}
set(val) {_property = val}
}
}
I set breakpoints and see that the extension method is called and not the concrete subclass method:
var subclassObject = Subclass()
someObject.doSomethingWithConcreteSubclassObject(subclassObject)
// other code;
fun doSomethingWithConcreteSuclassObject(object : UIView) {
var value = object.property // always goes to extension class get/set
}
As others have noted, Swift does not (yet) allow you to override a method declared in a class extension. However, I'm not sure whether you'll ever get the behavior you want even if/when Swift someday allows you to override these methods.
Consider how Swift deals with protocols and protocol extensions. Given a protocol to print some metasyntactic variable names:
protocol Metasyntactic {
func foo() -> String
func bar() -> String
}
An extension to provide default implementations:
extension Metasyntactic {
func foo() -> String {
return "foo"
}
func bar() -> String {
return "bar"
}
}
And a class that conforms to the protocol:
class FooBar : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
}
Swift will use dynamic dispatch to call the appropriate implementations of foo() and bar() based on each variable's runtime type rather than on the type inferred by the compiler:
let a = FooBar()
a.foo() // Prints "FOO"
a.bar() // Prints "BAR"
let b: Metasyntactic = FooBar()
b.foo() // Prints "FOO"
b.bar() // Prints "BAR"
If, however, we extend the protocol further to add a new method:
extension Metasyntactic {
func baz() -> String {
return "baz"
}
}
And if we override our new method in a class that conforms to the protocol:
class FooBarBaz : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
func baz() -> String {
return "BAZ"
}
}
Swift will now use static dispatch to call the appropriate implementation of baz() based on the type inferred by the compiler:
let a = FooBarBaz()
a.baz() // Prints "BAZ"
let b: Metasyntactic = FooBarBaz()
b.baz() // Prints "baz"
Alexandros Salazar has a fantastic blog post explaining this behavior in depth, but suffice it to say that Swift only uses dynamic dispatch for methods declared in the original protocol, not for methods declared in protocol extensions. I imagine the same would be true of class extensions, as well.
I know this question has been asked a while ago. But this will be handy for someone who looking for an easier way. There is a way of overriding an extension methods. I know its bit hacky but it does the job beautifully.
If you declare your protocol with #objc
#objc protocol MethodOverridable {
func overrideMe()
}
In Extension
extension MainClass: MethodOverridable {
func overrideMe() {
print("Something useful")
}
}
Subclass - You can able to override it in your subclass. It works like a magic. Well, not really when adding #objc it exposes your protocol to Objective-C and its Runtime. That allows your subclass to override.
class SubClass: MainClass {
override func overrideMe() {
print("Something more useful")
}
}
Swift 5
class Class
{
#objc dynamic func make() { print("make from class") }
}
class SubClass: Class {}
extension SubClass {
override func make() {
print("override")
}
}
It looks like you can override property for 2nd super class property. For example, you can access UIView property by making extension to the UILabel wanting to override frame property of UIView. This sample works for me in Xcode 6.3.2
extension UILabel {
override public var frame: CGRect {
didSet {
println("\(frame)")
}
}
}
You can't do this through normal means.
It's in Apple's docs that you can't override a method in an extension in a subclass.
Also, extensions can add new functionality to a type, but they cannot override existing functionality.
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
I think you forgot to override the superclass property in your subclass:
class Subclass : UIView {
var _property : Int = 1
override var property : Int {
get { return _property}
set(val) {_property = val}
}
}