Initializer Requirement 'Init(JSON:)' Can Only Be Satisfied by a 'Required' Initializer in the Definition of Non-Final Class 'Uicolor'

Implementing Codable for UIColor

You cannot make UIColor conform to Decodable in an extension because of the error given by the compiler.

One solution is to make a Codable wrapper type and use that instead.

Since UIColor already conforms to NSCoding, let's just write a generic type so we can encode and decode anything that conforms to NSCoding.

import UIKit

struct WrapperOfNSCoding<Wrapped>: Codable where Wrapped: NSCoding {
var wrapped: Wrapped

init(_ wrapped: Wrapped) { self.wrapped = wrapped }

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
guard let object = NSKeyedUnarchiver.unarchiveObject(with: data) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "failed to unarchive an object")
}
guard let wrapped = object as? Wrapped else {
throw DecodingError.typeMismatch(Wrapped.self, DecodingError.Context(codingPath: container.codingPath, debugDescription: "unarchived object type was \(type(of: object))"))
}
self.wrapped = wrapped
}

func encode(to encoder: Encoder) throws {
let data = NSKeyedArchiver.archivedData(withRootObject: wrapped)
var container = try encoder.singleValueContainer()
try container.encode(data)
}
}

let colors = [UIColor.red, UIColor.brown]
print(colors)
let jsonData = try! JSONEncoder().encode(colors.map({ WrapperOfNSCoding($0) }))
let colors2 = try! JSONDecoder().decode([WrapperOfNSCoding<UIColor>].self, from: jsonData).map({ $0.wrapped })
print(colors2)

Making UIColor Codable - conforming to protocol 'Encodable'

Why do you want UIColor additionally conform to Codable? It conforms already to NSSecureCoding so it's serializable to Data by default.

And even with your implementation you can encode a color

let encoded = UIColor.Words.adverb.encode()!

and decode it

let color = UIColor.color(data: encoded)

And particularly in Core Data you can use computed properties to transform a supported type to an unsupported and vice versa.

My recommedation for Core Data is to save the color as Int32 (the hex representation) or as (hex) String


This is an implementation I'm using in Core Data, an extension to convert the color to a hex string

extension UIColor {

private func float2String(_ float : CGFloat) -> String {
return String(format:"%02X", Int(round(float * 255)))
}

var hex : String {
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: nil)
return "#" + float2String(red) + float2String(green) + float2String(blue)
}

convenience init(hex : String) {
let hex = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex
if hex.count == 6, hex.range(of: "[^0-9A-Fa-f]", options: .regularExpression) == nil {
let chars = Array(hex)
let numbers = stride(from: 0, to: chars.count, by: 2).map() {
CGFloat(strtoul(String(chars[$0 ..< min($0 + 2, chars.count)]), nil, 16))
}
self.init(red: numbers[0] / 255, green: numbers[1] / 255, blue: numbers[2] / 255, alpha: 1.0)
} else {
self.init(white: 1.0, alpha: 1.0)
}
}
}

and the relevant part of the NSManagedObject subclass

@NSManaged public var hexColor: String


var color : UIColor {
get { return UIColor(hex: hexColor) }
set { hexColor = newValue.hex }
}

Saving UIColor within Struct Array to UserDefaults

The errors are not related to my solution.

  • The first error tells you that the object is an array. Please read your code, tastings is clearly an array.

    So you have to decode an array

    let newTastings = try JSONDecoder().decode([Tasting].self, from: data)
  • The second error tells you that in your struct is a type which is not property list compliant. This type is UIColor. You cannot save Tasting instances to UserDefaults, but you can save JSON-/ or PropertyList-encoded Tasting instances.

     let data = try JSONEncoder().encode(tastings)
    UserDefaults.standard.set(data, forKey: "tastings")

Does not conform to String protocol SwiftUI Picker View

As E.Coms noted, the solution is to use one of the following:

Text(self.cardController.cards[$0].description)

Text(String(describing: self.cardController.cards[$0]))

Here's an explanation of why you have to do this inside the Text initializer, but not print().


Look at the two initializers for Text:

init(verbatim content: String) (docs)

init<S>(_ content: S) where S : StringProtocol (docs)

You must pass either a String or a Substring, the only two types conforming to StringProtocol. In this case, even though your type conforms to CustomStringConvertible, you are still passing a Card.


Contrast this with something like the print function:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") (docs)

Notice that the print function's arguments are denoted by Any, which is explained as

Any can represent an instance of any type at all, including function types.

The print function then converts whatever you passed it to a String:

The textual representation for each item is the same as that obtained by calling String(item).

String has an initializer which takes a type conforming to CustomStringConvertible and returns the description property.


So the reason you can write print(Card()) and not Text(Card() is because the print function has an intermediate step through String that can understand your conformance to CustomStringConvertible, but Text does not. If Text allowed you to pass it any type, it would be both more ambiguous ("What is the text representation of this type?" is not necessarily immediately apparent, as it depends on a hierarchical set of protocols), and more work for the SwiftUI system, which is already doing a lot.

Instance member cannot be used on type

You just have syntax error when saying = {return self.someValue}. The = isn't needed.

Use :

var numPages: Int {
get{
return categoriesPerPage.count
}

}

if you want get only you can write

var numPages: Int {
return categoriesPerPage.count
}

with the first way you can also add observers as set willSet & didSet

var numPages: Int {
get{
return categoriesPerPage.count
}
set(v){
self.categoriesPerPage = v
}
}

allowing to use = operator as a setter

myObject.numPages = 5

Read and write a String from text file

For reading and writing you should use a location that is writeable, for example documents directory. The following code shows how to read and write a simple string. You can test it on a playground.

Swift 3.x - 5.x

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

let fileURL = dir.appendingPathComponent(file)

//writing
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */}

//reading
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
}
catch {/* error handling here */}
}

Swift 2.2

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = NSURL(fileURLWithPath: dir).URLByAppendingPathComponent(file)

//writing
do {
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
}
catch {/* error handling here */}

//reading
do {
let text2 = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
}
catch {/* error handling here */}
}

Swift 1.x

let file = "file.txt"

if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
let dir = dirs[0] //documents directory
let path = dir.stringByAppendingPathComponent(file);
let text = "some text"

//writing
text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);

//reading
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
}


Related Topics



Leave a reply



Submit