Making a Private Sub-Type Internal or Public - swift

Occasionally, the Swift compiler will synthesize a sub-type for you when you conform a type to a protocol. From my experience, these types are given the access level of private. This is usually fine, but I have come across a case in which I want to be able to access the private subtype in an extension held in a different file (same module, in case you are wondering) which you apparently can't do.
Is it possible to give the type a higher access level without having to write it by hand and take advantage of the synthesized code?
An example of where this happens is conformance to Codable. The CodableKeys sub-type is declared as private when it is synthesized, but you can make it any access level you want when you manually write it.
Here is an example of code that raises an error about this.
Company.swift:
class Company: Codable {
let name: String
let founded: Date
init(name: String, founded: Date) {
self.name = name
self.founded = founded
}
}
Company+JSON.swift:
extension Company {
func json() -> JSON {
let data: [String: JSON] = [
CodingKeys.name: self.name
]
return JSON.object(data)
}
}
I get:
'CodingKeys' is inaccessible due to 'private' protection level
It wouldn't be hard to write the type for a type this size, but it quickly becomes a mess when you have multiple types with 9, 10, 11+ properties.

Related

Swift: casting un-constrained generic type to generic type that confirms to Decodable

Situation
I have a two generic classes which will fetch data either from api and database, lets say APIDataSource<I, O> and DBDataSource<I, O> respectively
I will inject any of two class in view-model when creating it and view-model will use that class to fetch data it needed. I want view-model to work exactly same with both class. So I don't want different generic constraints for the classes
// sudo code
ViewModel(APIDataSource <InputModel, ResponseModel>(...))
// I want to change the datasource in future like
ViewModel(DBDataSource <InputModel, ResponseModel>(...))
To fetch data from api ResponseModel need to confirms to "Decodable" because I want to create that object from JSON. To fetch data from realm database it need to inherit from Object
Inside ViewModel I want to get response like
// sudo code
self.dataSource.request("param1", "param2")
If developer tries to fetch api data from database or vice-versa it will check for correct type and throws proper error.
Stripped out version of code for playground
Following is stripped out version of code which shows what I want to achieve or where I am stuck (casting un-constrained generic type to generic type that confirms to Decodable)
import Foundation
// Just to test functions below
class DummyModel: Decodable {
}
// Stripped out version of function which will convert json to object of type T
func decode<T:Decodable>(_ type: T.Type){
print(type)
}
// This doesn't give compilation error
// Ignore the inp
func testDecode<T:Decodable> (_ inp: T) {
decode(T.self)
}
// This gives compilation error
// Ignore the inp
func testDecode2<T>(_ inp: T){
if(T.self is Decodable){
// ??????????
// How can we cast T at runtime after checking T confirms to Decodable??
decode(T.self as! Decodable.Type)
}
}
testDecode(DummyModel())
Any help or explanation that this could not work would be appreciated. Thanks in advance :)
As #matt suggests, moving my various comments over to an answer in the form "your problem has no good solution and you need to redesign your problem."
What you're trying to do is at best fragile, and at worst impossible. Matt's approach is a good solution when you're trying to improve performance, but it breaks in surprising ways if it impacts behavior. For example:
protocol P {}
func doSomething<T>(x: T) -> String {
if x is P {
return "\(x) simple, but it's really P"
}
return "\(x) simple"
}
func doSomething<T: P>(x: T) -> String {
return "\(x) is P"
}
struct S: P {}
doSomething(x: S()) // S() is P
So that works just like we expect. But we can lose the type information this way:
func wrapper<T>(x: T) -> String {
return doSomething(x: x)
}
wrapper(x: S()) // S() simple, but it's really P!
So you can't solve this with generics.
Going back to your approach, which at least has the possibility of being robust, it's still not going to work. Swift's type system just doesn't have a way to express what you're trying to say. But I don't think you should be trying to say this anyway.
In the method that fetch data I will check type of generic type and if it confirms to "Decodable" protocol I will use it to fetch data from api else from database.
If fetching from the API vs the database represents different semantics (rather than just a performance improvement), this is very dangerous even if you could get it to work. Any part of the program can attach Decodable to any type. It can even be done in a separate module. Adding protocol conformance should never change the semantics (outwardly visible behaviors) of the program, only the performance or capabilities.
I have a generic class which will fetch data either from api or database
Perfect. If you already have a class, class inheritance makes a lot of sense here. I might build it like:
class Model {
required init(identifier: String) {}
}
class DatabaseModel {
required init(fromDatabaseWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromDatabaseWithIdentifier: identifier )}
}
class APIModel {
required init(fromAPIWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromAPIWithIdentifier: identifier )}
}
class SomeModel: DatabaseModel {
required init(fromDatabaseWithIdentifier identifier: String) {
super.init(fromDatabaseWithIdentifier: identifier)
}
}
Depending on your exact needs, you might rearrange this (and a protocol might also be workable here). But the key point is that the model knows how to fetch itself. That makes it easy to use Decodable inside the class (since it can easily use type(of: self) as the parameter).
Your needs may be different, and if you'll describe them a bit better maybe we'll come to a better solution. But it should not be based on whether something merely conforms to a protocol. In most cases that will be impossible, and if you get it working it will be fragile.
What you'd really like to do here is have two versions of testDecode, one for when T conforms to Decodable, the other for when it doesn't. You would thus overload the function testDecode so that the right one is called depending on the type of T.
Unfortunately, you can't do that, because you can't do a function overload that depends on the resolution of a generic type. But you can work around this by boxing the function inside a generic type, because you can extend the type conditionally.
Thus, just to show the architecture:
protocol P{}
struct Box<T> {
func f() {
print("it doesn't conform to P")
}
}
extension Box where T : P {
func f() {
print("it conforms to P")
}
}
struct S1:P {}
struct S2 {}
let b1 = Box<S1>()
b1.f() // "it conforms to P"
let b2 = Box<S2>()
b2.f() // "it doesn't conform to P"
This proves that the right version of f is being called, depending on whether the type that resolves the generic conforms to the protocol or not.

Swift generics init: Non-nominal type 'T' does not support explicit initialization

I'm using Realm in my iOS app and because of the thread safety issues I have opted to create a realm layer for my data model, as well as a layer that can be converted to and from the realm objects.
There is a lot more complexity to what I'm actually doing, but I was able to create a playground that demonstrates the unexpected issues I'm running into.
Basically I created a protocol called RealmObject. In my actual app this is a protocol which Realm.Object types can conform to which requires all the necessary properties.
I'm just including the UUID part of this protocol in the playground.
//the realm layer
protocol RealmObject {
var uuid: String { get }
}
Next I have my Realm layer model. They all have UUID, but have some other properties that correspond to their non-Realm relatives.
struct NoteObject: RealmObject {
let uuid: String
let noteText: String
}
struct AppointmentObject: RealmObject {
let uuid: String
let time: Date
}
Cacheable is the protocol that types which can be saved in realm must conform to.
For now I've just included the UUID requirement, and the requirement to be initialized from a RealmObject
//Cacheable
protocol Cacheable {
associatedtype T: RealmObject
init(object: T)
}
struct Note: Cacheable {
let noteText: String
let uuid: String
init(object: NoteObject) {
self.uuid = object.uuid
self.noteText = object.noteText
}
}
struct Appointment: Cacheable {
let uuid: String
let time: Date
init(object: AppointmentObject) {
self.uuid = object.uuid
self.time = object.time
}
}
Finally the issue. In my app this function does a lot more than this, but this is as simple as I could make it and still demonstrate the issue.
Basically, T is the generic type which must be a Cacheable object.
U is the RealmObject I want to convert to a cacheable object.
Every Cacheable object has an initializer that accepts an object which is a RealmObject
But it fails
func getCacheable<T: Cacheable, U: RealmObject>(from realmObject: U) -> T {
let thing = T(object: realmObject)
ERROR: Non-nominal type 'T' does not support explicit initialization
return thing
}
let noteObject = NoteObject(uuid: "bobalobla", noteText: "hi how are you")
let note: Note = getCacheable(from: noteObject)
let appointmentObject = AppointmentObject(uuid: "bobloblaw", time: Date())
let appointment: Appointment = getCacheable(from: appointmentObject)
I don't see anything that is so ambiguous the compiler shouldn't be able to easily figure it out
Replacing the generics with the types should be simple
Once the function knows which Cacheable type its working with, the initializer should be easily route to the correct init method. I won't pretend to understand whats actually happening, but this seems like a pretty basic thing to do with generics so I assume I must be doing something wrong. What is going on?
The type system doesn't know, from your list of generics <T: Cacheable, U: RealmObject>, that U is the correct type of RealmObject for T (in other words, you could be passing a Note as T, and an AppointmentObject as U). You just need to update your function signature to:
// The `where T.T == U` is the important part. Because that is defined,
// we can also remove `U: RealmObject` because it's redundant information.
func getCacheable<T: Cacheable, U>(from realmObject: U) -> T where T.T == U {
let thing = T(object: realmObject)
return thing
}
// Alternatively, you can just do:
func getCacheable<T: Cacheable>(from realmObject: T.T) -> T {
let thing = T(object: realmObject)
return thing
}

How do I combine the safety of type requirements with the safety of enums?

So I'm working on an sql database abstraction layer in swift and I want to write it as safe as possible. By that I mean I would prefer to make it impossible for anybody to do anything illegal in the database using my library.
One thing I'm not fully able to solve yet is how to make the conversion from Swift types to SQL data types 100% safe. So I'll explain my current implementation and its flaws:
So in SQL, many data types have parameters. For example, when you want to define a column as a VARCHAR, you need to give it a parameter that represents the max length of the VARCHAR, like VARCHAR(255). You could say that VARCHAR is one of SQL's primitive data-types and VARCHAR(255) is a more specified data-type. To represent that in a type-safe way in swift I created two protocols:
public protocol PrimitiveDataType {
associatedtype Options = Void
}
public protocol SpecifiedDataType {
associatedtype Primitive: PrimitiveDataType
static var options: Primitive.Options { get }
static func from(primitive: Primitive) -> Self
var primitive: Primitive { get }
}
So here's an example of a PrimitiveDataType:
extension String: PrimitiveDataType {
public enum DatabaseStringType {
case char(length: Int), varchar(limit: Int), text
}
public typealias Options = DatabaseStringType
}
And here's an example of a SpecifiedDataType:
struct Name {
let value: String
init?(_ value: String) {
guard case 0...50 = value.characters.count else {
return nil
}
self.value = value
}
}
extension Name: SpecifiedDataType {
static let options = String.DatabaseStringType.varchar(limit: 50)
static func from(primitive: String) -> Name {
return Name(primitive)!
}
var primitive: String {
return value
}
}
Now I can use the information elsewhere in the library to know what kind of database-column is expected whenever a Name type is requested. This gives my library the power to make sure the database columns are always of the correct type.
I haven't written all that code yet, but it might look something like this:
func createColumn<SDT: SpecifiedDataType>(ofType type: SDT) -> String {
switch SDT.options {
case let options as String.DatabaseStringType:
switch options {
case .text:
return "TEXT"
case .char(let length):
return "CHAR(\(length))"
case .varchar(let limit):
return "VARCHAR(\(limit))"
}
case let length as UInt32.Options:
// return something like UNSIGNED INTEGER(\(length)) or whatever
// case etcetera
}
}
There is just one small problem with this. The set of valid primitive data-types is limited, but the set of possible implementations of the PrimitiveDataType protocol is unlimited, because I can't stop anybody from creating their own implementation of this protocol.
So for this reason it would be better to use some kind of enum instead of a number of implementations of a protocol. Because nobody can extend an enum that I create, so I can keep the options limited to whatever I define as valid types. However, an enum would create another problem that doesn't exist in my current implementation.
You see in my current implementation, whenever someone implements SpecifiedDataType with their options defined like so:
static let options = String.DatabaseStringType.varchar(limit: 50)
I can trust that the compiler will force them to make their type compatible with string by implementing the from(primitive: String) method and the primitive: String (computed) variable. AFAIK, if I switch the protocol for an enum I lose this compiler-enforced type-safety.
So in summary, I want to have all of the following:
A way for a developer to declare what sql data-type they want their type to correspond with.
A way to then force that developer to actually make their type compatible with that sql data-type.
To guarantee that the developer using my library can't somehow extend the list of sql-data types, but can only use the ones that I have defined for him / her.
So far I only know how to do either the first two, but not the last one, or the last one, but not the first two. How do I get all three?

implicitly convert between two 'identical' types in swift

If I have two types
struct PersonFromLibraryA {
let name: String
let age: Int
}
struct PersonFromLibraryB {
let name: String
let age: Int
}
Is there a way to implicitly be able to pass an A into a method which is expecting a B?
func doSomething(withPerson person: PersonFromLibraryA) {
...
}
let person = PersonFromLibraryB(name: "Alice", age: 42)
doSomething(withPerson: person))
I'd expect this to be type safe i.e. if A or B diverge in any way whatsoever, this shouldn't compile.
I have about 20 or so of this situation (I'm mapping between layers of abstraction in a library) and am getting very tired of filling files with boilerplate mapping methods!
I suspect I already know the answer to this question, but I figure I'll ask it here just to be sure!
Agreed with rmaddy that the one way to fix this w/ type-saftey is protocol (without type-safety, you can do it with unsafeBitCast, but please don't do that). This raises a warning flag, though, that you may have over-abstracted something (or more likely: abstracted along the wrong axis) if you have many types that are identical but distinct between layers.
I believe you should think OOP as Swift is an OOP language. Change both into classes. Generally, you can do this by simply changing the word struct to class. Then create a new class.
class PersonFromLibrary {
let name: String
let age: Int
}
Now setup your other classes:
class PersonFromLibraryA: PersonFromLibrary {
}
class PersonFromLibraryB: PersonFromLibrary {
}
Now instead of using PersonFromLibraryA as the parameter type use PersonFromLibrary. Now you can pass all of the PersonFromLibraryA type things as PersonFromLibrary. The only restriction here is that if you add any unique qualities to any of the PersonFromLibraryA level classes you will have to cast it back to PersonFromLibraryA or it's corresponding class in order to use its unique properties.

Can I have a Swift protocol without functions

Consider this typical example, sometimes seen in tutorials, etc (even in Apple's code):
protocol Nameable {
var name: String {get}
}
struct Person : Nameable {
var name: String
}
My question is, what would be the benefit of this pattern? I can understand it once a function is added to a protocol, but what could be a good application for a protocol with just one or more variables? Why not just add name to each struct and class ?
Persons are not the only not the only things that you may want to name. Pets have names, roads have names, heck, some people name their cars.
What if we want to name each object in a collection of different objects? If we store those objects in a collection of Any, we don't have any way to guarentee that all objects have names.
This is where protocols come in. By creating a Nameable protocol, we can create a collection of Nameable objects, and be certain that all the objects within are guaranteed to have a name.
Here's an example:
protocol Nameable {
var name: String {get}
}
struct Person : Nameable {
let name: String
let age: Int
// other properties of a Person
}
struct Pet : Nameable {
let name: String
let species: String
// other properties of a Pet
}
struct Car : Nameable {
let name: String
let horsepower: Double
// other properties of a Car
}
let namableItems: [Nameable] = [
Person(name: "Steve", age: 21),
Pet(name: "Mittens", species: "Cat"),
Car(name: "My Pride and Joy", horsepower: 9000)
]
for nameableItem in namableItems {
print("\(nameableItem.name) is a \(nameableItem.dynamicType).")
}
Which prints:
Steve is a Person.
Mittens is a Pet.
My Pride and Joy is a Car.
You can try it here.
Basically you make a promise that your class / struct will contain defined properties.
For structures this is very important because they can't be subclassed.
Also, subclass can only have 1 superclass so you can't create Person who has more than one parent.
I would recommend you to read this article about why use protocols over subclassing and the main purpose of using them:
http://alisoftware.github.io/swift/protocol/2015/11/08/mixins-over-inheritance/
The key to the answer lies in the word protocol.
Protocol is a system of rules that explain the correct conduct and
procedures to be followed in formal situations.
That said, when you state that an object (struct, enum, class, ..) must conform to a protocol, you are obliged to respect it. If you do not, Xcode throws an error.
So one of the main, (absolutely not the only utility) is that you can not forget to include the attributes and / or functions in the various objects.
This could be very useful if multiple developers are working on the same code, or simply to avoid potential human error of distraction :) .
Another great advantage of the protocols is that they can be used as types.
Therefore a struct, enum, or different classes, which inherit the same protocol, they can all be traced to the same type.