Latest 0.0.6
Homepage https://github.com/MarioIannotta/Nomosi
License MIT
Platforms ios 9.0
Authors

Nomosi: Declarative plug and play network services for your iOS apps

Version
License
Platform

Why

Today every app is connected to some backend(s), usually that’s achieved through a network layer, generally a singleton, that has the resposability to take an input, perform a network request, parse the response and return a result.

In complex projects this approach could cause the network layer to be a massive and unmaintainable file with more than 20.000 LOC. Yes, that’s a true story.

The idea behind Nomosi is to breakdown the network layer into different services where every service represents a remote resource.

Each service is indipendent and atomic making things like module-based app development, client api versioning, working in large teams, testing and maintain the codebase a lot easier.

Features

Declarative functional syntax

The core object of Nomosi is a *Service*, declared as `Service` aka a generic class where the placeholder `Response` conforms the protocol `ServiceResponse`.

In this way instead of having a singleton that handle tons of requests, you’ll have different *services* and it’s immediatly clear what you should expect from each service.

After setting the required properties (url, method, etc..), by calling the `load()` function a new request will be performed. It is also possible to chain multiple actions like `onSuccess`, `onFailure`, `addingObserver` in a fancy functional way.

Example:
“`swift
/**
The service class: a resource “blueprint”, here it is possible to set endpoint, cache policy, log level etc…
*/
class AService: Service {

init() {
super.init()
basePath = “https://api.aBackend.com/v1/resources/1234”
cachePolicy = .inRam(timeout: 60*5)
log = .minimal
decorateRequest { [weak self] completion in
// here you can decorate the request as you wish,
// for example you can place here the token refresh logic
// it is possible to pass a ServiceError to the completion block or nil
completion(nil)
}
}

}

/**
The service response, since it conforms `Decodable`, there’s no need to implement the parse function.
*/
struct AServiceResponse: Decodable {
var aPropertyOne: String?
var aPropertyTwo: String?
}

AService()
.load()?
.onSuccess { aResponse in
// aResponse is an instance of `AServiceResponse`: Type-safe swift superpower!
}
.onFailure { error in
// handle error
}
}
“`

Type-safe by design

Leveraging Swift’s type system and latest features, with Nomosi you won’t ever need to handle JSON and mixed data content directly. You can forget about third party libraries such as Marshal and SwiftyJSON.

Easy to decorate (eg: token refresh) and/or invalidate requests

Handling tokens and requests validation could be tricky. That’s why the closure `decorateRequest` has been introduced.

The closure is called just before the network task is started and, using the completion block, it’s possible to invalidate or decorate the request that is about to be performed.

Example:

“`swift
class TokenProtectedService: Service {

init() {
super.init()
basePath = “https://api.aBackend.com/v1/resources/1234”
decorateRequest { [weak self] completion in
AuthManager.shared.retrieveToken { token in
if let token = token {
self?.headers[“Authorization”] = token
completion(nil)
} else {
completion(ServiceError(code: 100, reason: “Unable to retrieve the token”))
}
}
}
}

}
“`

Straightforward cache configuration with the layer of your choice (URLCache by default)

Cache is handled with the protocol `CacheProvider`, that makes easy to use the persistent layer of you choice such as Realm or CoreData by implementing just three methods:
“`swift
func removeAllCachedResponses(before date: Date)

func loadIfNeeded(request: URLRequest,
cachePolicy: CachePolicy,
completion: ((_ data: Data?) -> Void))

func storeIfNeeded(request: URLRequest,
response: URLResponse,
data: Data,
cachePolicy: CachePolicy) -> Bool
“`

`URLCache` already conforms `CacheProvider` so you can use cache without doing anything.

Discard invalid or redundant requests

Nomosi ensure that every performed request is valid and unique.

For exampe, if you call two time the `load()` method on the same service, only one request will be performed, you’ll receive a *reduntant request* error for the second one.

Mock support

Mock are handled with the protocol `MockProvider` defined as it follows:
“`swift
protocol MockProvider {

var isMockEnabled: Bool { get }
var mockedData: DataConvertible? { get }

}
“`

By default mock are retieved by searching for files in the bundle named `ServiceName.mock`.

Example

“`swift
// UserService.swift
class UserService: Service {

init(userID: Int) {
super.init()
basePath = “https://api.aBackend.com/v1/users/(userID)”
}

}
“`

“`swift
// User.swift
struct User {
let name: String
let surname: String
let website: String
}
“`

“`swift
// UserService.mock
{
“name”: “Mario”,
“surname”: “Iannotta”,
“website”: “http://www.marioiannotta.com”
}
“`

Develop and attach thirdy part components

Any class that conforms the protocol `ServiceObserver` can be notified when a request starts and ends; all ui components such as loader and fancy buttons are built using this protocol.

Prebaked UI Components

Installing `Nomosi/UI` you can use different prebaked components, such as:
– `NetworkActivityIndicatorHandler` to handle the network activity indicator in the status bar.
– `RemoteImageService` to load, efficiently cache and display remote images in imageviews using custom loaders and placeholders.
– `ServiceOverlayView` to handle loaders while performing requests and display any occurred errors alongside the retry button.
– `ServiceObserverButton` to perform custom animations (resize, show loader, hide content etc…) on buttons while performing requests.

For an extensive overview about how all of that works, you can take a look at the service flow chart.

Installation

Nomosi is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod 'Nomosi'

License

Nomosi is available under the MIT license. See the LICENSE file for more info.

TODOs:

  • [ ] Download requests
  • [ ] Document all the public stuff
  • [x] Upload requests
  • [x] Add a way to mock services
  • [x] Providing a generic interface for the cache so it’s possible to use any storage layer by implementing just the methods loadIfNeeded and storeIfNeeded
  • [x] UIImageView.Placeholder doesn’t seems to work fine with cell reuse
  • [x] Add status bar activity indicator
  • [x] Split pod in podspec (Core + UI)
  • [x] Provide a dictionary as body
  • [x] Http status code range validation

Latest podspec

{
    "name": "Nomosi",
    "version": "0.0.6",
    "summary": "Declarative plug and play network services for your iOS apps.",
    "description": "## Featuresnn* Declarative functional syntaxn* Type-safe by designn* Easy to decorate (eg: token refresh) and/or invalidate requestsn* Straightforward cache configuration with the layer of your choice (URLCache by default) n* Discard invalid or redundant requestsn* Mock supportn* Develop and attach thirdy part componentsn* Prebaked UI Components (by adding Nomosi/UI)",
    "swift_version": "4.2",
    "homepage": "https://github.com/MarioIannotta/Nomosi",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "MarioIannotta": "[email protected]"
    },
    "source": {
        "git": "https://github.com/MarioIannotta/Nomosi.git",
        "tag": "0.0.6"
    },
    "social_media_url": "https://twitter.com/MarioIannotta",
    "platforms": {
        "ios": "9.0"
    },
    "default_subspecs": "All",
    "subspecs": [
        {
            "name": "All",
            "dependencies": {
                "Nomosi/Core": [],
                "Nomosi/UI": []
            }
        },
        {
            "name": "Core",
            "source_files": "Nomosi/Core/**/*",
            "exclude_files": "Nomosi/UI/**/*"
        },
        {
            "name": "UI",
            "ios": {
                "source_files": "Nomosi/UI/**/*"
            },
            "dependencies": {
                "Nomosi/Core": []
            }
        }
    ]
}

Pin It on Pinterest

Share This