After developing a number of applications, we noticed that everyone's networking code was different. Every time maintenance developers had to take over a project, they had to "learn" the individual nuances of each network layer. This lead to a lot of wasted time. To solve this, our iOS Engineers decided to use the same networking setup. Thus we looked into Protocol-Oriented Networking.
- 
Alamofire - We ended up going with Alamofire instead of
URLSessionfor a few reasons. Alamofire is asynchronous by nature, has session management, reduces boilerplate code, and is very easy to use. - 
PromiseKit - We use Promises because they simplify asynchronous programming and separate successful and failed responses, allowing you to focus on each part in their own individual closures.
 
- NOTE: If you are looking for the Swift 3 version, use BuckoNetworking version 
1.1.3. 
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following command:
$ brew update
$ brew install carthageTo integrate BuckoNetworking into your Xcode project using Carthage, specify it in your Cartfile:
github "teepsllc/BuckoNetworking" ~> 2.1.0
- 
Run
carthage update --platform iOS --no-use-binariesto build the framework. - 
On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop
BuckoNetworking.frameworkfrom the Carthage/Build folder on disk. You will also need to dragAlamofire.frameworkandPromiseKit.frameworkinto your project. - 
On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script in which you specify your shell (ex:
/bin/sh), add the following contents to the script area below the shell:/usr/local/bin/carthage copy-frameworks
 - 
Add the paths to the frameworks you want to use under “Input Files”, e.g.:
$(SRCROOT)/Carthage/Build/iOS/BuckoNetworking.framework $(SRCROOT)/Carthage/Build/iOS/Alamofire.framework $(SRCROOT)/Carthage/Build/iOS/PromiseKit.frameworkThis script works around an App Store submission bug triggered by universal binaries and ensures that necessary bitcode-related files and dSYMs are copied when archiving.
 - 
Add the paths to the copied frameworks to the “Output Files”, e.g.:
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/BuckoNetworking.framework $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)Alamofire.framework $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/PromiseKit.frameworkWith output files specified alongside the input files, Xcode only needs to run the script when the input files have changed or the output files are missing. This means dirty builds will be faster when you haven't rebuilt frameworks with Carthage.
 
To use BuckoNetworking, just import the module.
import BuckoNetworkingBuckoNetworking revolves around Endpoints. There are a few ways you can use it. We use services to make all of our endpoints.
Swift 4 introduced the Codable protocol and the DecodableEndpoint in BuckoNetworking uses this to the max!
struct User: Decodable {
  var name: String
  var phoneNumber: String
  enum CodingKeys: String, CodingKey {
    case name
    case phoneNumber = "phone_number"
  }
}
struct UserService: DecodableEndpoint {
  typealias ResponseType = User
  var baseURL: String { return "https://example.com" }
  var path: String { return "/users" }
  var method: HTTPMethod { return .get }
  var body: Parameters { return Parameters() }
  var headers: HTTPHeaders { return HTTPHeaders() }
}
UserService().request().then { users in
  // Do something with users
  users.count
}.catch { error in
  if let json = error.json {
    // Use json
  } else {
    // Some other error occurred that doesn't include json
  }
}If you don't want to use Promises, BuckoNetworking also provides normal closures:
UserService().request { (user, error) in
  guard let user = user else {
    // Do Error
    return
  }
  // Do something with user
}Using an enum and DecodableEndpoint is possible, however, DecodableEndpoint will require that each case return the same type.
If you want each case to respond with a separate Codable type, you can use Endpoint and its request(responseType:, completion:) method.
enum UserService: Endpoint {
  case index
  var baseURL: String { return "https://example.com" }
  var path: String { return "/users" }
  var method: HTTPMethod { return .get }
  var body: Parameters { return Parameters() }
  var headers: HTTPHeaders { return HTTPHeaders() }
}
// Use your Endpoint
UserService.index.request(responseType: [User].self).then { users in
  // Do something with users
  users.count
}.catch { error in
  if let json = error.json {
    // Use json
  } else {
    // Some other error occurred that doesn't include json
  }
}
// Or without Promises
UserService.index.request(responseType: [User].self) { (users, error) in
  guard let users = users else {
    // Do Error
    return
  }
  // Do something with users
}If you don't want to use Codable, you can instead use the Endpoint protocol, and provide your own object mapping.
import BuckoNetworking
// Create an Endpoint
struct UserCreateService: Endpoint {
    var baseURL: String = "https://example.com/"
    var path: String = "users/"
    var method: HTTPMethod = .post
    var parameters: Parameters {
        var parameters = Parameters()
        parameters["first_name"] = "Bucko"
        return parameters
    }
    var headers: HttpHeaders = ["Authorization" : "Bearer SOME_TOKEN"]
}
// Use your Endpoint
Bucko.shared.request(UserCreateService()).then { response in
  // Response successful!
}.catch { error in
  //  Failure
}
// Or without Promises
Bucko.shared.request(UserCreateService()) { response in
  if response.result.isSuccess {
    // Response successful!
    // Convert `response.result.value!` to JSON
  } else {
    // Failure
  }
}import BuckoNetworking
// Create an Endpoint
enum UserService {
    case getUsers
    case getUser(id: Int)
    case createUser(firstName: String, lastName: String)
}
extension UserService: Endpoint {
    var baseURL: String { return "https://example.com/" }
    // Set up the paths
    var path: String {
        switch self {
        case .getUsers: return "users/"
        case .getUser(let id): return "users/\(id)/"
        case .createUser: return "users/"
        }
    }
    // Set up the methods
    var method: HTTPMethod {
        switch self {
        case .getUsers: return .get
        case .getUser: return .get
        case .createUser: return .post
        }
    }
    // Set up any headers you may have. You can also create an extension on `Endpoint` to set these globally.
    var headers: HTTPHeaders {
        return ["Authorization" : "Bearer SOME_TOKEN"]
    }
    // Lastly, we set the body. Here, the only route that requires parameters is create.
    var parameters: Parameters {
        var parameters: Parameters = Parameters()
        switch self {
        case .createUser(let firstName, let lastName):
            parameters["first_name"] = firstName
            parameters["last_name"] = lastName
        default:
            break
        }
        return parameters
    }
}
// Use your Endpoint
Bucko.shared.request(UserService.getUser(id: 1)).then { response in
  // Response successful!
}.catch { error in
  //  Failure
}
// Or without Promises
Bucko.shared.request(UserService.getUser(id: 1)) { response in
  if response.result.isSuccess {
    // Response successful!
    // Convert `response.result.value!` to JSON
  } else {
    // Failure
  }
}You can go to our Blog to read more.