Loading repository data…
Loading repository data…
emrcftci / repository
:key: An example of generic parsing via ObjectMapper
:boom: This project will help you to understand generic parsing via ObjectMapper
:bangbang: Parsing Generic Types provides less line of code for mapping different objects. Please visit ObjectMapper github page, ObjectMapper CocoaDocs page or Object Mapping in Swift Medium Article fore more detail.
:100: Build Script changes URL of source every build.
This to-do list is consist of 4 sections. We will see how to install libraries, parsing generics and write a build script with Swift. :rocket:
In this part, we will learn how to install ObjectMapper via Swift Package Manager, Cocoapods, Carthage and Manually. Please visit Cocoadocs Documentation for more details.
To add ObjectMapper to a Swift Package Manager based project, add:
.Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2),
to your Package.swift files dependencies array.
ObjectMapper can be added to your project using Cocoapods 0.36 (beta) by adding the following line to your Podfile:
pod 'ObjectMapper', '~> 0.2'
You can add a dependency on ObjectMapper by adding it to your Cartfile:
github "Hearst-DD/ObjectMapper" ~> 0.2
Add ObjectMapper as a submodule by opening the terminal, cd-ing into your top-level project directory, and entering the command git submodule add https://github.com/Hearst-DD/ObjectMapper.git
Open the ObjectMapper folder, and drag ObjectMapper.xcodeproj into the file navigator of your app project.
In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
Ensure that the deployment target of ObjectMapper.framework matches that of the application target.
In the tab bar at the top of that window, open the "Build Phases" panel.
Expand the "Target Dependencies" group, and add ObjectMapper.framework.
Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add ObjectMapper.framework.
In this part, we will learn how to install Alamofire via Swift Package Manager, Cocoapods, Carthage and Manually. Please visit Cocoadocs Documentation for more details.
To add Alamofire to a Swift Package Manager based project, add:
.Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4),
to your Package.swift files dependencies array.
To integrate Alamofire into your Xcode project using CocoaPods, specify it in your Podfile:
pod 'Alamofire', '~> 5.0.0-rc.3'
To integrate Alamofire into your Xcode project using Carthage, specify it in your Cartfile:
github "Alamofire/Alamofire" "5.0.0-rc.3"
If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually.
$ git init
$ git submodule add https://github.com/Alamofire/Alamofire.git
Open the new Alamofire folder, and drag the Alamofire.xcodeproj into the Project Navigator of your application's Xcode project.
Select the Alamofire.xcodeproj in the Project Navigator and verify the deployment target matches that of your application target.
Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar.
In the tab bar at the top of that window, open the "General" panel.
Click on the + button under the "Embedded Binaries" section.
You will see two different Alamofire.xcodeproj folders each with two different versions of the Alamofire.framework nested inside a Products folder.
Select the top Alamofire.framework for iOS and the bottom one for macOS.
And that's it!
These are protocols of ObjectMapper which we'll use for parsing generic responses.
StaticMappable is an alternative to Mappable. It provides developers with a static function that is used by ObjectMapper for object initialization instead of init?(map: Map).
Note: StaticMappable, like Mappable, is a sub protocol of BaseMappable which is where the mapping(map: Map) function is defined.
static func objectForMapping(map: Map) -> BaseMappable?
ObjectMapper uses this function to get objects to use for mapping. Developers should return an instance of an object that conforms to BaseMappable in this function. This function can also be used to:
- validate JSON prior to object serialization
- provide an existing cached object to be used for mapping
- return an object of another type (which also conforms to BaseMappable) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for mapping (see examples in ClassClusterTests.swift)
If you need to implement ObjectMapper in an extension, you will need to adopt this protocol instead of Mappable.
Our Usage
import ObjectMapper
/// Firstly we should map "type" key for recognize what kind of object will come
public class AnyResponse: StaticMappable {
public var type: ResponseType = .undefined
/// `data` returns an object which has correct type and conforms `ViewModelProtocol`
public var data: ViewModelProtocol? {
return AnyResponse.dataFor(self)
}
public func mapping(map: Map) {
type <- (map["type"], EnumTransform<ResponseType>())
}
public class func objectForMapping(map: Map) -> BaseMappable? {
/// Map type first
let type: ResponseType = try! map.value("type", using: EnumTransform<ResponseType>())
/// Then we can check the type for return correct model
///
/// Response<Space>(): return Response with <T> -T is Space for this case-
/// Constructor `()` call the map function of Response<T> and T is set before calling
switch type {
case .space:
return Response<Space>()
case .company:
return Response<Company>()
case .continent:
return Response<Continent>()
case .person:
return Response<Person>()
case .undefined:
return nil
}
}
/// Helper function for return correct `ViewModelProtocol` object to use UI configuration
///
/// *Example:* `SpaceViewModel` has an initializer like -> `init(component: Response<Space>)` so `component.unpacked()` function's <T> parameter is `Space` for this example.
private class func dataFor(_ component: AnyResponse) -> ViewModelProtocol? {
switch component.type {
case .space:
return SpaceViewModel(component: component.unpacked())
case .company:
return CompanyViewModel(component: component.unpacked())
case .continent:
return ContinentViewModel(component: component.unpacked())
case .person:
return PersonViewModel(component: component.unpacked())
case .undefined:
return nil
}
}
/// Unpacked `AnyResponse` with correct object (`T`) and return Response with <T>
private func unpacked<T>() -> Response<T> {
return self as! Response<T>
}
}
The following lines are describing BaseMappable in ObjectMapper's source code.
/// BaseMappable should not be implemented directly. Mappable or StaticMappable should be used instead
public protocol BaseMappable {
/// This function is where all variable mappings should occur. It is executed by Mapper during the mapping (serialization anddeserialization) process.
mutating func mapping(map: Map)
}
Our Usage
import ObjectMapper
/// Response contains our data field for map generic type objects
public final class Response<T: BaseMappable>: AnyResponse, Mappable {
/// This will be our models (`Continent, Company, Person, Space`)
public var detail: T!
public override init() {
super.init()
}
public required init?(map: Map) {
super.init()
detail = try? map.value("detail")
}
/// Called after `objectForMapping(map: ) -> BaseMappable?` returns a correct T object
public override func mapping(map: Map) {
super.mapping(map: map)
detail <- map["detail"]
}
}
${SRCROOT}is the path to the directory containing the Xcode project. [SO answer](https://sta