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 arraylet 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 saveTasting
instances toUserDefaults
, but you can save JSON-/ or PropertyList-encodedTasting
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
Swiftui: Prevent Image() from Expanding View Rect Outside of Screen Bounds
Swift Error: Binary Operator '&&' Cannot Be Applied to Two 'Bool' Operands
Swift: Corelocation Handling Nserror in Didfailwitherror
Simultaneous Gesture Recognition for Specific Gestures
iOS 8 Beta Today Extension Widget Not Showing in a Swift App
Binary Operator Cannot Be Applied to Operands of Type Int and Int? Swift 3
Reference as Key in Swift Dictionary
How to Detect When Url of Amp Page Changed with Wkwebview
Xcode 8.3 Can't Support Swift 2.3
Swift 4.2 Imagepickercontroller Issue
Missing Return in a Function Expected to Return 'Double'
Compare App Versions After Update Using Decimals Like 2.5.2
Swift Closure Not Setting Variable
Convert Generic Free Function into Array Extension