I'm trying to integrate Gloss to do json parsing but I get lots of non sense errors.
I installed it with pod install and I experienced followings:
First when I import it, I got
: Cannot import underlying modules glossy, I don't know how, but after copy pasting the example from GlossExample project in github repo it disappeared.
Second if I use: struct Repo: Glossy I get error that Repo doesn't conform to protocols Decodable and Encodable but the code is pasted from the example and there exist the methods init?(json: JSON) and func toJSON() -> JSON?
Then I tried to use struct Repo: Decodable and having this function for decoder:
init?(json: JSON) {
let repoId: Int = "id" <~~ json
I get following error:
35: Binary operator '<~~' cannot be applied to operands of type 'String' and 'JSON'
Last I said OK, I won't use overloaded operators but the normal Decoder class:
init?(json: JSON) {
self.repoId = Decoder.decodeURL("id")(json)
}
I got:
Cannot convert value of type 'JSON' to expected argument type 'JSON' (aka 'Dictionary<String, AnyObject>')
Your helps and answers are welcome!
First when I import it, I got : Cannot import underlying modules glossy, I don't know how, but after copy pasting the example from GlossExample project in github repo it disappeared.
This is an issue that happens in Xcode with a few frameworks, but simply cleaning the code or directly running it usually gets rid of the warning. Otherwise it may have to do with using an older build of Xcode.
Second if I use: struct Repo: Glossy I get error that Repo doesn't conform to protocols Decodable and Encodable but the code is pasted from the example and there exist the methods init?(json: JSON) and func toJSON() -> JSON?
This means that your struct doesn't conform to both protocols. From the Glossy declaration:
/**
Convenience protocol for objects that can be
translated from and to JSON
*/
public protocol Glossy: Decodable, Encodable { }
So the Glossy protocol inherits both Decodable and Encodable, which means that you need to implement the protocol functions for both protocols, and not just toJSON() -> JSON?.
Then I tried to use struct Repo: Decodable and having this function for decoder: ...
You need to first declare the constant in the struct, and in the init deserialise the JSON and set the value to the constant:
struct Repo: Decodable {
let repoId: Int
init?(json: JSON) {
guard let repoId: Int = "id" <~~ json { else return nil }
self.repoId = repoId
}
}
Related
The below code snippet, which is taken from this Github issue, fails to compile on Xcode 14.1
enum GenericState<Mode>: Hashable where Mode: Hashable {}
// error: Circular reference
protocol P {
associatedtype Mode: Hashable
// (false positive error) - Type 'Self.Mode' does not conform to protocol 'Hashable'
typealias SomeState = GenericState<Mode>
typealias Foo = [CustomKeyPath<Self, String>]
}
struct Interface<Base> where Base: P {}
typealias CustomKeyPath<Base, T> = KeyPath<Interface<Base>, T> where Base: P
See screenshot of errors:
So, apparently, the latest swift update has some issues with Circular references.
How should I go about fixing these errors?
I was experimenting with Decodable and couldn't figure out how the compiler synthesises a decode(_ type: Decodable.Protocol, forKey key: CodingKey) function for a specific Decodable instance (Such as a Book).
struct Book: Codable {
var title: String
var pages: Int
var author: String
var coauthor: String
}
struct Bookstore: Codable {
var book: Book
var owner: String
}
I tried to write a decode(_:forKey:) function on my own so I could get a better idea of how the compiler synthesises it but I was absolutely blanking. I started with overloading the decode(_:forKey:) function for Book.Type but that's as far as I got.
extension KeyedDecodingContainer {
func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
print("I am about to decode a Book from the data keyed by \(key.stringValue)")
// I have no clue what to put here
}
}
If someone can help me finish this implementation I'll be very grateful as it will help me understand the decoding process better.
Thanks in advance!
Edit:
What I've tried so far:
extension KeyedDecodingContainer {
func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
print("I am about to decode a Book from the data keyed by \(key.stringValue)")
let decoder = try self.superDecoder(forKey: key as! K)
return try Book(from: decoder)
}
}
This works but I have no clue what exactly as! K is doing nor do I know what superDecoder(forKey:) does. Also I'm not sure if using force-casting in this situation is considered risky. Is there a safer way to do this?
I think you misunderstood something here, the compiler doesn't synthesise anything. decode is generic, with a generic type parameter T constrained to Decodable. This means that once it is declared, it will work on all types that conform to Decodable. There isn't a separate implementation of it generated for Book. It's all the same implementation, just with different Ts. The signature that you should be focusing on is:
func decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T : Decodable
How is it implemented? The source code says:
public func decode<T: Decodable>(
_ type: T.Type,
forKey key: Key
) throws -> T {
return try _box.decode(T.self, forKey: key)
}
So a question such as "How would you implement [this method] if you had to?" is not very meaningful because the method has already been implemented, and you would not have to implement it yourself.
Now obviously that implementation isn't very useful in understanding how it works. It just delegates the call to something else. The call eventually gets delegated to KeyedDecodingContainerProtocol, and which implementation of KeyedDecodingContainerProtocol that is, depends on the decoder that you are using. For example, JSONDecoder would use a different decoding container from PropertyListDecoder.
Now the question becomes, how is the decode method in KeyedDecodingContainerProtocol implemented? Well, as we've established, it depends on the decoder. The JSON decoder would do some JSON-specific things, and the property list decoder would do some property list-specific things, but eventually they would probably call T.init(from:) to get an instance of T. KeyedDecodingContainerProtocol.decode is something you can implement yourself. You need to implement this if you are writing your own decoder. T.init(from:) is also something you should implement, if you want your codable object to be decoded in a custom way.
I can't find the source code of JSONDecoder, so here's an open source XML decoder that you can explore: https://github.com/ShawnMoore/XMLParsing
This is how they implemented KeyedDecodingContainerProtocol.decode. This calls unbox, which calls T.init(from:) here.
I have Application protocol with 2 variables. And I have component struct that has a variable, which confirms to Application protocol. I need to save this struct in disk . So I'm confirming it to Codable protocol. While doing so I'm getting an error like this ,
"Protocol type 'Application' cannot conform to 'Decodable' because only concrete types can conform to protocols"
Here is my code,
public protocol Application {
var name : String {get}
var ownerName : String {get}
}
public struct component : Codable {
let application : Application
private enum CodingKeys: String, CodingKey {
case application
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
application = try values.decode(Application.self, forKey: .application)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(application, forKey: .application)
}
}
I'm new to swift so sorry if I'm missing something very obvious. Im not able to fix this and I need some help in right direction. Thank you in advance.
How you address this strongly depends on the problem you're solving.
If you want to store and load exactly these two keys in JSON, then Application should be a struct (as jawadAli notes).
If you mean to store more information, but a given Component is tied to exactly one type of Application, then you want Component to be generic:
public struct Component<App: Application & Codable> : Codable {
let application : App
...
}
Note the addition of & Codable. If all things that conform to Application should be Codable, then you can make that a requirement of Application:
public protocol Application: Codable {
var name : String {get}
var ownerName : String {get}
}
It is important to understand that this does not make Application conform to Codable. It means that in order to conform to Application, a type must also conform to Codable. It is never possible to decode an abstract type (i.e. a protocol).
If you mean to store more information, but a given Component doesn't actually know what kind of Application it holds, then this is a more complicated problem (and often over-complicated and should be rethought; if you find you're using a lot of as? tests, then you should almost certainly redesign). If that's your problem, you should explain a bit more what problem you're solving, and we can discuss how to solve it. (It generally requires some kind of dynamic type registration system, and a JSON format that supports metadata about types. Or you can switch to NSCoder and not use JSON.)
Use Struct that is confirming to codable instead of protocol will solve the issue
public struct Application : Codable {
var name : String
var ownerName : String
}
Currently I have been working on a task of converting code from objective c to swift. The work was going smooth until I occured with a common resuable code that works in objective c but I haven't getting any idea how should I do that in swift.
The scenario working in objective c is.
I have a common function in my dataManager class
- (void)saveRequest:(id)request forId:(NSNumber *)requestId {
WebRequest *requestData = [[WebRequest alloc] initWithEntity:[NSEntityDescription entityForName:WEB_REQUEST inManagedObjectContext:self.context] insertIntoManagedObjectContext:self.context];
requestData.data = [request toJSON];
requestData.requestId = requestId;
requestData.timestamp = [NSDate date];
[self save];
}
in my project the request classes are already created which contains the toJSON function.
from my controller according to user changes I created the request object and passes the request object to this function and this function calls the toJSON function in the request class and everything works in objective c.
But when I convert this function in swift then it didn't support id as function input variable and if I use Any in place of id then it gives an error that Any don't have any toJSON function.
As this function is common different request objects will come from different controllers.
I don't have any idea how should I go further from hear, If anyone have any idea please help me out
Your class should be like
class WebRequest:NSObject
{
var data :Data?
var requestId: NSNumber?
var timestamp: Date?
init(entity:String , insertIntoManagedObjectContext:NSManagedObjectContext)
{
//your code here
}
}
and your code will be as follows
func saveRequest(request:Request, requestId:NSNumber)
{
let requestData = WebRequest(entity: "entityName", insertIntoManagedObjectContext:self.context)
requestData.data = request.toJSON();
requestData.requestId = requestId;
requestData.timestamp = Date()
}
and Request class in which toJson() present
class Request: NSObject
{
//has some members
func toJSON()->Data
{
return Data()
}
}
There is an existing Swift protocol, Codable (or you can do just Encodable if you want, as Codable is merely Encodable and Decodable), which is designed explicitly for representing an object in JSON (or other formats).
You then use JSONEncoder (rather than JSONSerialization, for example) to encode the object into JSON. See Encoding and Decoding Custom Types:
Consider a Landmark structure that stores the name and founding year of a landmark:
struct Landmark {
var name: String
var foundingYear: Int
}
Adding Codable to the inheritance list for Landmark triggers an automatic conformance that satisfies all of the protocol requirements from Encodable and Decodable:
struct Landmark: Codable {
var name: String
var foundingYear: Int
}
You can then do:
let landmark = Landmark(name: "Big Ben", foundingYear: 1859)
do {
let data = try JSONEncoder().encode(landmark)
print(String(data: data, encoding: .utf8)!)
} catch {
print(error)
}
That will product JSON like so:
{
"name": "Big Ben",
"foundingYear": 1859
}
See that Encoding and Decoding Custom Types for more information.
But, if you make your types Codable/Encodable, you could then retire your toJSON method entirely. There’s no need to write code to encode JSON anymore.
If you’re looking for a more tactical edit to your project as you convert it from Objective-C to Swift, you could define your own protocol, say JsonRepresentable, that has a single method requirement, your toJSON (or to whatever you’ve renamed this method during your conversion process).
protocol JsonRepresentable {
func toJSON() -> Data
}
And then, for all of the types that have implemented this method, just add this conformance.
Ideally, go back to those individual files and move the method into an extension for that protocol, e.g., for your first object type:
extension RequestObject1: JsonRepresentable {
func toJSON() -> Data {
...
}
}
And for your second:
extension RequestObject2: JsonRepresentable {
func toJSON() -> Data {
...
}
}
Etc.
is not there a simpler way rather than changing it in whole project
I would suggest that the above is best, but, if you don’t want to go back to all of those individual type declarations, you can just add conformance with an empty extension right where you defined JsonRepresentable:
extension RequestObject1: JsonRepresentable { }
extension RequestObject2: JsonRepresentable { }
As long as those types have implemented that method, these extensions will let the compiler know about their conformance to your protocol.
Anyway, this method can then use this protocol:
func save(_ request: JsonRepresentable, requestId: Int) {
let requestData = ...
requestData.data = request.toJSON()
requestData.requestId = requestId
requestData.timestamp = Date()
save()
}
I'm using Swift and trying to make some collection objects. These collection objects have a backing Dictionary to hold custom objects. For example, an object might be of type Cat and the collection object would be of type Cats. Cats would have a private dictionary containing values of type Cat. I have other types that also need respective collections (each collection type has specific logic for the type it holds).
I created a protocol to ensure each collection has a few common characteristics. These common functions and subscripts are generally passthroughs to the backing dictionary. Here is the protocol:
protocol ObjectDictionaryProtocol {
// These are necessary for generics to work in protocols
typealias Key: Hashable
typealias Value
// MARK: - Properties
var count: Int { get }
var isEmpty: Bool { get }
var keys: LazyMapCollection<Dictionary<Key, Value>, Key> { get }
var values: LazyMapCollection<Dictionary<Key, Value>, Value> { get }
// MARK: - Subscripts
subscript(key: Key) -> Value? { get set }
}
When I go to actually use the protocol as a type, for instance:
var objects: ObjectDictionaryProtocol
or
init(objs: ObjectDictionaryProtocol) {
...
}
I get the error:
Protocol 'ObjectDictionaryProtocol' can only be used as a generic constraint because it has Self or associated type requirements
I've searched around and it looks like the Hashable protocol I'm conforming to for my Key typealias is causing this. What is the best way to get around this? Is there a way to change the protocol such that I don't need the Hashable, or do I need to do something in the class that is using ObjectDictionaryProtocol? Or maybe there's a better way to effectively 'subclass' a Swift Dictionary (quotes because I realize the Dictionary struct cannot be subclassed)?
A protocol with associated types is less a type and more a template for a type. The actual type exists when the protocol's associated types are specified. Thus ObjectDictionaryProtocol 'becomes' a type when you specify:
ObjectDictionaryProtocol<String,Cat>
but the above is not valid Swift...
So you asked... '[is there] a better way to effectively 'subclass' a Swift Dictionary'. You might get by with an extension with something like:
class Cat {}
extension Dictionary where Value : Cat {
func voice (name: Key) {
if let _ = self[name] {
print ("\(name): Meow")
}
}
}
var someCats = Dictionary<String,Cat>()
someCats["Spot"] = Cat()
someCats.voice("Spot")
// Spot: Meow
Or you may need to actually implement the protocol.
class ObjectDiciontary<Key:Hashable, Value> : ObjectDictionaryProtocol {
var backingDictionary = Dictionary<Key,Value>()
// implement protocol
}
var object : ObjectDictionary<String,Cat> = ...
// ...
The reason is because when you're using typealias you're effectivly making your Protocol into a generic protocol as Protocol<T>. Bear with me here, I'll explain why and how to fix it.
The issue here is that Apple has decided to make typealias the keyword for defining associated types. This is fixed in Swift 2.2 (Xcode 7.3)
You can read more about it on https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md
It's being renamed to associatedtype which makes more sense.
This means your protocol has to be adopted, and there define its associated types.
In your case it would look something like
protocol ObjectDictionaryProtocol {
associatedtype Value
}
extension String : ObjectDictionaryProtocol {
associatedtype = Double
}
The init could look like
init<T : ObjectDictionaryProtocol>(objs: ObjectDictionaryProtocol)
or
init<T : ObjectDictionaryProtocol
where T.Value == Double>(objs: ObjectDictionaryProtocol)
Now for typealias Key: Hashable it means that whatever type that is assigned to Key has to conform or be Hashable
This will give you an error that String isn't conforming to ObjectDictionaryProtocol as it can't fulfil the requirements.
protocol ObjectDictionaryProtocol {
associatedtype Value : FloatingPointType
}
extension String : ObjectDictionaryProtocol {
associatedtype = Int
}
Int isn't a FloatingPointType (but Double is)