12

I created a generic class in swift and I'd like to initialise an adaptor with the type 'AdaptorType', but I received an compiling error

class Sample<ItemType, AdaptorType> {
    var items = Array<ItemType>() 
    let adaptor = AdaptorType()   //Error: 'AdaptorType' is not constructible with '()'
}

I also tried initialise it in init(), but same problem toke place

class SampleGnericClass<ItemType, AdaptorType> {
    var items = Array<ItemType>() 
    let adaptor : AdaptorType

    init() {
        adaptor = AdaptorType()      //It doesn't work, same error occors here
    }
}

What's the right way to get adaptor property initialised with the generic type AdaptorType? Thank you a lot!

EDIT: Full codes about this question

import UIKit

protocol XYManagedObject {

}

protocol XYNetworkProtocol {

    typealias ManagedObjectType : XYManagedObject

    func object(fromItemDictionary dictionary: NSDictionary?) -> ManagedObjectType?
    func objects(fromEntryDictionary dictionary: NSDictionary?) -> Array<ManagedObjectType>?
    func actionParameters(page: UInt?) -> (action: String, parameters: Dictionary<String, String>?)
    func pageSize() -> UInt? // nil for unlimited
}

class ProductAdaptor : XYNetworkProtocol {

    var filter = Dictionary<String, String>()

    typealias ManagedObjectType = XYProductObject

    func object(fromItemDictionary dictionary: NSDictionary?) -> ManagedObjectType? {
        //... Will return an object here
        return nil
    }

    func objects(fromEntryDictionary dictionary: NSDictionary?) -> Array<ManagedObjectType>? {
        //... will return an array of objects here
        return nil
    }

    func actionParameters(page: UInt?) -> (action: String, parameters: Dictionary<String, String>?) {
        var parameters = filter
        if page {
            parameters["page"] = "\(page!)"
        }

        return ("product_list", parameters)
    }

    func pageSize() -> UInt? {
        return 100
    }
}

class XYDataManager<ItemType: XYManagedObject, AdaptorType: XYNetworkProtocol> {

    var objects = Array<ItemType>()
    let adaptor = ProductAdaptor() //work around, adaptor should be any type conform to XYNetworkProtocol

    func loadPage(page: UInt?) -> (hasNextPage: Bool, items: Array<ItemType>?) {
        let fetcher = XYFetchController()

        let ap = adaptor.actionParameters(page)
        if let fetchedResult = fetcher.JSONObjectForAPI(ap.action, parameters: ap.parameters) {

            let npage = fetchedResult["npage"].integerValue
            var hasNextPage = true
            if !npage || !page {
                hasNextPage = false
            }
            else if npage <= page {
                hasNextPage = false
            }

            let objects = adaptor.objects(fromEntryDictionary: fetchedResult)
            return (hasNextPage, (objects as Array<ItemType>?))
        }
        else {
            return (false, nil)
        }
    }
}
Sensheng Xu
  • 121
  • 1
  • 5

4 Answers4

3

Not all objects can be initialized without arguments like you are trying to do. You have to take an initialized instance to the initializer of you class

You may also be able to define your own protocol that requires an empty initializer (by adding init to it) and restrict AdapterType to be of that protocol

drewag
  • 93,393
  • 28
  • 139
  • 128
  • Actually I'm already do so. Please view my full code. I'm thinking about generic may not be the best solution. I should use subclassing. – Sensheng Xu Jun 12 '14 at 19:23
  • 2
    @SenshengXu you are not already doing this. Your protocol does not define an empty init method. You should add `init()` to the protocol – drewag Jun 13 '14 at 00:26
1

You can make a protocol that declares the initializer

protocol HasEmptyInitializer {
    init()
}

Then have the generic conform to the protocol

class Sample<ItemType, AdaptorType:HasEmptyInitializer> {
    var items = Array<ItemType>() 
    let adaptor = AdaptorType()
}

Anything that might be passed as AdapterType must also conform to the protocol

extension ProductAdaptor : HasEmptyInitializer {}
drawnonward
  • 53,459
  • 16
  • 107
  • 112
0

Since you don't know the class, you don't know how to create an instance of the class. You have to take an instance in your constructor.

class SampleGenericClass<ItemType, AdaptorType> {
    var items = Array<ItemType>() 
    let adaptor : AdaptorType

    init(a: AdaptorType) {
        adaptor = a
    }
}

Fun fact: this makes the playground crash.

Kevin
  • 53,822
  • 15
  • 101
  • 132
0

You may be able to force cast if you know exactly what type your generic is in the case.

Instantiate an object of a generic type in Swift

Community
  • 1
  • 1
teradyl
  • 2,584
  • 1
  • 25
  • 34