Infer Generic Type in Class Method with Swift - ios

Is it possible for a generic method to infer its type based on the class in which it is being executed? I use CoreData NSManagedObject models to store and retrieve local data, and have managed to make everything generic in an easy to read and usable way, except for in one place. If a user wishes to query the local database to fetch a list of objects, he would write the following line:
let posts: [Post] = Post.all()
This will properly return "all" Post objects in the database, but the syntax requires that the type be defined ([Post]) on top of calling the method from the Post class itself (Post.all()), which feels unnecessarily redundant. Is there any way to define the generic type simply by calling the all() method from the Post class? I imagine I could just create global functions for fetching data, like so:
let posts: [Post] = all()
This doesn't feel nearly as readable as it would be if the syntax was as follows:
let posts = Post.all()
The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future, regardless of if someone is working on it or just reading it for some other reason.
For more insight, here is a bit more information about the current structure:
//Model.swift - The model base class. All models extend this class.
class Model: NSManagedObject {
/**
Some other stuff here
**/
//MARK: Fetch
internal class func fetch<T: Model>(predicate: NSPredicate? = nil) -> [T]? {
do {
if let request = NSFetchRequest.FromEntityName(self.entityName) { //Get entity with the name defined in the current class
request.predicate = predicate
if let result = try self.context?.executeFetchRequest(request) as? [T] {
return result
}
}
}
catch let error as NSError {
Log.Error("\(error)")
}
return nil
}
//MARK: Fetch general
class func all<T: Model>() -> [T]? {
if let result: [T] = self.fetch() {
return result
}
Log.warning("No \(self.entityName) found")
return nil
}
}
//Post.swift - An example model class. Extends Model.swift
class Post: Model {
//some fields
}
//Example view controller
class ViewController: UIViewController {
override func viewDidLoad() {
let posts: [Post] = Post.all()
//do stuff
}
}
If anyone has an idea about then please let me know. All help is appreciated!

In the general case, the typical way for a class method to return "type of the class" even for subclasses is to use protocol extensions and the Self type. Here's an example that boils your approach down to the bare minimum to make the type checking work the way you want:
// define a protocol
protocol ModelType {}
// create a static method on the protocol that returns [Self]
extension ModelType where Self: NSManagedObject {
static func all() -> [Self]? {
return [Self]() // do your fetch here
}
}
// conform to the protocol in your class hierarchy
class Model: NSManagedObject, ModelType {}
class Post: Model {}
let posts = Post.all()
// implicit type of `posts` is `[Post]?`
Note that all() should be provided by the protocol extension, but not a requirement of the protocol. If you declare all() inside protocol ModelType, then you can't make it use dynamic dispatch, which is necessary if it's to use a dynamic type.
Also, note that in Swift 3 (and macOS 10.12 / iOS 10 / tvOS 10 / watchOS 3), Core Data itself defines some Swift API shortcuts that replace some of the ones you've defined for yourself. Note this example from What's New in Core Data:
func findAnimals() {
context.performAndWait({
let request = Animal.fetchRequest // implicitly NSFetchRequest<Animal>
do {
let searchResults = try request.execute()
// use searchResults ...
} catch {
print("Error with request: \(error)")
}
})
}
Finally, some commentary on your choice of style...
fyi I capitalize the first letter in all static/class methods just as a convention
The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future
I'm not sure that breaking from language-standard conventions (like the lowercase method names recommended in the Swift 3 API Guidelines) is very compatible with your goal of making it easy for other developers new to your codebase to read and participate.

Related

Vapor 4: creating a protocol that contains a Fluent ParentProperty results in compiler errors

I have a whole bunch of Fluent (Vapor 4) models, all which have a parent field like this:
final class Location: Model, Content {
// .. bunch of other properties
#Parent(key: FieldKeys.campaign)
var campaign: Campaign
}
Now, I want to make a protocol that can be applied to all these models, like this:
protocol HasCampaignId: Model {
var _campaign: ParentProperty<Self, Campaign> { get }
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self
}
extension HasCampaignId {
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self {
if self._campaign.id != campaignID {
throw Abort(.forbidden)
}
return self
}
}
Just a convenient little function I can apply to any instance of a model that HasCampaignId, that's the idea. Sadly this doesn't compile, I get the error Property '_campaign' must be declared internal because it matches a requirement in internal protocol 'HasCampaignId'. I can make the protocol public, but then I just get another error: Property '_campaign' must be as accessible as its enclosing type because it matches a requirement in protocol 'HasCampaignId'.
I could change the protocol like this:
protocol HasCampaignId: Model {
var campaign: Campaign { get }
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self
}
extension HasCampaignId {
func belongsToCampaign(_ campaignID: Campaign.IDValue) throws -> Self {
if self.campaign.id != campaignID {
throw Abort(.forbidden)
}
return self
}
}
But that requires me to load the campaign relation, which is usually not what I want - otherwise it crashes with a fatal error: Fatal error: Parent relation not eager loaded, use $ prefix to access: Parent<Loot, Campaign>(key: campaign_id).
The campaign property within my protocol can't have a property wrapper applied either.
So how can I have a protocol that requires a ParentProperty field? How can I solve the compiler errors?
As of Swift 5.4 and 5.5 - you can't. Property wrappers in protocols have been pitched but they're not implemented and it's impossible for enforce. There was a lot of effort to try and work around these restrictions in Fluent but it's not currently possible to do what you're trying.

Infering Generic types from one of used types generics

I have a pretty complicated structure with Generic types in my app. This works, but there is an issue, that at the end of this chain, I need to specify some types 2 times, because they need to be used as generics of some class, and one of these generic types, also need generic type. Which are always the same types as these before it. It goes like this <A, B, C<A, B>>
This makes it a little unpleasant to use. Is there some way to make it infer A and B from C
Here is sample code, with stripped functionalities:
// MARK: - Base classes that Im using, stripped from funcionalities.
// This one is a base for performing some detection. It can return any type as a result of scanning.
class DetectionPerformer<ResultType> {}
// This one adds possibility to load some model needed to perform scanning from the disk.
class LocalFileDetectionPerformer<ResultType, LocalModelType>: DetectionPerformer<ResultType> {
required init(localModelURL: URL) {}
}
// This one adds possibility to download this model and store it on the disk before loading.
class DownloadableDetectionPerformer<ResultType, LocalModelType>: LocalFileDetectionPerformer<ResultType, LocalModelType> {}
// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<ResultType, LocalModelType, LocalFileDetectionPerformerType: DownloadableDetectionPerformer<ResultType, LocalModelType>>: DetectionPerformer<ResultType> {
private let localFileDetectionPerformer: LocalFileDetectionPerformerType
init(remoteModelURL: URL) {
let localModelURL = Self.localModelURL(for: remoteModelURL)
localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
}
static func localModelURL(for url: URL) -> URL {
url.appendingPathExtension("local")
}
}
// Detector is main object in application. It takes some type of Detector as init parameter, and works on it.
class Detector<ResultType, DetectionPerformerType: DetectionPerformer<ResultType>> {
let performer: DetectionPerformerType
init(performer: DetectionPerformerType) {
self.performer = performer
}
}
// Now I can implement some specific performers, whcich will do real work. For example:
class SamplePerformer: DownloadableDetectionPerformer<Int, String> {}
// And I'm able to create Detector with any type of Performer:
let detectorA = Detector(performer: SamplePerformer(localModelURL: URL(string: "")!))
// The problem begins, when I want to wrap Performer into RemoteFileDetectionPerformer
let detectorB = Detector(performer: RemoteFileDetectionPerformer<Int, String, SamplePerformer>(remoteModelURL: URL(string: "")!))
// Here I need to specify all 3 generic types of RemoteFileDetectionPerformer, even tough two first are always the same as generic types of SamplePerformer. I can't even specify different ones, as this would create an error.
// Is there some way for RemoteFileDetectionPerformer to infer these first two generic types from LocalFileDetectionPerformerType? Maybe I need to construct these some differently?
I feel like the classes you showed in the first half of the code block should be protocols instead. That is, DetectionPerformer, LocalFileDetectionPerformer, DownloadableDetectionPerformer should all be protocols. They don't seem to have any real implementation in them, as is evident in your comment "Now I can implement some specific performers, which will do real work". If you have any implementations that you want to put in them, you can put it in an extension most of the time. Why making them protocols solves the problem? Because then we can use associated types rather than type parameters.
protocol DetectionPerformer {
associatedtype ResultType
}
// This one adds possibility to load some model needed to perform scanning from the disk.
protocol LocalFileDetectionPerformer: DetectionPerformer {
associatedtype LocalModelType
init(localModelURL: URL)
}
// This one adds possibility to download this model and store it on the disk before loading.
protocol DownloadableDetectionPerformer: LocalFileDetectionPerformer {}
// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<LocalFileDetectionPerformerType: DownloadableDetectionPerformer>: DetectionPerformer {
typealias ResultType = LocalFileDetectionPerformerType.ResultType
private let localFileDetectionPerformer: LocalFileDetectionPerformerType
init(remoteModelURL: URL) {
let localModelURL = Self.localModelURL(for: remoteModelURL)
localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
}
static func localModelURL(for url: URL) -> URL {
url.appendingPathExtension("local")
}
}
class Detector<DetectionPerformerType: DetectionPerformer> {
let performer: DetectionPerformerType
init(performer: DetectionPerformerType) {
self.performer = performer
}
}
class SamplePerformer: DownloadableDetectionPerformer {
required init(localModelURL: URL) {
}
typealias ResultType = Int
typealias LocalModelType = String
}
This allows you to do:
let detectorB = Detector(performer: RemoteFileDetectionPerformer<SamplePerformer>(remoteModelURL: URL(string: "")!))
First, I strongly agree with Sweeper. This is almost certainly a poor use of class inheritance and generics in Swift. Generally, if you think you want class inheritance, you should reach for other tools first. First, composition (could you just pass functions or bundles of functions). And then protocols. Looking at your types, it feels like you should be splitting up "thing that fetches data" from "thing that detects result in data" and composing the two.
That said, the problem is general and very legitimate, and there is a solution. Pass the type as a parameter:
init(performer: LocalFileDetectionPerformerType.Type, remoteModelURL: URL) { ... }
Then, when you call it, rather than explicitly type-specifying it, pass the type:
let detectorB =
Detector(performer: RemoteFileDetectionPerformer(performer: SamplePerformer.self,
remoteModelURL: URL(string: "https://example.com")!))
The type will automatically be worked out:
Detector<Int, RemoteFileDetectionPerformer<Int, String, SamplePerformer>>
If there are cases where the type would be known from context by other means, you can add a default parameter:
init(performer: LocalFileDetectionPerformerType.Type = LocalFileDetectionPerformerType.self, ...
Then you can leave off the parameter when it's not necessary.

Passing generic Class as argument to function in swift

Below is my method in which there is fetch I make on a Managed object Class Appointment. I need to use same function for other similar managed object Classes. How do I pass different "Class" as parameter every time as I need. And also use it to fetch as I have currently "Appointment" Class. I might need to use Generics may be. Dont know how though.
func getAppointmentArray(aPredicate : String , aModel : Any) -> [Any]
{
var apptArr = [Any]()
let fetchRequest = NSFetchRequest<Appointment>(entityName: "Appointment")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format: aPredicate)
do{
let records = try managedObjectContext.fetch(fetchRequest)
if let records = records as? [NSManagedObject]{
if !records.isEmpty{
print("coreData apptmnt result : \(records)")
var appointment : Appointment?
for obj in records
{
}
}else{
print("No records found")
apptArr = []
}
}
}catch{
print("Error")
apptArr = []
}
return apptArr
}
The good folks at Objc.io provide a really good approach for this. First declare a protocol which inherits 'NSFetchRequestResult' protocol as below.
protocol Managed: class, NSFetchRequestResult {
static var entityName: String { get }
}
Now we can provide a very convenient protocol extension for our protocol 'Managed'.
We do the check 'Self: NSManagedObject' as we want the static method entity() of the NSManagedObject class to get the 'NSEntityDescription' describing the entity associated with our class. Particularly this way we get the entity name dynamically(and conveniently too) for all our ManagedObjects that conform to our protocol.
extension Managed where Self: NSManagedObject {
static var entityName: String { return entity().name! }
}
We now improve the protocol extension by providing a method which conveniently creates a fetch request and then calls a configuration block which might be used to configure the created fetch request by whoever calls it. At the end of this method we do a fetch using the created request.
extension Managed where Self: NSManagedObject {
static var entityName: String { return entity().name! }
//Continued
static func fetch(in context: NSManagedObjectContext, configurationBlock: (NSFetchRequest<Self>) -> ()) -> [Self] {
let request = NSFetchRequest<Self>(entityName: Self.entityName)
configurationBlock(request)
return try! context.fetch(request)
}
}
As you can see we do the following things here:
We make good use of protocols and protocol extensions for making our life easy.
We get the entity name without needing to write a method for each concrete managed object class that we might create. This is reusable for every managed object class that will conform to 'Managed'
The fetch method that we wrote makes use of the dynamic and convenient entityName.
The fetch method again makes use of Self which is implementation independent here. This way we make FetchRequests which are generic in itself.
We provide a convenient way to configure the request to whoever calls this method.
And at atlast we return result after fetching which is also dynamic [Self]
To see our protocol in action we can do this for your case:
class Appointment: NSManagedObject, Managed{
//properties for attributes
//etc...
//Will I get free implementation for entity name and a fetch method
//without writing extra code ?
//Yes why not
}
Testing our hard earned knowledge:
let aPredicate = "......
let context: NSManagedObjectContext.....
let appointments = Appointment.fetch(in: context) { fetchRequest in
//Configuration code like adding predicates and sort descriptors
fetchRequest.predicate = NSPredicate(format: aPredicate)
}
You can use the same pattern for any other ManagedObjects if they conform to the protocol. Eg a Doctor ManagedObject subclass conforming to our Managed protocol:
let aPredicate = "......
let context: NSManagedObjectContext.....
let doctors = Doctor.fetch(in: context) { fetchRequest in
//Configuration code like adding predicates and sort descriptors
fetchRequest.predicate = NSPredicate(format: aPredicate)
}
for the generic you can do something like this:
class FetchingDataHandler<T>{
func getAppointmentArray<T>(forClass : T, aPredicate : String , aModel : Any) -> [Any]
{
}
}

Where to put reusable functions in IOS Swift?

New to IOS programming but just wondering where is the best place to put functions that I would use throughout my code. For example, I want to write a few functions to perform a POST request to a web service and return a dictionary. Maybe another function to do some calculations. Is it best to create another .swift file and put all my functions there. And what would be a good name to give the file if so?
public func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
The best way is to create a helper class with static functions, like this:
class Helper{
static func postRequest() -> [String:String] {
// do a post request and return post data
return ["someData" : "someData"]
}
}
Now every time you need to use postRequest you can just use like so: Helper.postRequest()
I usually create a separate class if I have functions that will be used by multiple classes, especially for the ones involving network operations.
If you just have separate functions that will be used, you can simply create static functions inside that class so it is easily accessible by other classes in a static way:
class DataController {
static func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.getData() // example
However, what often has been the case for me (especially if it involves more complicated operations) was that these network operations needed to establish an initial connection beforehand or required some initial setups, and they also performed asynchronous operations that needed to be controlled. If this is the case and you will often be calling such methods, you might want to create a singleton object that you could use throughout different classes and functions. This way, you could do the initial setup or establish an initial connection just once, and then do the rest as needed with the other functions, instead of doing them every time the function gets called.
Creating a singleton object is pretty simple in Swift:
class DataController {
static let sharedInstance = DataController() // singleton object
init() {
// do initial setup or establish an initial connection
}
func getData() -> [String:String] {
// do some operations
return ["someData" : "someData"]
}
}
let data = DataController.sharedInstance.getData() // example
For the name of the class, I usually name it something like DataController or DataHelper, but anything that makes sense as a "helper" class would work.
Hope this helps :)
For reusable functions it depends what I decide to use. For this specific case I use a separate file, because posting to a backend will become more complicated when the application evolves. In my app I use a backend class, with all kinds of helper classes:
struct BackendError {
var message : String
}
struct SuccessCall {
var json : JSON
var containsError : Bool {
if let error = json["error"].string {
return true
}
else {
return false
}
}
}
typealias FailureBlock = (BackendError) -> Void
typealias SuccessBlock = (SuccessCall) -> Void
typealias AlamoFireRequest = (path: String, method: Alamofire.Method, data: [String:String]) -> Request
typealias GetFunction = (path: String , data: [String : String], failureBlock: FailureBlock, successBlock: SuccessBlock) -> Void
class Backend {
func getRequestToBackend (token: String )(path: String , data: [String : String], failureBlock: FailureBlock, successBlock:
}
For other cases I often use extensions on Swift classes. Like for getting a random element from an Array.
extension Array {
func sampleItem() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
This very old question but I would like to chirp some more points.
There are a few option, basically you can write your utility functions in Swift -
A class with static function. For example
class CommonUtility {
static func someTask() {
}
}
// uses
CommonUtility.someTask()
Also, you can have class method's as well instead of static method but those functions can be overridden by subclasses unlike static functions.
class CommonUtility {
class func someTask() {
}
}
// uses
CommonUtility.someTask()
Secondly, you can have Global functions as well, that are not part of any class and can be access anywhere from your app just by name.
func someTask() {
}
Though, selecting one over other is very subjective and I thing this is ok to make a class with static function in this particular case, where you need to achieve networking functionality but if you have some functions which perform only one task than Global function is a way to go because Global functions are more modular and separate out single tasks for a single function.
In case of static functions, if we access one of the static member, entire class gets loaded in memory. But in case of global function, only that particular function will be loaded in mem
You can create a separate swift class, might name it WebServicesManager.swift, and write all methods related to web requests in it.
You can use class methods, or singleton pattern to access the methods.

How to reference a class that follows a protocol in return type of function?

I have a protocol called Social Service, declared as follows:
protocol SocialService: class {
class func testFunc()
}
A class that follows the protocol may look like this:
class Twitter: SocialService {
class func testFunc() {
}
}
I want to have a method which returns a class that follows this protocol, so calling it would look like this:
let socialService = socialServiceForServiceType(serviceType: String)
I'm not sure what I need to put as the return value type of this function. For example, this:
func socialServiceForServiceType(serviceType: String) -> SocialService.Type
doesn't give an error right here, but trying to call it as above, gives an error:
Accessing members of protocol type value 'SocialService.Type' is
unimplemented
EDIT: I don't want an instance of that type, I want a class of that type. So I want a Twitter class, so I can call the class methods from the SocialService protocol on it.
Like the error says, this feature is unimplemented. However...
I don't want an instance of that type, I want a class of that type. So I want a Twitter class, so I can call the class methods from the SocialService protocol on it.
I'm not sure what you think you're getting from avoiding instances like this. Bear in mind classes don’t need to have member variables, and without them are essentially just collection of function pointers – which is what you seem to be looking for.
If you implement a Twitter class that has no properties and that conforms to a protocol, then calling methods on that protocol will dynamically dispatch to the implementations of that instance:
protocol SocialService: class {
func testFunc()
}
class Twitter: SocialService {
func testFunc() {
println("Testing Twitter!")
}
}
func socialServiceForServiceType(serviceType: String) -> SocialService {
return Twitter()
}
let service = socialServiceForServiceType("blah")
// prints "Testing Twitter!"
service.testFunc()
If your concern is that you want to put member variables in the Twitter class, but don’t want the overhead of that for some features, then this probably suggests you want to decompose this functionality into two different classes. Alternatively, if you want a singleton instance (to handle the connectivity for example) then there are other patterns to handle this.
Use simply
func socialServiceForServiceType(serviceType: String) -> SocialService
A protocol can be the return type of a function.
Totally agree with Airspeed Velocity, but I'd like to expand on one of his points:
I'm not sure what you think you're getting from avoiding instances like this. Bear in mind classes don’t need to have member variables, and without them are essentially just collection of function pointers – which is what you seem to be looking for.
I assume you're trying to do something like this:
func socialServiceForServiceType(serviceType: String) -> SocialService.Type
...
let cls = socialServiceForServiceType("twitter")
let conn = cls.connect(user)
Or something like that. You don't need classes to achieve that. You can just return functions.
typealias Connect = User -> Connection
func connectorForServiceType(serviceType: String) -> Connect {
switch serviceType {
case "twitter": return Twitter.Connect
...
}
}
let connect = connectorForServiceType("twitter")
let conn = connect(user)
If you have a whole bundle of functions that you want to package together, just use a struct.
struct ServiceHandlers {
let connect : User -> Connection
let ping : () -> Bool
let name: () -> String
}
func standardPinger(host: String) -> () -> Bool {
return { host in
// perform an ICMP ping and return Bool
}
}
func handlersForServiceType(serviceType: String) -> ServiceHandlers {
switch serviceType {
case "twitter":
return ServiceHandlers(connect: Twitter.connect,
ping: standardPinger("www.twitter.com"),
name: { "Twitter" })
...
}
}
let service = handlersForServiceType("twitter")
let conn = service.connect(user)
In some ways this is duplicative with class methods, but (a) the features you need for class methods aren't implemented, and (b) this is much more flexible. You can return any collection of functions you want; they don't have to all be class methods. It's easier to have default behaviors (which are hard in Swift when you use inheritance). It's easier to extend because you don't necessarily have to extend all the classes (see my use of standardPinger, which is some function I've made up that returns another function; it doesn't have to be a class method).
Breaking free of class/inheritance thinking and just passing around functions can be a major benefit in Swift. Sometimes a struct is better than a protocol.
Use a Factory pattern to achieve the same.
class SocialFactory : NSObject
{
class func socialServiceForServiceType(serviceType: String) -> SocialService?
{
switch serviceType
{
case "Twitter":
return Twitter();
case "Facebook":
return Facebook()
default:
return nil;
}
}
}

Resources