Image for post
Image for post

Pulling out local files and functionality for a private framework to me, makes sense.

What if I could make a testable (tested), reusable HTTPManager that I could use in all my projects?

Prerequisites:

  • Some experience of using Pods (Pod Init, Pod Install)

One extra thing you might need to know is that use of TextEdit may introduce awful smartquotes into the podspec and podfile. You should use a general text editor like Sublime text for editing these files.

Creation of the Pod

  1. Open Xcode
  2. Create a new project. But this time, make sure that it is a new Cocoa Touch Framework
Image for post
Image for post

3. Close Xcode

4. Use Terminal to traverse to the project folder. Within terminal create the podfile with ‘Pod Init’

5. Pod Install

6. Open the xcworkspace, if you like with the command line “open -a Xcode HTTPManager.xcworkspace”

Your project will have an objective-c header file, and this is fine and normal

Image for post
Image for post

7. Either write code, or create your classes and tests. I’ve created a HTTPManager class that conforms to a HTTPManagerProtocol so can be tested.

If you want to follow along you can use this HTTPManagerClass (as a gist, then followed by easy copy-pastable code):

public protocol HTTPManagerProtocol {
func get(urlString: String, completionBlock: @escapting (Result<Data, Error>) -> Void
func get(url: URL, completionBlock: @escaping (Result<Data, Error>) -> Void)
}
public class HTTTPManager {
public init(session: URLSessionProtocol) {
self.session = session
}
enum HTTPError: Error {
case invalidURL
case noInternet
case invalidResponse(Data?, URLResponse?)
}
public func get(urlString: String, completionBlock: @escaping (Result<Data,Error) -> Void) {
guard let url = URL(string: urlString) else {
completionBlock(.failure(HTTPError.invalidURL))
return
}
get(url: url) {(result) in
switch result {
case .failure(let error):
DispatchQueue.main.async { completionBlock(.failure(error))}
case .success(let data):
DispatchQueue.main.async { completionBlock(.success(data))
}
}
}
}fileprivate let session: URLSessionProtocolpublic func get(url: URL, completionBlock: @escaping (Result<Data, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard error == nil else {
completionBlock(.failure(error!))
return
}
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~httpResponse.statusCode else {
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
}
completionBlock(.success(responseData))
}
task.resume()
}
}

public protocol URLSessionprotocol {
func dataTask(with request: URLRequest, completionHandler: @escapting (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}
extension URLSession: URLSessionProtocol {}
extension HTTPManager: HTTPManagerProtocol {}

One issue is that when you run your project nothing happens! This is not entirely unexpected, as what would you expect the file to do?

8. Write tests and run them until they are green!

Writing good tests is an art-form in itself, however I’m not going to reproduce them here due to space constraints (if you need some, use Twitter @stevenpcurtis and I’ll give you a copy).

9. Create a Podspec “pod spec create HTTPManager”

I’m creating a wholly INTERNAL Pod here, so many of the fields in the podspec do not apply.

I changed the description, source location (source) to my local directory and source code to s.source_files = “Classes”, “HTTPManager/**/*.*”, also licence has to be changed (I change it to s.license = { :type => “MIT”, :file => “FILE_LICENSE” })as does homepage (I change it to s.homepage = “http://www.test.com/HTTPManager").

Doing so requires a file FILE_LICENSE within the folder containing your source code.

The podspec is rather large, so you can take a look at my example here:

https://gist.github.com/stevencurtis/b485803a500d1e4af6911b5a100eb106

That’s it! The idea is that you will always make changes using this framework, keeping all your work in one place.

Creation of a project to use the pod

1. Create a Single View App to use this pod. I will call mine UseHTTPManager

2. Traverse to the new project’s folder and “pod init” followed by “pod install”

3. In the pod file direct your project to your pod. Obviously you will need to change the path to where you have located your framework (and the name if you did not call it HTTPManager).

“‘HTTPManager’, :path => ‘/Users/stevencurtis/Documents/CommercialApps/Frameworks/HTTPManager’”

The format can be seen in this example:

https://gist.github.com/stevencurtis/541d3536520d8afe6f3030e3478af8b2

4. Fix the following error:

CANNOT BUILD MULTIPLE COMMANDS PRODUCE INFO.PLIST ERROR

The error kind of makes sense — the development

from development pods untick info.plist from HTTPManager target membership

Image for post
Image for post

5. Use the pod!

You can now import HTTPManager and use it in your new view controller. Here is my implementation (very bare-bones):

Want to get in contact? Use the link here:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store