Using Vapor for return model to node :
func indexView(request: Request) throws -> ResponseRepresentable
{
let acro = try Acronym.makeQuery().sort(Acronym.idKey, .ascending)
return try acro.all().makeNode(in: <#T##Context?#>)
}
It always return error and don't know how to fixed it.
Contexts are used generally to pass current-use information around inside Vapor. The Vapor 3 documentation does not appear to contain detailed information (yet), but see https://docs.vapor.codes/2.0/node/getting-started/ for Vapor 2. I have never found a need for them in makeNode, so putting:
return try acro.all().makeNode(in:nil)
should make it work.
Node has been phased out in Vapor3. There's no need for it since it relies in Swift's native data types+protocols. In this case, Vapor 3 uses Content to work with JSON. (Content conforms to Codable by default)
e.g.
final class Person: Content, MySQLModel, Migration {
var id: Int?
var name: String
init(id: Int? = nil, name: String) {
self.id = id
self.name = name
}
}
func person(_ req: Request) throws -> Future<Person> {
return Person(name: "Mark")
}
router.get("test") { req -> Future<[Person]> in
return try Person.query(on: req).sort(\.id, .ascending).all()
}
Excerpt from the docs:
In Vapor 3, all content types (JSON, protobuf, URLEncodedForm, Multipart, etc) are treated the same. All you need to parse and serialize content is a Codable class or struct.
Related
Introduction
(Apologies if the title is confusing, but I explain the question better here!)
I'm building a networking library that can perform JSON decoding on its responses.
Host apps adopting this library will create enums conforming to NetLibRoute. All that currently does is enforce the presence of asURL:
public protocol NetLibRoute {
var asURL: URL { get throws }
}
In a host app, I have a routing system that enforces API structure at the compiler-level (via enums and associated values) for each endpoint, like this:
enum Routes: NetLibRoute {
case people(Int?)
// Other routes go here, e.g.:
// case user(Int)
// case search(query: String, limit: Int?)
var asURL: URL {
let host = "https://swapi.dev/"
let urlString: String
switch self {
case let .people(personID):
if let personID {
urlString = host + "api/people/\(personID)"
} else {
urlString = host + "api/people/"
}
// Build other URLs from associated values
}
return URL(string: urlString)!
}
}
I also want each enum to be associated with a certain Codable type. I can do that, of course, by modifying the Route protocol declaration to also require a type conforming to Decodable:
protocol NetLibRoute {
var asURL: URL { get throws }
var decodedType: Decodable.Type { get } // This
}
And a matching computed property in my Routes enum:
var decodedType: Decodable.Type {
switch self {
case .people(_):
return Person.self
// And so on
}
}
The Problem
Currently, my networking code has a declaration something like this:
public static func get<T>(route: NetLibRoute,
type: T.Type) async throws -> T where T: Decodable {
// performing request on route.asURL
// decoding from JSON as T or throwing error
// returning decoded T
}
Which lets me call it like this:
let person = try await NetLib.get(route: Routes.people(1), type: Person.self)
However, this redundancy (and potential human error from mismatching route and type) really irks me. I really want to be able to only pass in a route, and have the resulting type be inferred from there.
Is there some way to get the compiler to somehow check the NetLibRoute enum and check its decodedType property, in order to know what type to use?
Ultimately, I want this networking function to take one parameter (a route) and infer the return type of that route (at compile-time, not with fragile runtime hacks or !s), and return an instance of the type.
Is this possible?
Potential Alternatives?
I'm also open to alternative solutions that may involve moving where the get function is called from.
For example, calling this get function on a route itself to return the type:
let person = try await Routes.people(1).get(type: Person.self) // Works, but not optimal
let person = try await Routes.people(1).get() // What I want
Or even on the type itself, by creating a new protocol in the library, and then extending Decodable to conform to it:
public protocol NetLibFetchable {
static var route: NetLibRoute { get }
}
extension Decodable where Self: NetLibFetchable {
public static func get<T>() async throws -> T where Self == T, T: Decodable {
// Call normal get function using inferred properties
return try await NetLib.get(route: route,
type: T.self)
}
Which indeed lets me call like this:
let person = try await Person.get() // I can't figure out a clean way to pass in properties that the API may want, at least not without once again passing in Routes.people(1), defeating the goal of having Person and Routes.people inherently linked.
While this eliminates the issue of type inference, the route can no longer be customized at call-time, and instead is stuck like this:
extension Person: NetLibFetchable {
public static var route: NetLibRoute {
Routes.people(1) // Can't customize to different ID Ints anymore!
}
}
Which makes this particular example a no-go, and leaves me at a loss.
Appreciation
Anyway, thank you so much for reading, for your time, and for your help.
I really want this library to be as clean as possible for host apps interacting with it, and your help will make that possible.
Are you wedded to the idea of using an enum? If not, you can do pretty much what you want by giving each enum value its own type and using an associated type to do what you want.
public protocol NetLibRoute
{
var asURL: URL { get throws }
associatedtype Decoded: Decodable
}
struct Person: Decodable
{
var name: String
}
struct Login: Decodable
{
var id: String
}
struct People: NetLibRoute
{
typealias Decoded = Person
var id: Int
var asURL: URL { return URL(filePath: "/") }
}
struct User: NetLibRoute
{
typealias Decoded = Login
var id: String
var asURL: URL { return URL(filePath: "/") }
}
func get<N: NetLibRoute>(item: N) throws -> N.Decoded
{
let data = try Data(contentsOf: item.asURL)
return try JSONDecoder().decode(N.Decoded.self, from: data)
}
let thing1 = try get(item: People(id: 1))
let thing2 = try get(item: User(id: "foo"))
Where you might have had a switch before to do different things with different Routes you would now use a function with overloaded arguments.
func doSomething(thing: Person)
{
// do something for a Person
}
func doSomething(thing: Login)
{
// do something else for a Login
}
doSomething(thing: thing1)
doSomething(thing: thing2)
I think the problem lays in this function.
public static func get<T>(route: Route,
type: T.Type) async throws -> T where T: Decodable {
// performing request on route.asURL
// decoding from JSON as T or throwing error
// returning decoded T
}
On the first hand, it uses concretions instead of abstractions. You shouldn't pass a Route here, it should use your protocol NetLibRoute instead.
On the other hand, I think that the type param is not needed. Afaik you can get the Type to Decode with the var:
NetLibRoute.decodedType
Am I missing something on this matter?
Apart from that, I'd rather go with struct instead of enum when trying to implement the Routes (concretions). Enums cannot be extended. So you won't be allowing the creation of new requests in client side, only in the library.
I hope I've helped.
PS: Some time ago I made this repo. Maybe that could help you (specially this class). I used Combine instead of async/await, but it's not relevant to what you need.
For context: I'm using kotlin multiplatform to communicate with my backend for my both apps, I'm also trying to follow Clean Architecture + MVVM.
My multiplatform project holds my data layer (repositories and dtos) and my app project holds the presentation and domain layer (VC, View Model, Use Case).
I'm trying to do is mock my repository in the multiplatform project.
I have a class (DTO) on my Kotlin Multiplatform project that looks like this
#Serializable
data class LoginResponse(val data: LoginResponseBody){
#Serializable
data class LoginResponseBody(
var accessToken: String,
var lastChannel: String? = null,
var lastDateTime: String? = null,
var userId: String? = null,
var transactionalKeyStatus: String? = null,
var accessStatus: String? = null
)
}
When I add that multiplatform project into my xcode project, the object translates to:
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("LoginResponse")))
#interface SharedEmaLoginResponse : SharedEmaBase
- (instancetype)initWithData:(SharedEmaLoginResponseLoginResponseBody *)data __attribute__((swift_name("init(data:)"))) __attribute__((objc_designated_initializer));
- (SharedEmaLoginResponseLoginResponseBody *)component1 __attribute__((swift_name("component1()")));
- (SharedEmaLoginResponse *)doCopyData:(SharedEmaLoginResponseLoginResponseBody *)data __attribute__((swift_name("doCopy(data:)")));
- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)")));
- (NSUInteger)hash __attribute__((swift_name("hash()")));
- (NSString *)description __attribute__((swift_name("description()")));
#property (readonly) SharedEmaLoginResponseLoginResponseBody *data __attribute__((swift_name("data")));
#end;
I thought that class LoginResponse would conform to 'Decodable' but it doesn't, this is what my fake repository looks like in my xcode project
final class LoginRepositoryTest : XCTestCase, IAuthRepository {
var hasError = false
func login(loginRequest: LoginRequest, success: #escaping (LoginResponse) -> Void, failure: #escaping (ErrorResponse) -> Void, completionHandler: #escaping (KotlinUnit?, Error?) -> Void) {
if hasError {
failure(ErrorResponse(messages: []))
} else {
let data = loadStub(name: "loginResponse", extension: "json")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let loginData = try! decoder.decode(LoginResponse.self, from: data)
success(loginData)
}
}
func getKoin() -> Koin_coreKoin {}
}
Error
Instance method 'decode(_:from:)' requires that 'LoginResponse' conform to 'Decodable'
I think I have these two options:
I make that kotlin class conform to 'Decodable'
I find a way to convert that JSON to that object without using Decodable
Any help is appreciated
Kotlin serialization isn't meant to confirm Decodable protocol to the class. It's meant to be used from kotlin with kotlin serialization, like this:
val response: LoginResponse = Json.decodeFromString(
LoginResponse.serializer(),
string
)
p.s. originally you can use Json.decodeFromString<LoginResponse>(string), but this's not yet supported on multiplatform as far as I remember
Network and JSON parsing are things which are most easily done on KMM, and you can write tests there too, but in case you still need to use it directly from iOS, you can try the following wrapper:
object JsonHelper {
#Throws(Throwable::class)
fun decodeRtcMsg(string: String) =
Json.decodeFromString(
RtcMsg.serializer(),
string
)
}
Then you can use it from swift:
do {
try JsonHelper().decodeLoginResponse(string: "")
} catch {
print(error)
}
If you really need it to Decodable I'd create an expect/actual of Decodable for ex:
// in common
expect interface Decodable
// on Android
actual interface Decodable
// on iOS
actual typealias Decodable = Decodable
Then just implement Decodable for your DTOs. Perhaps the best example for this is how you could use the Parcelize in your shared code: https://github.com/icerockdev/moko-parcelize/blob/master/parcelize/src/androidMain/kotlin/dev/icerock/moko/parcelize/Parcelize.kt
I am new to iOS and trying to store User object in UserDefaults. So that when the app is launched again, I can check user type and based on it, I need to navigate to relevant screen.
For that, I have created a User class as below (Codable) and it has one userType enum property!
enum UserType: Int, Codable {
case userType1 = 0
case userType2 = 1
case notDetermined = 2
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(Int.self)
self = UserType(rawValue: label) ?? .notDetermined
}
}
class User: Codable {
public var userFullName: String? = ""
public var userType: UserType? //= .notDetermined
enum CodingKeys: String, CodingKey {
case userFullName
}
}
In my view Controller class, I am creating a new instance for User object and trying to store in user defaults as below:
let newUser = User()
newUser.userFullName = "Test"
newUser.userType = userTypeBtn.isSelected ? .userType1 : .userType2
when I print the newUser's userType, I can see proper value whichever is selected. But after that, when I am trying to store it in userDefaults as below, it returns nil for userType property.
do {
let encoded = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(encoded, forKey: UserDefaultKey.currentUser)
UserDefaults.standard.sync()
} catch {
print("Unable to Encode User Object: (\(error))")
}
when I tried to print this encoded variable, and decoded it in console
JSONDecoder().decode(User.self, from: encoded).userType
it prints nil.
Please help me how can I store optional enum property in UserDefaults and retrieve it when needed using Codable
You should include userType in your CodingKeys enum:
enum CodingKeys: String, CodingKey {
case userFullName
case userType
}
Or just delete the CodingKeys enum entirely, since by default, all the properties are included as coding keys. The keys in the CodingKeys enum determines what the synthesised Codable implementation will encode and decode. If you don't include userType, userType will not be encoded, so it will not be stored into UserDefaults.
I am not getting it from Server and userType is an external property outside the JSON response
This is fine, because userType is optional. If the JSON does not have the key, it will be assigned nil. This might be a problem if you are also encoding User and sending it to the server, and that the server can't handle extra keys in the request, in which case you need two structs - one for storing to/loading from UserDefaults, one for parsing/encoding server response/request.
Remember to encode a new User to UserDefaults when you try this out, since the old one still doesn't have the userType encoded with it.
Observations
Having a custom implementation for Decodable part of enum UserType: Int, Codable is probably not the best idea. Swift compiler supports encoding/decoding enum X: Int out of the box without having you to write custom implementation for it. (In fact, starting with Swift 5.5, Swift compiler can now do this for enums that have cases with associated values as well.)
You should try to avoid having cases like .notDetermined. Either user has a type that's well defined or user.type is nil. You can easily define convenience getters on user itself to know about it's type.
Swift allows nesting of types, so having User.Kind instead of UserType is more natural in Swift.
Following implementation takes care of all of these points.
import Foundation
class User: Codable {
enum Kind: Int, Codable {
case free = 1
case pro = 2
}
public var fullName: String?
public var kind: Kind?
}
let newUser = User()
newUser.fullName = "Test"
newUser.kind = .free
do {
let encoded = try JSONEncoder().encode(newUser)
UserDefaults.standard.set(encoded, forKey: "appUser")
if let fetched = UserDefaults.standard.value(forKey: "appUser") as? Data {
let decoded = try JSONDecoder().decode(User.self, from: fetched)
print(decoded)
}
}
Above code includes definition, construction, encodeAndStore, fetchAndDecode and it does everything you need without any custom implementation.
Bonus
Above code does not print a nice description for the User. For that, you can add CustomStringConvertible conformance like this.
extension User: CustomStringConvertible {
var description: String {
"""
fullName: \(fullName ?? "")
kind: \(kind?.description ?? "")
"""
}
}
extension User.Kind: CustomStringConvertible {
var description: String {
switch self {
case .free: return "free"
case .pro: return "pro"
}
}
}
If you try print(decoded) after implementing this, you will clearly see what you want to see for User instance.
User.kind can be nil and I don't want to handle it with if let every time I need to check this from different screens in the app.
No worries, it can be simplified to this.
extension User {
var isFreeUser: Bool { kind == .free }
var isProUser: Bool { kind == .pro }
}
I'm struggling to understand class/reference type behavior and how this relates to changes as I try to upgrade and reduce code using Codable in Swift 4.
I have two classes – a SuperClass with all of the data that will be persistent and that I save to UserDefaults (a place name & string with coordinates), and a SubClass that contains additional, temporary info that I don't need (weather data for the SuperClass coordinates).
In Swift 3 I used to save data like this:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
// subClassArray is of type [SubClass] and contains more data per element.
superClassArray = subClassArray
let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
UserDefaults.standard.set(superClassData, forKey: " superClassData")
}
SuperClass conformed to NSObject & NSCoding
It also included the required init decoder & the encode function.
It all worked fine.
In trying to switch to Swift 4 & codable I've modified SuperClass to conform to Codable.
SuperClass now only has one basic initializer and none of the encoder/decoder stuff from Swift 3. There is no KeyedArchiving happening with this new approach (below). SubClass remains unchanged. Unfortunately I crash on the line where I try? encoder.encode [giving a Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)]. My assumption is that the encoder is getting confused with identical reference types where one is SuperClass and one SubClass (subClassArray[0] === superClassArray[0] is true).
I thought this might work:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
superClassArray = subClassArray
// assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(superClassArray){
UserDefaults.standard.set(encoded, forKey: " superClassArray ")
} else {
print("Save didn't work!")
}
}
Then, instead of creating an empty superClassArray, then using:
superClassArray = subClassArray, as shown above, I replace this with the single line:
let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)}
This works. Again, assumption is because I'm passing in the values inside of the class reference type & haven't made the superClassArray = subClassArray. Also, as expected, subClassArray[0] === superClassArray[0] is false
So why did the "old stuff" in Swift 3 work, even though I used the line superClassArray = subClassArray before the let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
? Am I essentially achieving the same result by creating the array in Swift 4 that was happening with the old Swift 3 encoder/decoder? Is the looping / recreation
Thanks!
Polymorphic persistence appears to be broken by design.
The bug report SR-5331 quotes the response they got on their Radar.
Unlike the existing NSCoding API (NSKeyedArchiver), the new Swift 4 Codable implementations do not write out type information about encoded types into generated archives, for both flexibility and security. As such, at decode time, the API can only use the concrete type your provide in order to decode the values (in your case, the superclass type).
This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver
I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information.
Update I have written a sample using a single struct that manages recreating polymorphic classes. Available on GitHub.
I was not able to get it to work easily with subclassing. However, classes that conform to a base protocol can apply Codable for default encoding. The repo contains both keyed and unkeyed approaches. The simpler is unkeyed, copied below
// Demo of a polymorphic hierarchy of different classes implementing a protocol
// and still being Codable
// This variant uses unkeyed containers so less data is pushed into the encoded form.
import Foundation
protocol BaseBeast {
func move() -> String
func type() -> Int
var name: String { get }
}
class DumbBeast : BaseBeast, Codable {
static let polyType = 0
func type() -> Int { return DumbBeast.polyType }
var name:String
init(name:String) { self.name = name }
func move() -> String { return "\(name) Sits there looking stupid" }
}
class Flyer : BaseBeast, Codable {
static let polyType = 1
func type() -> Int { return Flyer.polyType }
var name:String
let maxAltitude:Int
init(name:String, maxAltitude:Int) {
self.maxAltitude = maxAltitude
self.name = name
}
func move() -> String { return "\(name) Flies up to \(maxAltitude)"}
}
class Walker : BaseBeast, Codable {
static let polyType = 2
func type() -> Int { return Walker.polyType }
var name:String
let numLegs: Int
let hasTail: Bool
init(name:String, legs:Int=4, hasTail:Bool=true) {
self.numLegs = legs
self.hasTail = hasTail
self.name = name
}
func move() -> String {
if numLegs == 0 {
return "\(name) Wriggles on its belly"
}
let maybeWaggle = hasTail ? "wagging its tail" : ""
return "\(name) Runs on \(numLegs) legs \(maybeWaggle)"
}
}
// Uses an explicit index we decode first, to select factory function used to decode polymorphic type
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which
//: is a valid index into the arrays `encoders` and `factories`
struct CodableRef : Codable {
let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast
typealias EncContainer = UnkeyedEncodingContainer
typealias DecContainer = UnkeyedDecodingContainer
typealias BeastEnc = (inout EncContainer, BaseBeast) throws -> ()
typealias BeastDec = (inout DecContainer) throws -> BaseBeast
static var encoders:[BeastEnc] = [
{(e, b) in try e.encode(b as! DumbBeast)},
{(e, b) in try e.encode(b as! Flyer)},
{(e, b) in try e.encode(b as! Walker)}
]
static var factories:[BeastDec] = [
{(d) in try d.decode(DumbBeast.self)},
{(d) in try d.decode(Flyer.self)},
{(d) in try d.decode(Walker.self)}
]
init(refTo:BaseBeast) {
self.refTo = refTo
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let typeCode = try container.decode(Int.self)
self.refTo = try CodableRef.factories[typeCode](&container)
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let typeCode = self.refTo.type()
try container.encode(typeCode)
try CodableRef.encoders[typeCode](&container, refTo)
}
}
struct Zoo : Codable {
var creatures = [CodableRef]()
init(creatures:[BaseBeast]) {
self.creatures = creatures.map {CodableRef(refTo:$0)}
}
func dump() {
creatures.forEach { print($0.refTo.move()) }
}
}
//: ---- Demo of encoding and decoding working ----
let startZoo = Zoo(creatures: [
DumbBeast(name:"Rock"),
Flyer(name:"Kookaburra", maxAltitude:5000),
Walker(name:"Snake", legs:0),
Walker(name:"Doggie", legs:4),
Walker(name:"Geek", legs:2, hasTail:false)
])
startZoo.dump()
print("---------\ntesting JSON\n")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encData = try encoder.encode(startZoo)
print(String(data:encData, encoding:.utf8)!)
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData)
print ("\n------------\nAfter decoding")
decodedZoo.dump()
Update 2020-04 experience
This approach continues to be more flexible and superior to using Codable, at the cost of a bit more programmer time. It is used very heavily in the Touchgram app which provides rich, interactive documents inside iMessage.
There, I need to encode multiple polymorphic hierarchies, including different Sensors and Actions. By storing signatures of decoders, it not only provides with subclassing but also allows me to keep older decoders in the code base so old messages are still compatible.
I am learning to parse JSON in Swift, coming from Android/Java, and I am using Unbox by John Sundell to help me with this, which reminds me of GSON.
Reference: Unbox pod
I use Realm as a database to store data locally.
Reference: Realm.io
It would be great to find a workflow to parse a class with JSON and save it to Realm. I don't want to have a struct that implements Unboxable AND a class that implements Object (Realm), because then I have to reflect the two. That isn't too much work for my current project, but it is kinda ugly...
Did any of you try a similar workflow?
I don't think you need two separate types. My suggestion is to create your objects as Swift classes that inherit from Realm's Object class, and then also conform them to the Unboxable protocol that Unbox offers. (Although the examples on Unbox's page use struct models, there's nothing in the code or documentation that indicates that classes wouldn't work.)
Realm model objects work just like any other classes: in addition to defining whatever properties on the objects you'd like stored in the database, you can also define methods and initializers, and even specify properties that you want Realm to ignore. This allows you to create an object that both serves as a Realm model and also a JSON model compatible with Unbox.
A more concise approach that doesn't require to override required initialisers (based on a tweet by Marin Todorov):
class Car: Object, Unboxable {
dynamic var vendor: String = ""
dynamic var modelName: String = ""
dynamic var electric: Bool = false
required convenience init(unboxer: Unboxer) throws {
self.init()
self.vendor = try unboxer.unbox(key: "vendor")
self.modelName = try unboxer.unbox(key: "modelName")
self.electric = try unboxer.unbox(key: "electric")
}
}
Here is an example that works perfectly for me:
class ProviderRealm: Object, Unboxable {
dynamic var identifier: String = "demo"
dynamic var name: String?
dynamic var logo: String?
/// Initializer used for unboxing of JSON string
required init(unboxer: Unboxer) throws {
self.identifier = (try? unboxer.unbox(key: "identifier")) ?? "demo"
self.name = try? unboxer.unbox(key: "name")
self.logo = try? unboxer.unbox(key: "logo")
super.init()
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
required init() {
super.init()
}
required init(value: Any, schema: RLMSchema) {
super.init(value: value, schema: schema)
}
override static func primaryKey() -> String? {
return "identifier"
}
}