Skip to content

Commit d92432c

Browse files
authored
Merge pull request #1 from masture/main
NodeZooSDK (v1.0.1)
2 parents 7172260 + 09bbead commit d92432c

11 files changed

+411
-2
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,4 @@ fastlane/test_output
8888
# https://github.com/johnno1962/injectionforxcode
8989

9090
iOSInjectionProject/
91+
contents.xcworkspacedata

Package.swift

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// swift-tools-version: 5.7
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "NodeZooSDK",
8+
platforms: [.iOS(.v14)],
9+
products: [
10+
// Products define the executables and libraries a package produces, and make them visible to other packages.
11+
.library(
12+
name: "NodeZooSDK",
13+
targets: ["NodeZooSDK"]),
14+
],
15+
dependencies: [
16+
// Dependencies declare other packages that this package depends on.
17+
// .package(url: /* package url */, from: "1.0.0"),
18+
],
19+
targets: [
20+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
21+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
22+
.target(
23+
name: "NodeZooSDK",
24+
dependencies: []),
25+
.testTarget(
26+
name: "NodeZooSDKTests",
27+
dependencies: ["NodeZooSDK"]),
28+
]
29+
)

README.md

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,32 @@
1-
# ios-sdk
2-
iOS SDK for nodezoon
1+
# NodeZooSDK
2+
3+
An iOS SDK for nodezoo to search the public packaged from [https://nodezoo.com/](https://nodezoo.com/)
4+
5+
## NodeZooService Initializer with API Key
6+
7+
```swift
8+
import NodeZooService
9+
10+
let nodeZooService = NodeZooService(apiKey: "<API Key of >")
11+
```
12+
13+
This initializer creates a 'NodeZooService` instance.
14+
15+
### Parameters
16+
17+
- `apiKey`: API Key to search packages from [nodezoo.com](nodezoo.com)
18+
19+
### Usage
20+
21+
Search the public package as follows:
22+
```swift
23+
let dataTask = nodeZooService.search("express") { result in
24+
switch result {
25+
case .success(let packages):
26+
// packages is the array of the found packages
27+
case .failure(let error):
28+
// handle the error here.
29+
}
30+
```
31+
The *dataTask* can be used to cancel the search.
32+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// APIService.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
protocol APIServicingClosure {
11+
func search(request: URLRequest, completion: @escaping (Result<[NZPackage], Error>)->Void) -> URLSessionDataTask?
12+
}
13+
14+
struct ClosureBasedAPIService: APIServicingClosure {
15+
16+
var urlSession = URLSession.shared
17+
18+
19+
func search(request: URLRequest, completion: @escaping (Result<[NZPackage], Error>) -> Void) -> URLSessionDataTask? {
20+
21+
let dataTask = urlSession.dataTask(with: request) {data, response, error in
22+
print("Received response")
23+
guard let httpResponse = response as? HTTPURLResponse else {
24+
completion(.failure(NodeZooServiceError.invalidResponse))
25+
return
26+
}
27+
28+
// --- Debugging purpose only ---
29+
#if DEBUG
30+
print("Status code: \(httpResponse.statusCode)")
31+
if let data = data {
32+
print("Data: \(String(describing: String(data: data, encoding: .utf8)))")
33+
}
34+
#endif
35+
36+
guard httpResponse.statusCode == 200 else {
37+
completion(.failure(NodeZooServiceError.serverError))
38+
return
39+
}
40+
41+
guard let data = data else {
42+
completion(.failure(NodeZooServiceError.noData))
43+
return
44+
}
45+
46+
do {
47+
let searchResponse = try JSONDecoder().decode(SearchResponse.self, from: data)
48+
if searchResponse.ok {
49+
completion(.success(searchResponse.data?.packages ?? []))
50+
} else {
51+
completion(.failure(NodeZooServiceError.nonOkResponse))
52+
}
53+
} catch {
54+
completion(.failure(error))
55+
}
56+
}
57+
dataTask.resume()
58+
return dataTask
59+
}
60+
61+
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// SearchRequest.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - SearchRequest
11+
internal struct SearchRequest: Encodable {
12+
let role = "web", scope = "public", search = "pkgs"
13+
let prefix: String
14+
15+
var body: Data? {
16+
var encodedData: Data? = nil
17+
do {
18+
encodedData = try JSONEncoder().encode(self)
19+
} catch {
20+
print("Error encoding: \(error)")
21+
}
22+
return encodedData
23+
}
24+
25+
}
26+
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// SearchResponse.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - SearchResponse
11+
internal struct SearchResponse: Codable {
12+
let ok: Bool
13+
let why: String?
14+
let details: Details?
15+
let data: DataClass?
16+
let meta: Meta
17+
18+
enum CodingKeys: String, CodingKey {
19+
case ok, data, why, details
20+
case meta = "meta$"
21+
}
22+
}
23+
24+
internal struct Details: Codable {
25+
let path: [String]
26+
let whyExactly: String
27+
28+
enum CodingKeys: String, CodingKey {
29+
case path
30+
case whyExactly = "why_exactly"
31+
}
32+
}
33+
34+
// MARK: - DataClass
35+
internal struct DataClass: Codable {
36+
let packages: [NZPackage]
37+
enum CodingKeys: String, CodingKey {
38+
case packages = "pkgs"
39+
}
40+
}
41+
42+
// MARK: - Meta
43+
internal struct Meta: Codable {
44+
let id: String
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// NetworkManager.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
struct NetworkManager {
11+
12+
let apiKey: String
13+
let apiService: APIServicingClosure
14+
15+
init(apiKey: String, apiService: APIServicingClosure = ClosureBasedAPIService()) {
16+
self.apiKey = apiKey
17+
self.apiService = apiService
18+
}
19+
20+
@discardableResult
21+
func search(package: String, completion: @escaping (Result<[NZPackage], Error>)->Void) -> URLSessionDataTask? {
22+
print("Searching for: \(package)")
23+
guard var request = createRequest() else {
24+
print("Invalid Request")
25+
completion(.failure(NodeZooServiceError.invalidRequest))
26+
return nil
27+
}
28+
let searchRequest = SearchRequest(prefix: package)
29+
print("Search request: \(searchRequest)")
30+
31+
request.httpBody = searchRequest.body
32+
33+
return apiService.search(request: request, completion: completion)
34+
35+
}
36+
37+
func createRequest(urlString: String = "https://api.nodezoo.com/api/public") -> URLRequest? {
38+
guard let url = URL(string: urlString) else {
39+
print("Invalid URL")
40+
return nil
41+
}
42+
43+
var request = URLRequest(url: url)
44+
request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
45+
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
46+
request.httpMethod = "POST"
47+
return request
48+
}
49+
50+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// NZPackage.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
11+
/// This struct contains the details of the NodeJS packages
12+
public struct NZPackage: Codable {
13+
/// Name of the package
14+
public let name: String
15+
/// version of the package
16+
public let version: String
17+
/// Description of the package
18+
public let desc: String
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// NodeZooService.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
/// Use NodeZooService to fetch the public search function of [nodezoo.com](nodezoo.com)
11+
///
12+
/// While instantiating the ``NodeZooService`` instance pass the API Key as follows:
13+
/// ```
14+
///import NodeZooService
15+
///
16+
///let nodeZooService = NodeZooService(apiKey: "<API Key of >")
17+
/// ```
18+
///
19+
/// And then search the public package as follows:
20+
///
21+
/// ```
22+
/// let dataTask = nodeZooService.search("express") { result in
23+
/// switch result {
24+
/// case .success(let packages):
25+
/// // packages is the array of the found packages
26+
/// case .failure(let error):
27+
/// // handle the error here.
28+
/// }
29+
/// ```
30+
/// The *dataTask* can be used to cancel the search.
31+
///
32+
public class NodeZooService {
33+
34+
private var networkManager: NetworkManager
35+
36+
/// Initializer of NodeZooService
37+
/// - Parameter apiKey: API Key to search packages from [nodezoo.com](nodezoo.com)
38+
public init(apiKey: String) {
39+
networkManager = NetworkManager(apiKey: apiKey)
40+
}
41+
42+
/// Search the packages
43+
/// - Parameters:
44+
/// - query: The query string to search for.
45+
/// - completion: The completion will be invoked with either success or failure.
46+
/// - Returns: Discardable data task. This can be used to cancel the search.
47+
///
48+
@discardableResult
49+
public func search(query: String, completion: @escaping (Result<[NZPackage], Error>)->Void) -> URLSessionDataTask? {
50+
return networkManager.search(package: query, completion: completion)
51+
}
52+
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// NodeZooServiceError.swift
3+
//
4+
//
5+
// Created by Pankaj Kulkarni on 09/05/23.
6+
//
7+
8+
import Foundation
9+
10+
/// The error returned by the ``NodeZooService/search(query:completion:)`` function
11+
///
12+
/// The ``NodeZooService`` will return one of these errors or Error when unable to decode the response.
13+
public enum NodeZooServiceError: Error {
14+
/// Invalid request - Should never happen – report to the package creator.
15+
case invalidRequest
16+
/// The HTTP Response is invalid
17+
case invalidResponse
18+
/// No data received
19+
case noData
20+
/// Non-success (200) response
21+
case serverError
22+
/// Success by not OK response
23+
case nonOkResponse
24+
}

0 commit comments

Comments
 (0)