I have a struct which confirm codable protocol.
public struct FlightDTO {
public var flightName: String = ""
public var PilotName: String = ""
public var copilotName: String = ""
public var totalCrewMem: Int = 0
}
extension FlightDTO: Codable {
enum CodingKeys: String, CodingKey {
case flightName
case PilotName
case copilotName
case totalCrewMem
}
}
when I create object of struct it give me this initialiser.
This struct is in swift package and I am initialising it from the app.
var flightData = FlightDTO(from: <#Decoder#>)
I am not sure what to pass in decoder.
The init that is suggested is used by the JSONDecoder when decoding JSON but to create objects manually in code you need to create your own public init because the synthesised one is internal.
So either
public init() {} // use default values
or
public init FlightDTO(flightName: String, PilotName: String, copilotName: String, totalCrewMem: Int) {
self.flightName = flightName
self.PilotName = PilotName
self.copilotName = copilotName
self.totalCrewMem = totalCrewMem
}
Related
I would use nodesWithDepth out of decodable:
this worked before:
public struct NEWTREE: Equatable, Codable {
public var Filename: String
public var GROUP: [GROUP]
public var ITEM: [ITEM]
public var CATEGORY: [CATEGORY]
public var ROOT: ROOT
but the modified not:
public struct NEWTREE: Equatable, Codable {
public var Filename: String
public var GROUP: [GROUP]
public var ITEM: [ITEM]
public var CATEGORY: [CATEGORY]
public var ROOT: ROOT
public var nodesWithDepth: [(text: String, depth: Int, type: TreeData2)]?
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
Filename = try container.decode(String.self, forKey: .Filename)
GROUP = try container.decode([GROUP].self, forKey: .GROUP)
ITEM = try container.decode([ITEM].self, forKey: .ITEM)
CATEGORY = try container.decode([CATEGORY].self, forKey: .CATEGORY)
ROOT = try container.decode(ROOT.self, forKey: .ROOT)
}
private enum CodingKeys: String, CodingKey {
case Filename
case GROUP
case ITEM
case CATEGORY
case ROOT
}
but this raise an error:
Cannot convert value of type '[[GROUP]]' to expected argument type '[GROUP].Type'
How can I fix?
Swift is confused by expressions such as
[GROUP].self
Apparently, the compiler thinks that GROUP here refers to the property GROUP, rather than the type GROUP. Note that it is totally valid to add .self to any expression, without changing what it means.
So effectively, you are passing a one-element array, containing the value of the property GROUP. This expression has the type [[GROUP]], but decode obviously expects a decodable metatype, hence the error.
Or in the case of ROOT, you are passing the value of the ROOT property, which has the type ROOT, but decode obviously expects a decodable metatype.
This behaviour can be shown with a more minimal example like this:
struct A {}
struct B {
let A: A
func f() {
print(A.self)
print([A].self)
}
}
B(A: A()).f()
print("-------")
print(A.self)
print([A].self)
The two print statements outside B prints metatypes, whereas the ones inside B prints an instance of A and an array.
You can solve this ambiguity by using the long form of the array type, Array<Group>.self
GROUP = try container.decode(Array<GROUP>.self, forKey: .GROUP)
Or introduce a type alias in cases such as ROOT:
typealias ROOTType = ROOT
...
ROOT = try container.decode(ROOTType.self, forKey: .ROOT)
But of course, none of this would have happened if you adhered to the Swift naming guidelines, and did:
var groups: [Group]
If you want the coding keys to all be capitalised, you can do it in the coding keys enum:
public struct NewTree: Equatable, Codable {
public var filename: String
public var groups: [Group]
public var items: [Item]
public var categories: [Category]
public var root: Root
// consider making the tuple here into another struct...
public var nodesWithDepth: [(text: String, depth: Int, type: TreeData2)]?
// you don't actually need to write the custom decoding logic just because you added nodesWithDepth
// Swift can figure it out from the CodingKeys enum because the
// case names match the property names
private enum CodingKeys: String, CodingKey {
case filename = "Filename"
case groups = "GROUP"
case items = "ITEM"
case categories = "CATEGORY"
case root = "ROOT"
}
}
Is there a way in Swift that I can get a property from a struct without having to reference it?
For example
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
It's possible by adopting CustomStringConvertible and constraining T also to CustomStringConvertible
public struct ASValue<T: Hashable>: Hashable, CustomStringConvertible where T: CustomStringConvertible {
private let id = UUID()
public var item: T
public var description : String {
return "\(item)"
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
And this is a struct, the init method is for free.
What you are looking for is exactly provided with property wrappers in swift. You have to make your ASValue structproperty wrapper by applying #propertyWrapper to the declaration:
#propertyWrapper
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var wrappedValue: T
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
Then you can use this wrapper to wrap any property in another type and access that property directly. You can even access the ASValue wrapper type by prefixing with _:
class SomeClass {
#ASValue var val = "Example"
func printVal() {
// print(_val) for actual value
print(val) // Prints: Example
}
}
You can also pass this property with its wrapper identity to other function:
func printVal<T: Hashable>(#ASValue val: T) {
// print(_val) for actual value
print(val) // Prints: Example
}
let obj = SomeClass()
printVal(val: obj.val)
If for some constraint you can't use the property wrapper approach you can try the #dynamicCallable attribute as a workaround. This allows values created from your types to be invoked as methods with specified arguments.
#dynamicCallable
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
// need to have withArguments with argument type confirming
// to ExpressibleByArrayLiteral
func dynamicallyCall(withArguments arg: [Int] = []) -> T {
return item
}
}
let val = ASValue(item: "Example")
print(val()) // Prints: Example
Goal
To create an "AutoIDable" protocol with the following behaviour.
Every instance of a class conforming to this protocol will get an auto-generated "id" property of String type.
The code should generate id strings in the format <prefix><Instance-count-starting-from-1> (Eg: E-1, E-2, ...E-<n> and so on for 1st , 2nd ... nth Instance of the conforming class.
The protocol & protocol extensions should do ALL of the required work to generate the id strings. The conforming class will only have to subscribe to the protocol and nothing more.
Current status:
I have achieved Goal-1 & Goal-2 with the following implementation:
protocol Identifiable {
var id: String { get }
}
protocol AutoIDable: Identifiable{
static var _instanceCount: Int { get set }
}
class AutoID: AutoIDable {
init(idPrefix: String) {
setAutoID(prefix: idPrefix)
}
internal static var _instanceCount: Int = 0
var id: String = ""
func setAutoID(prefix: String = ""){
Self._instanceCount += 1
self.id = "\(prefix)\(Self._instanceCount)"
}
}
class Employee: AutoID {
init(){
super.init(idPrefix: "E-")
}
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)
print(e2.id)
print(e3.id)
print(e1.id)
The output from running the above code:
E-1
E-2
E-3
E-1
Todo:
To achieve Goal-3, I need to eliminate the AutoID superclass and implement the same functionality using protocol extensions.
I ran into trouble because:
Protocol extensions do not allow static stored properties. I do know how to work around this limitation without using a superclass.
I do not know how to inject code into all the initialisers the creator of the Employee class might create. Again, I could not think of a workaround without using a superclass.
I would be grateful if you can point me in the right direction.
PS: New to Swift programming. If you’ve suggestions for implementing the code in a more “swifty” way, please do let me know. :-)
Since you want to use protocols, you can't have a stored property in the protocol. So, you'll need some place to store the incrementing ID value, if not the IDs themselves.
Not sure if it violates your requirements of using only protocols, because it would require a type for storage, but at least it won't require conforming classes to have a superclass.
So, let's say we build such a class that holds all the IDs and keeps the incrementing counter:
class AutoIncrementId {
static private var inc: Int = 0
static private var ids: [ObjectIdentifier: String] = [:]
static func getId(_ objectId: ObjectIdentifier, prefix: String) -> String {
if let id = ids[objectId] { return id }
else {
inc += 1
let id = "\(prefix)\(inc)"
ids[objectId] = id
return id
}
}
}
Then the protocol requirement could be:
protocol AutoIdentifiable {
static var prefix: String { get }
var id: String { get }
}
So, a class would need to define its prefix. But we could define a default implementation for id:
extension AutoIdentifiable where Self: AnyObject {
var id: String {
AutoIncrementId.getId(ObjectIdentifier(self), prefix: Self.prefix)
}
}
The usage would be:
class Employee: AutoIdentifiable {
static let prefix = "E-"
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id) // E-1
print(e2.id) // E-2
print(e3.id) // E-3
print(e1.id) // E-1
I like to separate soms function and var from a enum and thought this was a way. (Just sample code)
This results in a compile error "Type 'Self' has no member 'allCases'"
public protocol EnumFunctions: Hashable {
static var numOfCases: Self { get }
}
public extension EnumFunctions {
static var numOfCases: Self {
return self.allCases.count
}
}
my Enum Cooking Timer
public struct Cook_Time {
// struct naming for the dump command like
// dump(Cook_Time(), name: Cook_Time().structname)
let structname : String = "Cook_Time"
let a = "a"
let allValues = PFTime.allValues
public enum PFTime : String , CaseIterable, EnumFunctions {
case t1m = "1mim"
case t3m = "3min"
case t5m = "5min"
case t10m = "10min"
....
static let allValues = PFTimer.allCases.map { $0.rawValue }
}
}
How can I fix this? what is my false mind setting about this? I do need Self instead of self, rigth?
Also if I make an extension for PFTime, in a separated file, why does I get the error "Cannot find type 'PFTime' in scope"?
In order to access allCases property of CaseIterable protocol, you need to change your EnumFunctions protocol to something like this:
public protocol EnumFunctions: Hashable, CaseIterable {
static var numOfCases: Int { get }
}
public extension EnumFunctions {
static var numOfCases: Int {
return allCases.count
}
}
Also you can create an extension to PFTime just by adding Cook_Time. as prefix because PFTime is placed inside Cook_Time struct:
extension Cook_Time.PFTime {
}
I have a Codable class:
class Task: Codable {
var name: String
}
When I try to instantiate it:
let newTask = Task()
allTasks.append(newTask)
It gives me error:
Missing argument for parameter 'from' in call
All I want is to insert a new object (newTask) into an array. What is the simplest way to do this?
You can inherit the initializer from NSObject:
class Task: NSObject, Codable {
var name: String = ""
}
let newTask = Task()
If you don't want to inherit NSObject, then just create your own initializer:
class Task: Codable {
var name: String?
init() {
}
}
If you don't want to make name optional (or set it to a default), it has to be initialized in init() such as:
class Task: Codable {
var name: String
init(withName name: String) {
self.name = name
}
}
let newTask = Task(withName: "ikevin8me")
Yet another solution is to use struct
struct Task: Codable {
var name: String
}
let task = Task(name: "myname")
Your Task class doesn't provide its own initializer so it gets the one defined in the Codable protocol (which it gets from the Decodable protocol).
Either add your own explicit init that takes a name parameter or change your class to a struct. Either way, you need to create a Task by passing in a name value so the name property can be initialized.
None of this addresses the fact that the code you posted makes no use of Codable so maybe there is no need for your class (or struct) to conform to Codable.
The Task class doesn't provide any initializer to initialize an object that's why it's taking initializer from Codable protocol, Provide your own initializer to initialize an object.
Usage:
1.With Class
class Task: Codable {
var name: String
init(_ name : String) {
self.name = name
}
}
var task = Task("Jarvis")
2.With struct:
struct Task: Codable {
var name: String
}
let task = Task(name: "jarvis")
I would not assign a default value to the property but implement the expected init method and a convenience variant that didn't take any arguments or alternatively have a factory method that creates an "empty" Task
Here is the code with both options
class Task: Codable {
var name: String
init(_ name: String) {
self.name = name
}
convenience init() {
self.init("")
}
static func emptyTask() -> Task {
return Task("")
}
}
You could instantiate a object from json (for example when you use an API) like this:
struct Person: Codable {
var firstName: String
var lastName: String
var age: Int
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case age = "age"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
firstName = try container.decode(String.self, forKey: .firstName)
lastName = try container.decode(String.self, forKey: .lastName)
age = try container.decode(Int.self, forKey: .age)
}
init(data: Data) {
let decoder = JSONDecoder()
do {
let userDecoded = try decoder.decode(Person.self, from: data)
self = userDecoded
}
catch {
age = 0
firstName = ""
lastName = ""
}
}
func fullName() -> String {
return "\(self.firstName) \(self.lastName)"
}
}