Nomosi 0.1.13

Nomosi 0.1.13

Mario Iannotta 维护。



Nomosi 0.1.13

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

Version License Platform Carthage compatible

为什么

如今每个应用程序都与一些后端连接,通常这是通过网络层实现的,通常是一个单例,负责接受输入,执行网络请求,解析响应并返回结果。

在复杂项目中,这种方法可能会导致网络层成为一个包含超过20,000行代码的庞大且难以维护的文件。是的,这是一个真实的故事。

Nomosi 的理念是将网络层分成不同的 服务,其中每个服务代表一个远程资源。

每个服务都是独立和原子的,这使得基于模块的应用程序开发、客户端 API 版本控制、大型团队协作、测试和维护代码库变得更加容易。

特点

声明式函数式语法

Nomosi 的核心对象是一个 Service,声明为 Service<Response: ServiceResponse>,即一个泛型类,其中占位符 Response 符合 ServiceResponse 协议。

这样,您就不会有一个处理大量请求的单例,而是会有不同的 服务,并且对于每个服务可以立即明白期望得到的内容。

设置必要的属性(url、method 等)后,通过调用 load() 函数执行新的请求。还可能以优雅的函数式方式链式调用多个动作,如 onSuccessonFailureaddingObserver

示例

/**
  The service class: a resource "blueprint", here it is possible to set endpoint, cache policy, log level etc...
*/
class AService<AServiceResponse>: Service<Response> {

    init() {
        super.init()
        basePath = "https://api.a-backend.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?
}

// callback-based approach
AService()
    .load()
    .onSuccess { response in
        // response is an instance of `AServiceResponse`: Type-safe swift superpower!
    }
    .onFailure { error in
        // handle error
    }
}

// async/await-based approach
do {
    let response = try await AService().load()
    // response is an instance of `AServiceResponse`: Type-safe swift superpower!
    print(response)
} catch {
    print(error)
}

设计时类型安全

利用 Swift 的类型系统和最新特性,使用 Nomosi,您永远不会直接处理 JSON 和混合数据内容。您可以忘记像 MarshalSwiftyJSON 这样的第三方库。

易于装饰(例如:令牌刷新)和/或使请求无效

处理令牌和请求验证可能会很棘手。这就是为什么引入了 decorateRequest 闭包。

在启动网络任务之前,将调用该闭包。然后,使用完成块,可以取消或装饰即将执行的网络请求。

示例

class TokenProtectedService<ServiceResponse>: Service<Response> {

    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"))
                }
            }
        }
    }
    
}

简单直观的缓存配置,选择您需要的层(默认为`URLCache`)

缓存通过CacheProvider协议处理。

默认的URLCache支持此协议,使用podspec Nomosi/CoreDataCache可以将CoreData用作持久存储。

如果您想使用其他持久层库(如RealmCouchBase等...),您只需实现三个方法

func removeExpiredCachedResponses()

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

func storeIfNeeded(request: URLRequest,
                  response: URLResponse,
                  data: Data,
                  cachePolicy: CachePolicy,
                  completion: ((_ success: Bool) -> Void))

丢弃无效或冗余请求

Nomosi确保每个执行的请求都是有效且唯一的。

例如,如果在同一服务上连续两次调用load()方法,只会执行一个请求,第二次调用将收到一个冗余请求错误。

模拟支持

模拟通过定义如下MockProvider协议处理:

protocol MockProvider {

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

}

默认情况下,通过搜索名为ServiceName.mock的bundle中的文件来检索模拟。

示例

// UserService.swift
class UserService<User>: Service<Response> {

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

}
// User.swift
struct User {
    let name: String
    let surname: String
    let website: String
}
// UserService.mock
{
    "name": "Mario",
    "surname": "Iannotta",
    "website": "http://www.marioiannotta.com"
}

开发和附件第三方组件

任何符合ServiceObserver协议的类都可以在请求开始和结束时被通知;所有UI组件,如加载器和精美的按钮,都是通过此协议构建的。

预配置UI组件

安装Nomosi/UI后,您可以使用不同的预配置组件,例如

  • NetworkActivityIndicatorHandler用于处理状态栏中的网络活动指示器。
  • RemoteImageService用于在imageview中使用自定义加载器和占位符高效地加载、缓存和显示远程图像。
  • ServiceOverlayView在执行请求时处理加载器,并在旁边显示任何发生的错误和重试按钮。
  • ServiceObserverButton在执行请求时对按钮执行自定义动画(大小调整、显示加载器、隐藏内容等...)。


关于所有这些是如何工作的,您可以查看服务流程图

安装

Cocoapods

pod 'Nomosi'

Carthage

github "MarioIannotta/Nomosi"

许可

Nomosi 可在 MIT 许可下使用。有关更多信息,请参阅 LICENSE 文件。

待办事项

  • 记录所有公开的内容
  • 支持 async/await(即 Swift 并发
  • 添加单元测试
  • 添加 SSL 指针支持
  • CoreData 缓存提供者
  • 下载请求
  • 上传请求
  • 添加模拟服务的方式
  • 提供通用的接口以便可以通过仅实现 loadIfNeeded 和 storeIfNeeded 方法来使用任何存储层
  • UIImageView.Placeholder 在单元格重用时不一定工作正常
  • 添加状态栏活动指示器
  • 将 pod 拆分为 podspec (Core + UI)
  • 将字典作为正文提供
  • HTTP 状态码范围验证