I want to store the configuration of different local to remote connections (SFTP, FTP, ...) in a data structure. Therefore I have a struct Configuration
which holds the two variables from: Connection
and to: Connection
. Connection
is a protocol, which can be implemented by the different types of connections, for example SFTPConnection
. So the variables from
and to
can store any connection object, which implements the Connection
protocol.
To save Configuration
objects, the struct needs to conform to the Codable
protocol. This is difficult for example, when I would store multiple Configuration
objects in a .plist file. When loading this file I need to know for each element of which type from
and to
are, in order to encode the data. Also in other use cases in my application, I will need some way to know, of which type my Connection
objects are.
So I guessed I somehow need to store the type of from
and to
in the Configuration
object. My solution looks like this (LocalConnection
, SFTPConnection
and FTPConnections
are examples of structs, that implement Connection
):
struct Configuration {
var from: Connection
var to: Connection
private var id = UUID.init().uuidString
var fromType: ConnectionType {
if from is LocalConnection {
return .local
} else if from is SFTPConnection {
return .sftp
} else if from is FTPConnection {
return .ftp
} else {
return .local
}
}
var toType: ConnectionType {
if to is LocalConnection {
return .local
} else if to is SFTPConnection {
return .sftp
} else if to is FTPConnection {
return .ftp
} else {
return .local
}
}
}
protocol Connection: Codable {
var path: String { get set }
}
enum ConnectionType: Int, Codable {
case local
case sftp
case ftp
}
extension Configuration: Codable {
enum CodingKeys: String, CodingKey {
case from, to, id, fromType, toType
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
let fromType = try container.decode(ConnectionType.self, forKey: .fromType)
let toType = try container.decode(ConnectionType.self, forKey: .toType)
switch fromType {
case .local:
self.from = try container.decode(LocalConnection.self, forKey: .from)
case .sftp:
self.from = try container.decode(SFTPConnection.self, forKey: .from)
case .ftp:
self.from = try container.decode(FTPConnection, forKey: .from)
}
switch toType {
case .local:
self.to = try container.decode(LocalConnection.self, forKey: .to)
case .sftp:
self.to = try container.decode(SFTPConnection.self, forKey: .to)
case .ftp:
self.to = try container.decode(FTPConnection, forKey: .to)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.fromType, forKey: .fromType)
try container.encode(self.toType, forKey: .toType)
switch fromType {
case .local:
try container.encode((from as! LocalConnection), forKey: .from)
case .sftp:
try container.encode((from as! SFTPConnection), forKey: .from)
case .ftp:
try container.encode((from as! FTPConnection), forKey: .from)
}
switch toType {
case .local:
try container.encode((to as! LocalConnection), forKey: .to)
case .sftp:
try container.encode((to as! SFTPConnection), forKey: .to)
case .ftp:
try container.encode((to as! FTPConnection), forKey: .to)
}
}
}
This should work, but it isn't an elegant solution. Since there are six different places, where I need to distinguish the concrete type of Connection
, it's also not very good to maintain.
Is there a better, cleaner way to do this?