Protocol 'RawRepresentable' requires 'init(rawValue:)' to be available in iOS 13.0.0 and newer

341 Views Asked by At

I am currently building a framework, and would like to conform SwiftUI Color to the RawRepresentable protocol only for iOS 14. I have the following code:

@available(iOS 14.0, *)
extension Color: RawRepresentable {
    public init?(rawValue: Data) {
        let uiColor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: rawValue)
        if let uiColor = uiColor {
            self = Color(uiColor)
        } else {
            return nil
        }
    }
    
    public var rawValue: Data {
        let uiColor = UIColor(self)
        return (try? NSKeyedArchiver.archivedData(withRootObject: uiColor, requiringSecureCoding: false)) ?? Data()
    }
}

However, this results in two similar errors:

Protocol 'RawRepresentable' requires 'init(rawValue:)' to be available in iOS 13.0.0 and newer
Protocol 'RawRepresentable' requires 'rawValue' to be available in iOS 13.0.0 and newer

Is this not possible? I am able to modify the code to:

extension Color: RawRepresentable {
    public init?(rawValue: Data) {
        let uiColor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: rawValue)
        if let uiColor = uiColor {
            self = Color(uiColor)
        } else {
            return nil
        }
    }

    public var rawValue: Data {
        if #available(iOS 14, *) {
            let uiColor = UIColor(self)
            return (try? NSKeyedArchiver.archivedData(withRootObject: uiColor, requiringSecureCoding: false)) ?? Data()
        }
        fatalError("do not use Color.rawValue on iOS 13")
    }
}

This fixes the error, but it seems wrong and hack-y to call a fatalError.

Thank you for any help!

1

There are 1 best solutions below

0
On

It seems Swift hasn't officially(or fully) supported this feature at the moment (Swift 5.3).

Your codes can be reduced to the following lines:

// for example: in project with minimum deploy target of iOS 13.0

struct A {}

protocol B {
  var val: Int { get }
}

@available(iOS 14.0, *)
extension A: B {   
  var val: Int {  // <- Compiler Error 
    0
  }
}

// The compiler says: Protocol 'B' requires 'val' to be available in iOS 13.0.0 and newer.

The related feature was discussed in Swift forums: https://forums.swift.org/t/availability-checking-for-protocol-conformances/42066.

And hopefully when it lands in Swift 5.4 ( https://github.com/apple/swift/blob/main/CHANGELOG.md#swift-54), your problem would be solved.