In my Quiz app I initialize quizzes, and the providing class does not know the format of the questions before being provided them (although they are constrained by a QuestionProtocol):
public protocol QuestionProtocol {
init?(fields: [String] )
var description: String {get}
var question: String {get}
var solution: String {get}
var explainAnswer: String {get}
var answered: Int {get set}
var qa: String {get}
var qb: String {get}
var qc: String {get}
var qd: String {get}
}
And I can initialize the quizzes and return them easily enough through a method with the signature
public func initializeQuizzes<T: QuestionProtocol>(with type: T.Type, withCompletionHandler completion: ((Result<[Quiz<T>], Error>) -> Void)?)
However to provide these quizzes is expensive (an API call or a SQL retrieval) so I want to store these quizzes and retrieve them separately from a suitable function with signature
public func getNextQFromSet<T: QuestionProtocol>(with type: T.Type) -> (question: T, answers: [String])?
The problem I have is storing these questions which are of type T.
They are linked to a Quiz object:
public class Quiz<T> {
private let questions : [T]
private let name : String
init(name: String, questions: [T]) {
self.name = name
self.questions = questions
}
public func getQuestions() -> [T] {
return questions
}
func getName() -> String {
return name
}
}
So I'm able to store them as quizzes that conform to the QuestionProtocol
private var quizzes = [Quiz<QuestionProtocol>]()
But then I lose the extra information I want to store in the question.
I can store Any, but I believe that is bad practice
private var anyquizzes = [Quiz<Any>]()
Ideally I would like to store T i.e.
Quiz<T>
but that seems to be impossible in Swift.
Because these classes are in a pod they have no way of knowing about the internal workings of a Question, and are provided these at runtime hence the use of generics and the difficulties in storing these questions.
I can't think of a way to improve the design of the App (more specifically the Pod) - I want to initialize the quizzes once and once only and then run functions like getNextQFromSet() to retrieve a relevant question - which obviously depends on me knowing the type of the question (which I do not know before runtime).
For clarity here is a link to the Pod: https://github.com/stevencurtis/QuizManager
How can I store an array containing these questions without knowing the type?
To be short, I think it makes sense to remove QuestionProtocol and replace it with plain data structure struct Question.
Before I explain my point of view, I want to note that even though I looked at the pod, I still do not know all the requirements, so I might be wrong.
Let's try to have a look at the problem from design perspective instead of programming language perspective.
What is the reason of having QuestionProtocol? Could it be replaced with, let's say, object instead? Why do those properties should be polymorphic? Of course implementation details should be hidden, but hiding data is not about protocols or additional function layers, is about abstractions.
Let's convert QuestionProtocol to Question object for now and think about an abstraction. If there is a real abstraction, then there should an object that hides the data (details) and expose functions that manipulate that data. But there is no functions in Question object and it means that there is no real abstraction behind.
Finally, It means that Question entity most likely is a plain data structure with public properties and could be defined as struct Question.
Having this Question struct now, you can define quizzes as Quiz<Question> and use it to save and retrieve the data.
In addition, I think it worth to point out two things which could simplify and potentially improve design and implementation:
Why does SQLiteManager knows something about concrete question (depends on QuestionProtocol)? I think it makes sense to introduce some generic DBObject or at least plain dictionary [String: Any] which SQLiteManager would know how process and then insert. Then Repository could transform Question data structure into DBObject on some level of composition and pass it to SQLiteManager.
When using generics, in most cases there is no need to define additional type: T.Type parameter. Once generic is defined you can use it as [T], T.init, etc. If you still need a metatype (T.Type) you can get by T.self.
Hope this helps!
Update:
There is great example of Quiz app created with TDD and modular design: Quiz App. There is also a video series explaining design and creation process step by step.
How can I store an array containing these questions without knowing
the type?
To my knowledge you can't. As rraphael pointed out in his comment generics aren't resolved at runtime. Furthermore Arrays in swift are designed to hold a single type:
Specifically, you use the Array type to hold elements of a single type, the array’s Element type.
So whatever you do you'll have either an array of Any or maybe QuestionProtocol but nothing more dynamic than that : the type will be resolved at compilation time
You may be able to redesign your QuestionProtocol to suit your needs but without any information on the different types of question it's a bit difficult to help you more since it is an architecture matter.
You can use enum with associated values for describing types. For example:
struct QuestionTypeA { }
struct QuestionTypeB { }
struct QuestionTypeC { }
enum Question {
case typeA(question: QuestionTypeA)
case typeB(question: QuestionTypeB)
case typeC(question: QuestionTypeC)
}
And then:
public class Quiz {
private let questions : Question
private let name : String
...
And store an array of Quiz without generic
private var anyquizzes = [Quiz]()
You wouldn't be able to store a Quiz<T> and a Quiz<U> in the same array using either of those types. They're just not the same type.
If you have an Array<QuizProtocol>, you can just match against your known types in a switch-case statement:
var quizzes: [QuizProtocol] = ...
for quiz in quizzes {
switch quiz {
case let someQuiz as SomeQuiz:
...
case let someOtherQuiz as SomeOtherQuiz:
...
default:
... // couldn't cast to any known type; do some fallback logic
....
}
}
where SomeQuiz and SomeOtherQuiz conform to QuizProtocol (though strictly speaking, you could match against any type).
Related
TLDR: Using many Swift protocols in a large project is great for testing and SOLID coding, but I’m getting function clutter and invalid redeclaration clashes. What’s the best practice to avoid these problems in Swift while making heavy use of protocols?
Concretely, I want to use protocols to separate responsibilities from view classes such that they don’t need to know anything about the data models used to “decorate” them. But this is creating a lot of functions for my data model classes that are exposed throughout the app, and that are starting to clash with other protocols.
As an example, let’s say I want to set up my custom tableview cell from a certain data model in my project. Let’s call it MyDataModel. I create a decorating protocol like so:
protocol MyCellDecorator {
var headingText: String?
var descriptionText: String?
}
And then my cell is like
class MyCell: UITableViewCell {
#IBOutlet weak var headingLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
func setup(fromDecorator decorator: MyCellDecorator) {
headingLabel.text = decorator.headingText
descriptionLabel.text = decorator.descriptionText
}
}
Now all I need to do is provide an extension from my data model class implementing MyCellDecorator, providing headingText and descriptionText, and I can plug my data model object into setup(fromDecorator:).
extension MyDataClass: MyCellDecorator {
var headingText: String {
return “Some Heading“
}
var descriptionText: String {
return “Some Description“
}
}
This makes the cell easy to test; it clearly separates responsibilities, MyCell and the UIViewController driving it now need to know nothing about MyDataModel..
BUT now MyDataModel has two extra properties, headingText, and descriptionText - available everywhere. But MyDataModel already extends 10 other decorator protocols for specific UI throughout my project, and it so happens that another protocol already defines headingText, so I get the compilation error “invalid redeclaration of ‘headingText’”.
With all of this headache, I decide to quit, go ahead and just pass MyDataModel into MyCell, it all compiles but I lose all the aforementioned advantages.
What are good ways, in such a big project as this, to score those sweet sweet protocol wins, without cluttering up my class’s function tables and having redeclaration clashes between different extensions?
I agree with where Andrey is going, but I believe it's even simpler. You just need decorator types, and the way you've described them, they should be able to be simple structs, with no inherent need for protocols.
struct MyCellDecorator {
let headingText: String
let descriptionText: String
}
(I've made these non-optional, and I strongly recommend that unless you have a UI distinction between "empty string" and "none.")
Extensions work almost exactly as you've done before:
extension MyDataClass {
func makeMyCellDecorator() -> MyCellDecorator {
return MyCellDecorator(headingText: "Some Heading",
description: "Some Description")
}
}
In some cases, you may find that model objects have very consistent ways that they generate a decorator. That's a place where protocols will allow you to extract code such as:
protocol MyCellDecoratorConvertible {
var headingText: String { get }
var descriptionText: String { get }
}
extension MyCellDecoratorConvertible {
func makeMyCellDecorator() -> MyCellDecorator {
return MyCellDecorator(headingText: headingText,
description: description)
}
}
This example captures the case where the cell happens to have exactly the right names already. Then you just have to add MyCellDecoratorConvertible and the property comes for free.
The key point to all of this is that rather than have model.headingText you'll have model.makeMyCellDecorator().headingText, which will address your explosion of properties.
Note this will generate a new Decorator every time you access it, which is why I'm using a make (factory) naming convention. There are other approaches you might consider, such as an AnyMyCellDecorator type eraser (but I'd start simple; these are likely very small types and copying them is not expensive).
You can split the UI into modules and use internal extensions. Those will not appear in other modules, which will prevent myCellDecorator from showing up everywhere. If more convenient, you can put the myCellDecorator extensions in the same file with MyCell and mark them private.
Since this is a large, existing code-base, I highly recommend allowing any existing code duplication to drive your design. There is no one pattern that is ideal for all systems. It's not even necessary to have every decorator follow the exact same pattern (in some cases it may make more sense to use a protocol; in others a struct; in others you might want both). You can create a pattern "language" without boxing yourself into a world where you're creating extra protocols just because "that's the pattern."
But MyDataModel already extends 10 other decorator protocols for specific UI throughout my project, and it so happens that another protocol already defines headingText, so I get the compilation error “invalid redeclaration of ‘headingText’”.
I think this is the main pitfall here, that you use single model to provide data for different parts of the application. If we are talking about the MVC pattern, then the single model should only provide data for corresponding controller. I think in this case there will be much less protocol adoptions in the model.
On other hand you can try to split functionality inside of the model:
For instance, if we have
protocol CellDecorator {
var headingText: String?
var descriptionText: String?
init(withSomeData data: ...) {}
}
we could create something like this
class MyCellDecorator: CellDecorator {
var headingText: String?
var descriptionText: String?
}
class MyDataClass {
lazy var cellDecorator: CellDecorator = {
return CellDecorator(withSomeData: ...)
}
}
One struct-based way I've played with is this:
Instead of extending MyDataClass, I create a simple struct (which can be fileprivate to the view controller class, or not) that looks like:
struct MyDataClassCellDecorator: MyCellDecorator {
var headingText: String? {
return "Some heading with \(data.someText)"
}
var descriptionText: String? {
return data.someOtherText
}
let data: MyDataClass
}
This way MyCell can still use the protocol to decorate itself, MyDataClass doesn't need any extension at all, and in whatever access scope I want it I get a struct that does the decorating logic for MyDataClass + MyCellDecorator.
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.
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?
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.
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.