2016-05-21 4 views
0

Ich experimentiere mit Generika in Swift und versuche, sie an ihre Grenzen zu bringen.Schreiben von API-Anfragen mit Vervollständigungsblöcken unter Verwendung von Swift-Generika

In meiner Anwendung habe ich eine sehr einfache API-Wrapper um Alamofire. Die Struktur ist in etwa so:

API -> Request -> Alamofire request 

Hier sind einige allgemeine Code, den ich in einen Spielplatz warf einige Konzepte zu testen. Hier ist, was ich bisher:

protocol SomeProtocol { 
    var cheese: String { get } 
    init() 
} 

class Something: SomeProtocol { 
    required init() { } 

    var cheese: String { 
     return "wiz" 
    } 
} 

class API { 

    class func performRequest<T: SomeProtocol>(completion: (T?, NSError) -> Void) { 

     // This code is irrelevant, just satisfying the completion param 
     let test = T() 
     let error = NSError(domain: "Pizza", code: 1, userInfo: nil) 

     completion(test, error) 
    } 

} 

func test() { 
    API.performRequest<Something> { item, error in 

    } 
} 

Aufruf der Funktion gibt den Fehler:

"Cannot explicitly specialize a generic function" 

****** ****** UPDATE

Gemäß der Antwort Im Folgenden wird der typische <> generische Typspezifizierer entfernt und stattdessen der erwartete Typ zu den Abschlussparametern hinzugefügt, um das Problem zu lösen. Nur ein kleines Beispiel:

func test() { 
    API.performRequest { (item: Something?, error) in 

    } 
} 

Zusätzlich habe ich entdeckt, dass die API-Wrapper-Klasse eine generische Klasse zu machen, löst das Problem wie folgt:

protocol SomeProtocol { 
    var pizza: String { get } 
} 

class SomeObject: SomeProtocol { 
    var pizza: String { return "pie" } 
} 

class API<T: SomeProtocol> { 
    class func performRequest(completion: (T?, NSError?) -> Void) { 

    } 
} 

func test() { 
    API<SomeObject>.performRequest { item, error in 
     // Do something with item, which has a type of SomeObject 
    } 
} 

So oder so, das Endziel erreicht wird. Wir haben eine einzige generische Methode, die eine Reihe von Aufgaben ausführt und das Objekt basierend auf dem mit jeder Verwendung übergebenen Typ über die Abschlussschließung zurückgibt.

+0

Ich glaube, der Compiler weiß nicht, welchen Typ auf die Funktion anzuwenden ist.Versuchen Sie, den Parametertyp in Ihren Schließungen anzugeben. –

+0

Würde das nicht den Zweck der Verwendung von Generika besiegen? Soweit ich weiß, leitet iOS den Typ normalerweise basierend auf Scan-Parametern ein. Es scheint, dass Vollzugsabschlüsse dies nicht angemessen unterstützen. –

Antwort

1

Die Art und Weise, wie Generika funktionieren, erlaubt es einer Funktion, unspezialisierte Variablen innerhalb ihrer Implementierung zu verwenden. Man kann diesen Variablen Funktionalität hinzufügen, indem man angibt, dass die Variablen einem bestimmten Protokoll entsprechen müssen (dies geschieht innerhalb der Deklaration). Das Ergebnis ist eine Funktion, die als Vorlage für viele Typen verwendet werden kann. Wenn die Funktion jedoch im Code selbst aufgerufen wird, muss der Compiler in der Lage sein, sich zu spezialisieren und Typen auf die Generika anzuwenden.

In Ihrem Code oben, versuchen

func test() { 
    API.performRequest<Something> { item, error in 

    } 
} 

mit

func test() { 
    API.performRequest { (item: Something?, error) in 

    } 
} 

ersetzen auf diese Weise können der Compiler wissen, welche Art es ohne explizite Angabe auf die Funktion anwenden müssen. Die Fehlermeldung, die Sie erhalten haben, sollte jetzt sinnvoller sein.

+0

Ahhhh ok, ich habe deine erste Aussage missverstanden. Ihr Codebeispiel ist sinnvoll. Klappt wunderbar! Vielen Dank! –

1

Hier ist, was ich mit Alamofire und Alamofire Object Mapper: Schritt 1: Erstellen Sie modale Klassen, die Mappable-Protokolle entspricht.

class StoreListingModal: Mappable { 
var store: [StoreModal]? 
var status: String? 
required init?(_ map: Map){ 

} 

func mapping(map: Map) { 
    store <- map["result"] 
    status <- map["status"] 
} 
} 

Schritt 2: Erstellen einer Anforderung unter Verwendung der generischen Typen holen:

func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) { 
    Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in 
     guard response.result.isSuccess else{ 
      print("Error while fetching: \(response.result.error)") 
      completion(nil, response.result.error) 
      return 
     } 
     if let responseObject = response.result.value{ 
      print(responseObject) 
      completion(responseObject, nil) 
     } 
    } 
} 

Schritt 3: Jetzt alles, was Sie brauchen, ist diese Funktion holen zu nennen. Dies kann folgendermaßen erfolgen:

self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in 

    } 

Sie erhalten nicht nur Ihr Antwortobjekt, sondern es wird auch Ihrer modalen Klasse zugeordnet.

Verwandte Themen