2015-07-08 3 views
17

Questa è una domanda in 2 parti, la prima è simile a questa domanda: Proper usage of the Alamofire's URLRequestConvertible. Ma ho bisogno di un piccolo aiuto in più!Modello Singleton e uso corretto di URLRequestConvertible di Alamofire

1) Creo un router enum che implementa URLRequestConvertible per ciascun modello nel mio livello di modello?

La pagina alamofire github fornisce un esempio di un router che ho copiato qui:

enum Router: URLRequestConvertible { 
    static let baseURLString = "http://example.com" 
    static var OAuthToken: String? 

    case CreateUser([String: AnyObject]) 
    case ReadUser(String) 
    case UpdateUser(String, [String: AnyObject]) 
    case DestroyUser(String) 

    var method: Alamofire.Method { 
     switch self { 
     case .CreateUser: 
      return .POST 
     case .ReadUser: 
      return .GET 
     case .UpdateUser: 
      return .PUT 
     case .DestroyUser: 
      return .DELETE 
     } 
    } 

    var path: String { 
     switch self { 
     case .CreateUser: 
      return "/users" 
     case .ReadUser(let username): 
      return "https://stackoverflow.com/users/\(username)" 
     case .UpdateUser(let username, _): 
      return "https://stackoverflow.com/users/\(username)" 
     case .DestroyUser(let username): 
      return "https://stackoverflow.com/users/\(username)" 
     } 
    } 

    // MARK: URLRequestConvertible 

    var URLRequest: NSURLRequest { 
     let URL = NSURL(string: Router.baseURLString)! 
     let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path)) 
     mutableURLRequest.HTTPMethod = method.rawValue 

     if let token = Router.OAuthToken { 
      mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") 
     } 

     switch self { 
     case .CreateUser(let parameters): 
      return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0 
     case .UpdateUser(_, let parameters): 
      return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0 
     default: 
      return mutableURLRequest 
     } 
    } 
} 

Quando guardo questo (io sono nuovo a una rapida quindi per favore abbiate pazienza con me> _ <) I vedere le operazioni su un oggetto utente; stanno creando un utente, aggiornando un utente, ecc ... Quindi, se avessi oggetti modello, azienda, posizione nel mio livello di modello, dovrei creare un router per ogni oggetto del modello?

2) Quando si interagisce pesantemente con un'API, sono abituato a creare un singleton "network manager" per astrarre il livello di rete e contenere intestazioni e baseur per tale API. L'alamofire ha un "Gestore" qui descritta:

di livello superiore metodi di convenienza come Alamofire.request utilizza un'istanza condivisa di Alamofire.Manager, che è configurato con il default NSURLSessionConfiguration. Come tale, le due istruzioni seguenti sono equivalenti:

Alamofire.request(.GET, "http://httpbin.org/get") 

let manager = Alamofire.Manager.sharedInstance 
manager.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/get"))) 

è questo manager che cosa dovrei usare il mio Singleton? Se è così, come faccio a impostare la baseurl sul gestore? Inoltre, se utilizzo questo gestore, questo può/può funzionare insieme al costrutto del router mostrato sopra (con ogni impostazione dell'oggetto modello è baseurl e NSURLRquest)? Se è così puoi fornire un semplice esempio?

Sono nuovo nella libreria Alamofire e veloce. Quindi, so che ci sono molti buchi nella mia comprensione, ma sto solo cercando di capire il meglio che posso! Qualsiasi informazione aiuta. Grazie.

risposta

32

Queste sono alcune ottime domande. Lasciatemi tentare di rispondere a ciascuno a turno.

Devo creare un router enum che implementa URLRequestConvertible per ogni modello nel mio livello di modello?

Questa è una grande domanda e sfortunatamente non esiste una risposta perfetta. Esistono alcuni modi per estendere il modello Router in modo da adattarsi a più tipi di oggetto. La prima opzione sarebbe quella di aggiungere più casi per supportare un altro tipo di oggetto. Tuttavia, questo diventa peloso abbastanza rapidamente quando si ottengono più di 6 o 7 casi. Le tue istruzioni sugli switch iniziano appena a diventare fuori controllo. Pertanto, non consiglierei questo approccio.

Un altro modo per affrontare il problema è introdurre i farmaci generici nello Router.

RouterObject protocollo

protocol RouterObject { 
    func createObjectPath() -> String 
    func readObjectPath(identifier: String) -> String 
    func updateObjectPath(identifier: String) -> String 
    func destroyObjectPath(identifier: String) -> String 
} 

Modello Oggetti

struct User: RouterObject { 
    let rootPath = "/users" 

    func createObjectPath() -> String { return rootPath } 
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
} 

struct Company: RouterObject { 
    let rootPath = "/companies" 

    func createObjectPath() -> String { return rootPath } 
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
} 

struct Location: RouterObject { 
    let rootPath = "/locations" 

    func createObjectPath() -> String { return rootPath } 
    func readObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func updateObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
    func destroyObjectPath(identifier: String) -> String { return "\(rootPath)/\(identifier)" } 
} 

Router

let baseURLString = "http://example.com" 
var OAuthToken: String? 

enum Router<T where T: RouterObject>: URLRequestConvertible { 
    case CreateObject(T, [String: AnyObject]) 
    case ReadObject(T, String) 
    case UpdateObject(T, String, [String: AnyObject]) 
    case DestroyObject(T, String) 

    var method: Alamofire.Method { 
     switch self { 
     case .CreateObject: 
      return .POST 
     case .ReadObject: 
      return .GET 
     case .UpdateObject: 
      return .PUT 
     case .DestroyObject: 
      return .DELETE 
     } 
    } 

    var path: String { 
     switch self { 
     case .CreateObject(let object, _): 
      return object.createObjectPath() 
     case .ReadObject(let object, let identifier): 
      return object.readObjectPath(identifier) 
     case .UpdateObject(let object, let identifier, _): 
      return object.updateObjectPath(identifier) 
     case .DestroyObject(let object, let identifier): 
      return object.destroyObjectPath(identifier) 
     } 
    } 

    // MARK: URLRequestConvertible 

    var URLRequest: NSMutableURLRequest { 
     let URL = NSURL(string: baseURLString)! 
     let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path)) 
     mutableURLRequest.HTTPMethod = method.rawValue 

     if let token = OAuthToken { 
      mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") 
     } 

     switch self { 
     case .CreateObject(_, let parameters): 
      return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0 
     case .UpdateObject(_, _, let parameters): 
      return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0 
     default: 
      return mutableURLRequest 
     } 
    } 
} 

Esempio di utilizzo

func exampleUsage() { 
    let URLRequest = Router.CreateObject(Location(), ["address": "1234 Road of Awesomeness"]).URLRequest 
    Alamofire.request(URLRequest) 
     .response { request, response, data, error in 
      print(request) 
      print(response) 
      print(data) 
      print(error) 
    } 
} 

Ora ci sono certamente alcuni compromessi che si devono fare qui. Prima di tutto, gli oggetti del modello devono essere conformi al protocollo RouterObject. Altrimenti il ​​Router non ha idea di cosa usare per il percorso. Inoltre, è necessario assicurarsi che tutti i percorsi possano essere creati con un singolo identifier. Se non possono, questo disegno potrebbe non funzionare. L'ultimo problema è che non è possibile memorizzare baseURL o OAuthToken direttamente all'interno dell'enum Router. Sfortunatamente, le proprietà statiche e memorizzate non sono ancora supportate nelle enumerazioni generiche.

Indipendentemente da ciò, questo sarebbe sicuramente un modo valido per evitare di dover creare un Router per ogni oggetto del modello.

Il Alamofire.Manager.sharedInstance deve essere utilizzato come istanza singleton NetworkManager?

Certamente potrebbe essere usato in quel modo. Dipende davvero dal tuo caso d'uso e da come hai progettato l'accesso alla tua rete. Dipende anche da quanti diversi tipi di sessioni hai bisogno. Se hai bisogno di sessioni in background e sessioni predefinite, probabilmente hai ancora bisogno del concetto di NetworkManager che contiene ciascuna istanza personalizzata Manager. Tuttavia, se si sta solo colpendo la rete con una sessione predefinita, allora il sharedInstance sarebbe probabilmente sufficiente.

Come potrebbe la baseURL del Alamofire Singleton essere utilizzato in combinazione con il modello Router?

Buona domanda ... il codice seguente è un esempio di come potrebbe essere fatto.

estensione Alamofire Direttore

extension Manager { 
    static let baseURLString = "http://example.com" 
    static var OAuthToken: String? 
} 

router Aggiornamenti URLRequestConvertible

var URLRequest: NSMutableURLRequest { 
    let URL = NSURL(string: Alamofire.Manager.baseURLString)! 
    let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path)) 
    mutableURLRequest.HTTPMethod = method.rawValue 

    if let token = Alamofire.Manager.OAuthToken { 
     mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") 
    } 

    switch self { 
    case .CreateObject(_, let parameters): 
     return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0 
    case .UpdateObject(_, _, let parameters): 
     return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0 
    default: 
     return mutableURLRequest 
    } 
} 

Speriamo che aiuta a far luce. Buona fortuna!

+0

Hey @cnoon! Questo è molto utile, grazie! Il modo in cui questo è impostato, è possibile fare qualcosa come 'func readObjectPath (identificatore: String) -> String {return" \ (rootPath)/\ (identificatore) /? RandomParameter = true "}'.Ho tentato di farlo, ma quando trasformo l'URL in una mutevoleURLRequest, formatta il punto interrogativo in modo simile a questo: '% 3F' – Thomas

+0

Nevermind, l'ho risolto cambiandolo in modo che il percorso fosse aggiunto alla' baseURLString' all'interno del ' URL', anziché in 'mutableURLRequest' – Thomas