From 1468e660009bc1eaae161109880d30add24432ef Mon Sep 17 00:00:00 2001 From: Oleg Dreyman Date: Mon, 28 Mar 2016 13:04:42 +0300 Subject: [PATCH] Nice README update --- README.md | 140 ++++++++++++++++++++++++++++++++- Resources/Examples.swift | 64 +++++++++++++++ Topo.xcodeproj/project.pbxproj | 4 + 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 Resources/Examples.swift diff --git a/README.md b/README.md index a207f48..e4e881d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,145 @@ [![Zewo 0.4][zewo-badge]](http://zewo.io) [![Swift 3][swift-badge]](https://swift.org) -InterchangeData mapper library, deeply inspired by lyft/mapper +**Topo** is a tiny and simple library which allows you to convert Zewo's `InterchangeData` to strongly typed objects. Deeply inspired by Lyft's [Mapper][mapper-url]. + +## Usage +#### Simplest example: + +``` swift +import Topo + +struct User: Mappable { + let id: Int + let username: String + let city: String? + + // Mappable requirement + init(map: Mapper) throws { + try id = map.from("id") + try username = map.from("username") + city = map.optionalFrom("city") + } +} + +let content: InterchangeData = [ + "id": 1654, + "username": "fireringer", + "city": "Houston" +] +let user = User.from(content) // User? +``` + +#### Mapping arrays +**Be careful!** If you use `from` instead of `fromArray`, mapping will fail. + +```swift +struct Album: Mappable { + let songs: [String] + init(map: Mapper) throws { + try songs = map.fromArray("songs") + } +} +``` + +#### Mapping enums +You can use **Topo** for mapping enums with raw values. Right now you can use only `String` and `Double` as raw value. + +```swift +enum GuitarType: String { + case acoustic + case electric +} + +struct Guitar: Mappable { + let vendor: String + let type: GuitarType + + init(map: Mapper) throws { + try vendor = map.from("vendor") + try type = map.from("type") + } +} +``` + +#### Nested `Mappable` objects + +```swift +struct League: Mappable { + let name: String + init(map: Mapper) throws { + try name = map.from("name") + } +} + +struct Club: Mappable { + let name: String + let league: League + init(map: Mapper) throws { + try name = map.from("name") + try league = map.from("league") + } +} +``` + +#### Using `Convertible` +`Mappable` is great for complex entities, but for the simplest one you can use `Convertible` protocol. `Convertible` objects can be initializaed from `InterchangeData` itself, not from its `Mapper`. For example, **Topo** uses `Convertible` to allow seamless `Int` conversion: + +```swift +extension Int: Convertible { + public static func from(customInterchangeData value: InterchangeData) -> Int? { + switch value { + case .numberValue(let number): + return Int(number) + default: + return nil + } + } +} +``` + +## Installation +- Add `Topo` to your `Package.swift` + +```swift +import PackageDescription + +let package = Package( + dependencies: [ + .Package(url: "https://github.com/dreymonde/Topo.git", majorVersion: 0, minor: 2), + ] +) +``` + +## Destination +Behind **Topo** stands the idea to use the same data structures both on Swift server-side and client-side. **Topo** depends only on Zewo's [InterchangeData][interchange-data-url], so once it will be ported on Apple systems, **Topo** will become available for iOS, OS X, watchOS and tvOS. + +### Why not `ContentMappable`? +`ContentMappable: ContentInitializable` is a protocol provided by Zewo's [ContentNegotiationMiddleware][cont-neg-mid-url] and it has similar functionality to **Topo**. But it's a lot harder to use `ContentMappable` for types which are not described in `InterchangeData`. For example, you cannot simply call `content.get()` on `Int`. Nesting is another powerful feature of **Topo**. + +Also, use of `ContentMappable` would make **Topo** depends on `ContentNegotiationMiddleware`, which is senseless for client-side. + +Although, it is possible to have another version of **Topo** which is fully compatible with `ContentNegotiationMiddleware`. It may appear in future. + + +### See also +[Resource][resource-url] by @paulofaria, which provides RESTful resources for Zewo's Router. + +## Contributing +**Topo** is in very early stage. If you want to contribute or to propose a change - you are very welcome. Almost anything can be questioned. Open an issue or submit a pull request if you have an idea. + +## Community + +[![Slack](http://s13.postimg.org/ybwy92ktf/Slack.png)](http://slack.zewo.io) + +Join us on [Slack](http://slack.zewo.io). + +## License +**Topo** is released under the MIT license. See LICENSE for details. [zewo-badge]: https://img.shields.io/badge/Zewo-0.4-FF7565.svg?style=flat [swift-badge]: https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat +[mapper-url]: https://github.com/lyft/mapper +[interchange-data-url]: https://github.com/Zewo/InterchangeData +[resource-url]: https://github.com/paulofaria/Resource +[cont-neg-mid-url]: https://github.com/Zewo/ContentNegotiationMiddleware \ No newline at end of file diff --git a/Resources/Examples.swift b/Resources/Examples.swift new file mode 100644 index 0000000..4d53b27 --- /dev/null +++ b/Resources/Examples.swift @@ -0,0 +1,64 @@ +// +// Examples.swift +// Topo +// +// Created by Oleg Dreyman on 28.03.16. +// Copyright © 2016 Oleg Dreyman. All rights reserved. +// + +struct User: Mappable { + let id: Int + let username: String + let city: String? + + init(map: Mapper) throws { + try id = map.from("id") + try username = map.from("username") + city = map.optionalFrom("city") + } +} + +let content: InterchangeData = [ + "id": 1654, + "username": "fireringer", + "city": "Houston" +] +let user = User.from(content) + +enum GuitarType: String { + case acoustic + case electric +} + +struct Guitar: Mappable { + let vendor: String + let type: GuitarType + + init(map: Mapper) throws { + try vendor = map.from("vendor") + try type = map.from("type") + } +} + +struct League: Mappable { + let name: String + init(map: Mapper) throws { + try name = map.from("name") + } +} + +struct Club: Mappable { + let name: String + let league: League + init(map: Mapper) throws { + try name = map.from("name") + try league = map.from("league") + } +} + +struct Album: Mappable { + let songs: [String] + init(map: Mapper) throws { + try songs = map.fromArray("songs") + } +} \ No newline at end of file diff --git a/Topo.xcodeproj/project.pbxproj b/Topo.xcodeproj/project.pbxproj index 4ac16f9..54fe19d 100644 --- a/Topo.xcodeproj/project.pbxproj +++ b/Topo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + BEA128091CA92A46000F56F2 /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA128081CA92A46000F56F2 /* Examples.swift */; }; BEDB11861CA9195700C6C955 /* Convertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB11851CA9195700C6C955 /* Convertible.swift */; }; BEDB11971CA91A8F00C6C955 /* AsyncStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB11891CA91A8F00C6C955 /* AsyncStream.swift */; }; BEDB11981CA91A8F00C6C955 /* AsyncStreamClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB118A1CA91A8F00C6C955 /* AsyncStreamClient.swift */; }; @@ -45,6 +46,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + BEA128081CA92A46000F56F2 /* Examples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Examples.swift; sourceTree = ""; }; BEDB11851CA9195700C6C955 /* Convertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Convertible.swift; sourceTree = ""; }; BEDB11891CA91A8F00C6C955 /* AsyncStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncStream.swift; sourceTree = ""; }; BEDB118A1CA91A8F00C6C955 /* AsyncStreamClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncStreamClient.swift; sourceTree = ""; }; @@ -164,6 +166,7 @@ BEF2D84B1CA80ADC00172582 /* InterchangeData.swift */, BEF2D8311CA80A7800172582 /* Topo.h */, BEF2D8331CA80A7800172582 /* Info.plist */, + BEA128081CA92A46000F56F2 /* Examples.swift */, ); path = Resources; sourceTree = ""; @@ -284,6 +287,7 @@ BEF2D84C1CA80ADC00172582 /* InterchangeData.swift in Sources */, BEDB11A41CA91A8F00C6C955 /* URI.swift in Sources */, BEDB11971CA91A8F00C6C955 /* AsyncStream.swift in Sources */, + BEA128091CA92A46000F56F2 /* Examples.swift in Sources */, BEDB11991CA91A8F00C6C955 /* AsyncStreamServer.swift in Sources */, BEDB11A31CA91A8F00C6C955 /* StreamServer.swift in Sources */, BEDB11A11CA91A8F00C6C955 /* StreamClient.swift in Sources */,