So I'm working with a weather API:
https://api.darksky.net/forecast/88d117d813f2014a1ce7f3de6a00c720/50.909698,-1.404351
The thing I want to achieve is getting all the precipIntensity from each minute into an array. This way I can make a graph where the Y is the precipIntensity and the X is array.count (which would count up to 61 with this API).
So this is my code in the ViewController:
func downloadWeatherDetails(completed: DownloadComplete) {
// Alamofire Download
let currentWeatherURL = URL(string: WeatherURL)!
Alamofire.request(currentWeatherURL).responseObject { (response: DataResponse<WeatherResponse>) in
let Forecast = response.result.value
if let minutelyForecast = Forecast?.minutelyForecast {
for forecast in minutelyForecast {
print(forecast.time)
print(forecast.precipIntensity)
}
}
}
}
And my Class file is as follows:
class WeatherResponse: Mappable {
var summary: String!
var minutelyForecast: [Forecast]?
required init?(map: Map) {
}
func mapping(map: Map) {
summary <- map["minutely.summary"]
minutelyForecast <- map["minutely.data"]
}
}
class Forecast: Mappable {
var time: String!
var precipIntensity: String!
required init?(map: Map) {
}
func mapping(map: Map) {
time <- map["time"]
precipIntensity <- map["precipIntensity"]
}
}
However the print(forecast.precipIntensity) prints out nil, 61 times.
How do I get this info?
And how can I put it into an array to make it into a graph and continue with my work?
Thanks in advance.
The problem with your code was that time and precipIntensity aren't String values, but numbers.
class Forecast: Mappable {
var time: Int!
var precipIntensity: Double!
...
}
Extracting precipitation data :
func downloadWeatherDetails(completed: DownloadComplete) {
// Alamofire Download
let currentWeatherURL = URL(string: WeatherURL)!
Alamofire.request(currentWeatherURL).responseObject { (response: DataResponse<WeatherResponse>) in
let Forecast = response.result.value
if let minutelyForecast = Forecast?.minutelyForecast {
// Data is already sorted from the API
// but you could be sure nothing happened while decoding
// by using `sorted()`
let minutelyPrecipIntensity = minutelyForecast
.sorted { $0.time < $1.time }
.flatMap { $0.precipIntensity }
}
}
}
Edit: Removed Codable answer
Related
I'm new to ObjectMapper. I have response from the server:
{
"123123": 10,
"435555": 2,
"435333": 8,
"567567": 4
}
Keys (dynamic) are going to be IDs. Values are going to be COUNT. How can I map it with ObjectMapper?
My code isn't working because dynamic keys:
extension Item: Mappable {
private static let kId = "id"
private static let kCount = "count"
public init?(map: Map) {
self.init()
}
mutating public func mapping(map: Map) {
id <- map[Item.kId]
count <- map[Item.kCount]
}
}
Your response is an object and you can access it via map.JSON, and its type is [String: Any]. Then you can use that like a normal Dictionary.
Here I create a class named Model that has array of items (of type Item) and in func mapping(:Map) I mapped map.JSON elements to Item.
class Model: Mappable {
typealias Item = (id: String, count: Int)
var items: [Item] = []
required init?(map: Map) {
}
func mapping(map: Map) {
let rawDictionary = map.JSON
let items = rawDictionary.compactMap { (key, value) -> Item? in
guard let intValue = value as? Int else { return nil }
return (key, intValue)
}
self.items = items
}
}
let jsonString = """
{
"123123": 10,
"435555": 2,
"435333": 8,
"567567": 4
}
"""
let model = Model(JSONString: jsonString)
print(model?.items[0]) //Optional((id: "123123", count: 10))
You can try
do{
let res = try JSONDecoder().decode([String:Int].self, from: data)
}
catch {
print(error)
}
I have the main model of ChatConversationModel, which has an array property userAcitivities that is an array of ChatUserAcitivityModel objects. I want to get from Realm the ChatConversationModel object in which the userAcitivities contains the given friendID. I've tried a few different ways but haven't been able to get the result I'm after.
import Foundation
import RealmSwift
import ObjectMapper
class ChatConversationModel: Object, Mappable {
dynamic var id = ""
dynamic var typeIndex = ChatTypes.ChatType.oneToOne.index
var userAcitivities = List<ChatUserAcitivityModel>()
override class func primaryKey() -> String? {
return "id"
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
if map.mappingType == .fromJSON {
id <- map["id"]
typeIndex <- map["typeIndex"]
userAcitivities <- (map["userAcitivities"], ListTransform<ChatUserAcitivityModel>())
} else {
id >>> map["id"]
typeIndex >>> map["typeIndex"]
userAcitivities >>> (map["userAcitivities"], ListTransform<ChatUserAcitivityModel>())
}
}
}
import Foundation
import RealmSwift
import ObjectMapper
class ChatUserAcitivityModel: Object, Mappable {
dynamic var userID = ""
/// This is necessary in order to know from what point to download the chat if the user deleted it.
/// If this property is 0.0, then there has never been a deletion.
dynamic var removedChatTimeStamp = 0.0
override class func primaryKey() -> String? {
return "userID"
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
if map.mappingType == .fromJSON {
userID <- map["userID"]
removedChatTimeStamp <- map["removedChatTimeStamp"]
} else {
userID >>> map["userID"]
removedChatTimeStamp >>> map["removedChatTimeStamp"]
}
}
}
func getFriendChatConversationModel(_ friendID: String) {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
let friend = chatConversationModels.filter { $0.userAcitivities.filter { $0.userID == friendID } }
}
At the moment I'm doing the following and it's working for me, but I'd like to find the best way to express this:
func getFriendChatConversationModel(_ friendID: String) -> ChatConversationModel? {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
var friendChatConversationModel: ChatConversationModel?
for chatConversationModel in chatConversationModels {
if chatConversationModel.userAcitivities.contains(where: { (chatUserAcitivityModel) -> Bool in
chatUserAcitivityModel.userID == friendID
}) {
friendChatConversationModel = chatConversationModel
return friendChatConversationModel
}
}
return nil
}
If I understand correctly, I think a query like this will do what you're after:
func getFriendChatConversationModel(_ friendID: String) -> ChatConversationModel? {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
return chatConversationModels.filter("ANY userAcitivities.userID == %#", friendID).first
}
I am using the alamofire to get response JSON from server, then using ObjectMapper to map string to Realm object.
The realm object is:
class SolutionVideo: Object, Mappable {
dynamic var svID = 0
dynamic var solutionTitle = ""
dynamic var videoName = ""
dynamic var relatedInfo = ""
dynamic var shortDesc = ""
override static func primaryKey() -> String? {
return "svID"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
svID <- map["svID"]
solutionTitle <- map["solutionTitle"]
videoName <- map["videoName"]
relatedInfo <- map["relatedInfo"]
shortDesc <- map["shortDescription"]
}
}
The json string is:
[
{
"svID": "10",
"solutionTitle": "Video10",
"videoName": "Video10",
"realtedInfo": "",
"shortDescription": ""
},
{
"svID": "9",
"solutionTitle": "Video9",
"videoName": "Video9",
"realtedInfo": "",
"shortDescription": ""
}
]
in my viewController:
#IBAction func updateBtn(sender: AnyObject) {
// download file to update Realm
let url = "http://janicedemo.com/updates.json"
Alamofire.request(.GET, url).responseArray { (response: Response<[SolutionVideo], NSError>) in
let Array = response.result.value
print(Array)
if let Array = Array {
for video in Array {
let dbURL = Realm.Configuration.defaultConfiguration.fileURL
let realm = try! Realm(fileURL: dbURL!)
try! realm.write{
print("will save")
realm.add(video, update: true)
}
}
}
}
The problem is that I can add the object successfully. But the svID(primark key) keeps 0, instead of 10 or 9 (set in JSON). Is it because I have set default value to svID? Can someone gives me a hint? Thanks
Try
class SolutionVideo: Object, Mappable {
dynamic var svID = 0
dynamic var solutionTitle = ""
dynamic var videoName = ""
dynamic var relatedInfo = ""
dynamic var shortDesc = ""
func setCompoundID(id: Int) {
self.svID = svID
compoundKey = compoundKeyValue()
}
func setCompoundID(id: Int) {
self.solutionTitle = solutionTitle
compoundKey = compoundKeyValue()
}
func setCompoundID(id: Int) {
self.videoName = videoName
compoundKey = compoundKeyValue()
}
func setCompoundID(id: Int) {
self.relatedInfo = relatedInfo
compoundKey = compoundKeyValue()
}
func setCompoundID(id: Int) {
self.shortDesc = shortDesc
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return “compoundKey”
}
func compoundKeyValue() -> String {
return "\(svID)\(solutionTitle)\(videoName)\(relatedInfo)\(shortDesc)”
}
}
The main thing I can think of is that the primary key values are coming down as strings instead of proper integers (i.e., "10" instead of simply 10). It's possible that the mapper isn't smart enough to handle conversion of strings to integers, so it's just defaulting to 0.
According to the ObjectMapper documentation, you should be able to perform this conversion inline in your mapping function:
svID <- (map["svID"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
Also, as a sidenote, I noticed that one of your JSON key names has a spelling error: "realtedInfo". So I'd recommend double-checking that works too. :)
I have this JSON:
{
"location": {
"position": {
"type": "Point",
"coordinates": [
45.579553,
11.751805
]
}
}
}
Which belongs to another JSON object.
Trying to map it with Realm and ObjectMapper, I am findind difficulties mapping the coordinates property which is an array of double.
That's what reading the documentation and S.O. seems to have sense:
import Foundation
import RealmSwift
import ObjectMapper
class Coordinate:Object, Mappable{
dynamic var latitude:Double = 0.0
dynamic var longitude:Double = 0.0
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
latitude <- map[""]
longitude <- map[""]
}
}
class Position: Object, Mappable{
var type:String = ""
var coordinates:Coordinate?
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
type <- map["type"]
coordinates <- map["coordinates"]
}
}
class Location: Object, Mappable{
dynamic var id = ""
dynamic var position:Position?
dynamic var desc = ""
override static func indexedProperties()->[String]{
return["id"]
}
override class func primaryKey() -> String? {
return "id"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
position <- map["position"]
}
}
However I'm stuck in understanding how to map the "coordinates" object. Please note that this problem has nothing to do with ObjectMapper itself, it's more of a question on how to assign an array of Double to a property in a Realm model.
I was able to solve this following the indications in this issue:
https://github.com/realm/realm-cocoa/issues/1120 (credits #jazz-mobility)
class DoubleObject:Object{
dynamic var value:Double = 0.0
}
class Position: Object, Mappable{
var type:String = ""
var coordinates = List<DoubleObject>()
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
type <- map["type"]
var coordinates:[Double]? = nil
coordinates <- map["coordinates"]
coordinates?.forEach { coordinate in
let c = DoubleObject()
c.value = coordinate
self.coordinates.append(c)
}
}
}
You can now simply use List<Double>() without it storing an object.
More information can be found here: https://academy.realm.io/posts/realm-list-new-superpowers-array-primitives/
#objcMembers class RealmObject: Object, Mappable {
dynamic var listValues = List<MyRealmObject>()
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
listValues <- (map["listValues"], RealmlistObjectTransform())
}
}
#objcMembers class MyRealmObject: Object, Mappable {
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
}
}
class RealmlistObjectTransform: TransformType {
typealias Object = List<MyRealmObject> // My Realm Object here
typealias JSON = [[String: Any]] // Dictionary here
func transformFromJSON(_ value: Any?) -> List<MyRealmObject>? {
let list = List<MyRealmObject>()
if let actors = value as? [[String: Any]] {
let objects = Array<MyRealmObject>(JSONArray: actors)
list.append(objectsIn: objects)
}
return list
}
func transformToJSON(_ value: List<MyRealmObject>?) -> [[String: Any]]? {
if let actors = value?.sorted(byKeyPath: "").toArray(ofType: MyRealmObject.self).toJSON() {
return actors
}
return nil
}
}
As a lot of people has this error, so do I, I have no idea why the "responseObject cannot be invoked with argument list of type ((JSONResponse>) -> _)"
I'm also using mapping for JSON with Alamofire+ObjectMapper
Do anybody have an idea, whats wrong with Alamofire, its very often error, may be any thoughts about understanding Alamofire?
import Foundation
import Alamofire
import AlamofireObjectMapper
import ObjectMapper
public protocol Mappable {
static func newInstance(map: Map) -> Mappable?
mutating func mapping(map: Map)
}
class JMapping {
func getJSON() {
let url = "http://www.somestringToJson/json.json"
Alamofire
.request(.GET, url, parameters: nil)
.responseObject { (response: JSONResponse?) in
println(response?.title)
if let events = response?.events {
for event in events {
println(event.id)
println(event.title)
}
}
}
}
}
class JSONResponse: Mappable {
var title: String?
var id: String?
var events: [EventsResponse]?
class func newInstance(map: Map) -> Mappable? {
return JSONResponse()
}
func mapping(map: Map) {
title <- map["title"]
id <- map["id"]
events <- map["events"]
}
}
class EventsResponse: Mappable {
var title: String?
var id: String?
var content: [ContentResponse]?
class func newInstance(map:Map) -> Mappable? {
return EventsResponse()
}
func mapping(map: Map) {
title <- map["title"]
id <- map["id"]
content <- map["content"]
}
}
class ContentResponse: Mappable{
var content_type: String?
var visible: Bool?
var data: NSArray?
class func newInstance(map: Map) -> Mappable? {
return ContentResponse()
}
func mapping(map: Map) {
content_type <- map["content_type"]
visible <- map["visible"]
data <- map["data"]
}
}
Alamofire.request(.GET, "http://yoururl.de")
.response { (request, response, data, error) in
}
Basic alamofire get request looks like this. Response is called when request is finished.
After that I would recommend to use SwiftyJSON and convert your responsed data.
if let data = (responseBody as NSString).dataUsingEncoding(NSUTF8StringEncoding) {
let json = JSON(data: data)
println(json)
}