Can typealias be Codable? - swift

I have a root object that consists mainly of an array of tuples and then a bunch of state enums. The tuples are described this way:
typealias Program = [(Number, Statement)]
The root object is currently NSCoding, but I'd like to add Codable to make the switch to JSON. In most cases, making the various parts become Codable requires nothing more than adding , Codable to their declarations, but that's not working for the tuples. I think this is just a syntax issue, but neither of these work:
typealias Program, Codable = [(Number, Statement)]
typealias Program = [(Number, Statement)], Codable
Is this possible? The horrible documentation only mentions typealias when describing the protocol itself, and perhaps my Google-fu is failing, but I'm not seeing cogent references here on SO.
UPDATE: root object(s):
typealias Program = [(Number, Statement)]
public enum InterpreterState: Int, Codable {
case Idle
case ReadingStatement // while parsing code
case Running // while running it
case ReadingInput // while waiting on user input
}
public final class Interpreter: NSObject, NSCoding {
/// Array of program lines
var program: Program = Array()
/// State
var state: InterpreterState = .Idle
etc.

Tuples are not Codable. If you think about it, what should the keys for (Number, Statement) be? (I might have made it sound like named tuples are codable, but they aren't either)
Create your own struct:
struct ProgramLine: Codable {
let lineNumber: Number // assuming these types are codable
let statement: Statement
}
And then your type alias will be codable too (without you doing anything)!
typealias Program = [ProgramLine]

Related

Swift Codable struct recursively containing itself as property

I have a rather large struct that conforms to Codable, and one of its properties needs to be of the same type as itself. A shortened sample of what I'm trying to do is shown below:
struct Message: Codable {
let content: String
// ...other values
let reference: Message // <-- Error: Value type 'Message' cannot have a stored property that recursively contains it
}
Swift doesn't seem to allow a struct to recursively contain itself as one of its values. Is there any way to get this working besides creating a complete duplicate Message struct (which turns this into a chicken and egg problem where the duplicate struct cannot contain itself etc). Not creating a duplicate struct also allows me to reuse SwiftUI code that takes in and renders a Message struct.
A simple way is to just change the struct into a class:
class Message: Codable {
let content: String
// ...other values
let reference: Message? // optional - the recursion has to end somewhere right?
}
But this could break other parts of your code, since structs and classes have vastly different semantics.
An alternative would be to make a reference type Box:
class Box<T: Codable>: Codable {
let wrappedValue: T
required init(from decoder: Decoder) throws {
wrappedValue = try T(from: decoder)
}
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
}
Then,
struct Message: Codable {
let content: String
// ...other values
let boxedReference: Box<Message>?
// you can still refer to 'reference' as 'reference' in your swift code
var reference: Message? { boxedReference?.wrappedValue }
enum CodingKeys: String, CodingKey {
case content, boxedReference = "reference"
}
}
Sweeper's approach provides two good solutions, both of which involve injecting a class. But there are a several other things you might try, some that work and some that don't, and it's worth understanding the underlying issue. It's not just a quirk; it's an important part of how Swift works.
// FAIL (for abstract reasons)
var manager: Manager
var manager: CollectionOfOne<Manager>
// FAIL (for swift-specific reasons)
var manager: Manager? // when manager is a struct
var manager: Result<Manager, Error> // when manager is a struct
// PASS
var manager: Manager? // when Manager is a class
var manager: Result<Manager, Error> // when manager is a class
var manager: Box<Manager> // our custom class Box
var manager: [Manager]
var manager: EmptyCollection<Manager>
The following example also passes:
protocol ManagerLike {
var id: String { get set }
var mail: String { get set }
var manager: ManagerLike? { get set }
}
struct Manager: ManagerLike {
var id: String
var mail: String
var manager: any ManagerLike?
}
The first two cases fail for abstract, logical reasons. It's impossible for any "eager" language (like Swift) to construct a type that must contain a value of its own type. That's infinitely recursive. And an eager language needs to construct the entire value before it can progress. Even in a lazy language, you'd need some kind of generator to construct this. This is true no matter whether Manager is a struct or a class (or an enum or anything else).
The next two issues are related to how Swift implements structs. A struct stores each of its fields in sequence in memory; there's no indirection. So the size of the struct is based on the size of its properties, and Swift must know its final size at compile time. (In Rust, you can explicitly mark types as Sized to express this explicitly, but in Swift, it's an implicit part of being a struct.)
Optional and Result are themselves just structs, so they inline their contents just like any other struct, and their final size is dependent on the size of their content. Since there is no way to know at compile time how deep this chain goes, there's no way to know how large this struct is.
Classes, on the other hand, store their data on the heap, and are implemented as a pointer to that storage. They are always exactly the same size, no matter what they contain. So a nested Manager? works. It is logically possible, since it can eventually terminate (unlike a nested Manager). And it is sizable because it is always the size of an object pointer. Result works the same way.
Box is just a specific case of a class, which is why it works.
But what about [Manager]? Certainly we don't know how many elements are in it, so how does Swift know how large it is? Because Array is always the same size. It generally stores its contents on the heap, and just stores a pointer internally. There are cases where it can inline its contents if they're small enough. But the key point is that Array itself is a fixed size. It just has a pointer to variable sized storage.
EmptyCollection works for the same reason. It's always the same size, empty.
The final example, using a protocol, is an example of an existential container, which is what the new any points out. An existential container is very similar to the Box type, except that it's made by the compiler and you can't access it directly through the language. It moves the contents to the heap (unless the contents are very small), and so is itself fixed-size.
This brings up one more example that seems to work, but doesn't:
protocol ManagerLike {
var id: String { get set }
var mail: String { get set }
var manager: ManagerLike? { get set }
}
struct Manager<M: ManagerLike> {
var id: String
var mail: String
var manager: M?
}
Now, rather than using an any Manager, it is replaced with a generic. This will compile. So you might think "I'll just pass Manager as the type of M and get my original goal!" And you'll find the rule is...self-enforcing:
let m = Manager<Manager<Manager<Manager<...
The type itself is now infinitely recursive. Oh well. Hard to sneak past algebra.
The key take away is that a struct must know its size, and that means everything within it must know its size, and that's impossible if any of its properties' sizes rely on the size of the struct type itself.
I'm answering my own question after a tip-off by #Sweeper.
By converting the Message struct into a class and changing several extensions, Message recursively containing itself as a property is possible. This is possible since class is a reference type, which are allowed to recursively contain themselves. So, the code below will compile:
class Message: Codable { // <-- Message is now a class
let content: String
// ...other values
let reference: Message
}

Swift: Array of objects that conforms to same protocol as the object

I have a question about generics in swift.
First of all, In JSONDecoder we have the below function.
func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable
And assuming we have a struct called Book: Codable defined before, we can call this function as;
decoder.decode(Book.self, from: bookData)
and also as;
decoder.decode([Book].self, from: bookListData)
We were able to feed the function with the array of Book objects, however as far I know [Book].self does not conform to Decodable.
My question how can we apply a similar solution, when we try to feed a generic struct or class instead of a function. For example,
protocol Publishable { /* literally empty */ }
class Manager<T: Publishable> {
var data: T?
// managing stuff
}
we have the above Manager class and an extesion as, extension Book: Publishable. we can create an instance from it like,
let bookManager = Manager<Book>()
But we cannot directly say,
let bookListManager = Manager<[Book]>()
Let me tell you, I am not sure how the first examples even works. I didn't realize it until I have faced this problem. Anyway, I am looking for a solution or an advice that may enable me to create an instance as above. Or any other way.
Appreciated.
EDIT:
I am aware of a solution where I could create another struct as,
struct BookList: Publishable {
var books: [Book]
}
and then,
let bookListManager = Manager<BookList>()
will work. However, It would require me to create ton of middle structures which would be inconvenient.
as far I know [Book].self does not conform to Decodable
It does. (Actually, that's [Book], not [Book].Type, but [Book].self isn't what you meant.)
extension Array: Decodable where Element: Decodable { …
If you want similar functionality, extend similarly.
extension Array: Publishable where Element: Publishable { }

Protocol conforming to type with associated value

I've got the following snippet:
protocol MyProtocol: Identifiable where ID == UUID {
var id: UUID { get }
}
var test: [MyProtocol] = []
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements
Why doesn't this work? Shouldn't the where ID == UUID remove the ambiguity the error is concerned with? Am I missing something here?
I think this question is similar to this one: Usage of protocols as array types and function parameters in swift
However, I would have assumed that adding where ID == UUID should fix the problem? Why is that not the case?
Thanks!
Edit
So, this problem has occurred while experimenting with SwiftUI and struct data models. I've always used classes for any kind of data model but it seems like SwiftUI wants to get you to use structs as often as possible (I still don't see how that's realistically possible but that's why I'm experimenting with it).
In this particular case, I tried to have a manager that contains structs that all conform to MyProtocol. For example:
protocol MyProtocol: Identifiable where ID == UUID {
var id: UUID { get }
}
struct A: MyProtocol { // First data model
var id: UUID = UUID()
}
struct B: MyProtocol { // Second data model
var id: UUID = UUID()
}
class DataManager: ObservableObject {
var myData: [MyProtocol]
}
...
I don't actually have to declare Identifiable on MyProtocol but I thought it would be nicer and cleaner.
Because this is not a current feature of Swift. Once there is an associated type, there is always an associated type. It doesn't go away just because you constrain it. And once it has an associated type, it is not concrete.
There is no way to "inherit" protocols this way. What you mean is:
protocol MyProtocol {
var id: UUID { get }
}
And then you can attach Identifiable to structs that require it:
struct X: MyProtocol, Identifiable {
var id: UUID
}
(note that no where clause is required.)
There is no Swift feature today that allows you to say "types that conform to X implicitly conform to Y." There is also no Swift feature today that allows for an Array of "things that conform to Identifiable with ID==UUID." (That's called a generalized existential, and it's not currently available.)
Most likely you should go back to your calling code and explore why you require this. If you post the code that iterates over test and specifically requires the Identifiable conformance, then we may be able to help you find a design that doesn't require that.

Can I write protocol behave similar to Encodable & Decodable?

The swift4's Codable protocol is extremely useful. It provide default implementation functions if the conformation is rightly defined.
E.g. this is totally fine:
struct Good: Codable {
var foo: String // Non-optional
var bar: Int? // Optional
}
but this one will raise compile error with the request to create protocol conforming
struct Bad: Codable {
var foo: UIButton // Non-optional raise compile error for not conforming Codable Protocol
var bar: UIView? // optional is okay (not compile error because when decode failed, it fallback to nil)
var codable: SomeCodable // if the property is also Codable, then it's fine too!
}
So, the question is: Can I write a protocol that can require its conformance to follow itself (like properties need to conform same protocol)?
If yes, how? If no, why?
Also, I'd also like to know how defining CodingKeys inside the struct can change the encode/decode behaviour? Can I make something like that in my protocol as well?
Martin is correct you cannot make this on your own without touching the compiler.
First let's take a look at this basic example where I explain how coding keys are used.
struct CodableStruct: Codable {
let primitive: Int // No issues yet
enum CodingKeys: String, CodingKey {
case primitive
// This is the default coding key (i.e the JSON has structure ["primitive": 37]
// You can change this key to anything you need
//
// ex case primitive = "any_thing_you_want"
// JSON has to have structure ["any_thing_you_want": 37]
}
}
Changing the codingKey just changes the key the code will use when looking to "decode" that value from your JSON.
Now let's talk about the compiler. Let's say we create another struct
struct NotCodableStruct {
let number: Double
}
This struct does not conform to Codable. If we go and add this into our previous struct we have:
struct CodableStruct: Codable {
let primative: Int
let notCodable: NotCodableStruct // doesn't compile because this doesn't conform to codable
enum CodingKeys: String, CodingKey {
case primative
case notCodable
}
}
Since NotCodableStruct does not conform to Codable the compiler complains. In other words all variables in a struct or object that conforms to Codable must also conform to Codable. See the below screenshot for more information.
Of course if you make NotCodableStruct conform to Codable everyone will be happy again. Since there is no way for you to enforce the requirement that all variables conform to Codable you cannot make a similar protocol.

Codable class does not conform to protocol Decodable

Why am I getting a "Type 'Bookmark' does not conform to protocol 'Decodable'" error message?
class Bookmark: Codable {
weak var publication: Publication?
var indexPath: [Int]
var locationInText = 0
enum CodingKeys: String, CodingKey {
case indexPath
case locationInText
}
init(publication: Publication?, indexPath: [Int]) {
self.publication = publication
self.indexPath = indexPath
}
}
I do not wish to save the publication var since the Publication owns the Bookmark but the bookmark needs to know which Publication it belongs to. The decode init of Publication will set the bookmark reference to itself.
The compiler cannot synthesise the required init(from:) method due to the weak reference, so you need to write it yourself.
class Bookmark: Codable {
weak var publication: Publication?
var indexPath: [Int]
var locationInText = 0
private enum CodingKeys: String, CodingKey {
case indexPath
case locationInText
}
init(publication: Publication?, indexPath: [Int]) {
self.publication = publication
self.indexPath = indexPath
}
required init(from decoder:Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
indexPath = try values.decode([Int].self, forKey: .indexPath)
locationInText = try values.decode(Int.self, forKey: .locationInText)
}
}
Why am I getting a "Type 'Bookmark' does not conform to protocol 'Decodable'" error message
It's either because Publication isn't Decodable (you have not shown what it is, so it's hard to tell) or because of the weak designation on publication.
Either way, it's easy to fix: you just need to implement init(from:) to complete the implementation of Decodable; the compiler is simply telling you that this implementation cannot be synthesized.
Another reason you could get this message is if your CodingKeys enum isn't exhaustive. If you have three properties in the data type, then your CodingKeys enum needs to have three property/name cases as well.
On the hindsight, I received a similar error when trying to set Codable to my class which consisted of NSNumber type variables. See image below:
Changing NSNumber to primitive data type Int resolved the issue. See below:
I'm guessing this might be true for other datatypes that require bridging to Swift Standard Library Value Types such as NSString, NSArray and so on
In a similar scenario, I was getting the same issue because the variable name in my CodingKeys was not the same as a class variable. See below
Simply because your CodingKeys enum is not exhaustive, add publication property to the enum to achieve that.
try this:
class Bookmark: Codable {
weak var publication: Publication?
var indexPath: [Int]
var locationInText = 0
// All your properties should be included
enum CodingKeys: String, CodingKey {
case indexPath
case locationInText
case publication // this one was missing
}
}
You wont need the init method anymore as the implementation now can be synthesized.
Any class to be a codeable must have it's all property codeable.
Standard library types like String, Int, Double and Foundation types like Date, Data, and UR confirm the codeable protocol but some doesn't.
For e.g below
Note class have all properties of string which confirm codable protocol so no error:
But UIImage don't confirm codable protocol so it throw error:
You can omit a property from coding keys enum, only if it has a default value.
From apple docs
Omit properties from the CodingKeys enumeration if they won't be present when decoding instances, or if certain properties shouldn't be included in an encoded representation. A property omitted from CodingKeys needs a default value in order for its containing type to receive automatic conformance to Decodable or Codable.
In-short, while implementing Codable, all properties which are non-primitive data type (mean class type or may it can be objective-c class) must be Codable.
weak var publication: Publication?
in this case publication is of type class so Publication must have implemented Codable
Bit of a daft one but in case it helps someone else. I got this error because I put enum CodingKeys: CodingKeys instead of enum CodingKeys: CodingKey.
If you have tried all the above solutions and are still unable to fix the error then I think it could be because of the data type you are using for your data class fields.
In the question they have used 1 field weak var publication: Publication?, and if you have the same kind of class structure then maybe you should check if that Publication data class conforms to the Codable class or not.
Because it is mandatory to conform same protocol for the child class as well, as it's fields are also should be encodable and decodable.
I had a similar issue which I stumbled upon this fix to. As I am new to Swift, I am unsure as to why it works! If anyone knows I would appreciate the knowledge.
I changed this:
let id, type: Int
to this:
let id: Int
let type: Int