Ok, we have a forecast now, and we need to use it somehow.
Since it is a mood app, and weather affects mood more than temperature,
we are interested in showing this info next to the mood in a
MoodCell
.
To do this, we will need to update DB schema again: we will create
forecast
object, and will create a relation between it and a
mood
object: they will have a one-to-one relationship, which means
each
mood
will be able to have one
forecast
object and vice-versa.
forecast
object will have following properties:
id
summary
temperature
created_at
latitude
longitude
mood
Mood
schema, and add
forecast
object to it:
#import <Realm/Realm.h>
@class Forecast;
@interface Mood : RLMObject
@property NSDate *created_at;
@property NSString *comment;
@property NSInteger level;
@property NSInteger id;
@property Forecast *forecast;
@end
@interface Forecast : RLMObject
@property NSDate *created_at;
@property NSInteger latitude;
@property NSInteger longitude;
@property NSInteger temperature;
@property NSString *summary;
@property NSInteger id;
@property Mood *mood;
@end
@implementation Mood
@end
@implementation Forecast
@end
Since we have no
forecast
objects yet, migration code will be empty:
def migrate_db
next_id = 1
RLMRealmConfiguration.migrate_to_version(2) do |migration, old_version|
migration.enumerate "Mood" do |old_object, new_object|
# update object to 1st schema version:
if old_version < 1
# migrate the object to 1st version
new_object["id"] = next_id
# increment ID for a next record:
next_id += 1
end
if old_version < 2
# empty migration to add new Forecast model
end
end
end
end
Do you remember the small problem with
ID
fields we had with
Mood
class? We needed some unique field that would
represent each
Mood
object, and we created ID. Then we added
Mood.next_id
method
that would assign a unique ID to each new object.
Since
Forecast
object needs
ID
as well,
we will need to implement the same method for
Forecast
class as well. But in order not to repeat ourselves, we will
create a new
BaseObjectMethods
module that will have a
self.next_id
method, and that will set
ID
on each object to a unique value. And then we will
use it in both
Mood
and
Forecast
classes.
Let's create
/app/models/_base.rb
first, and update our three files
accordingly:
# /app/models/_base.rb:
module BaseObjectMethods
# use methods described in ClassMethods as a class methods:
def self.included(base)
base.extend(ClassMethods)
end
# class methods should be described in the module below:
module ClassMethods
def default_values
{
"id" => self.next_id
}
end
def next_id
# get max ID and increment it:
self.all.maxOfProperty("id").to_i + 1
end
end
end
# /app/models/forecast.rb:
class Forecast
include MotionRealm
include AppConstants
include BaseObjectMethods
def self.for_location(location, on_date: date, &block)
# ...
end
# rest of Forecast code ...
end
# /app/models/mood.rb:
class Mood
include MotionRealm
include BaseObjectMethods
end
Now when we have Forecast schema created and set up, we can start
creating new
Forecast
objects each time user creates new
Mood
objects
in
MoodSelectorScreen
.
class MoodSelectorScreen < PM::Screen
title "Mood Selector"
def set_mood(level)
if @forecast.is_a?(Hash) && @location.is_a?(Hash)
mood = Mood.create level: level, created_at: NSDate.now
forecast = Forecast.create summary: @forecast[:summary],
temperature: @forecast[:temperature],
latitude: @location[:lat],
longitude: @location[:lng]
forecast.mood = mood
mood.forecast = forecast
else
UIAlertController.alert self, "Error",
message: "Forecast is not downloaded yet. Please try again in a second",
buttons: ["OK"]
return
end
RLMRealm.write do |realm|
realm << mood
if forecast
realm << forecast
end
end
close
end
# rest of the code ...
end
If forecast data is not ready yet, we will show an alert. It is not
a good strategy - in a real app, you would need at least to show
some loading indicator.
Let's try to launch the app and create a new mood. When done, just try to check it in console, whether it has a forecast attached, and whether forecast has correct data:
Now we need to update
MoodHistoryScreen
to
show forecast in each Mood cell. We will update
def tableView(table, cellForRowAtIndexPath: index)
methods that render each cell to provide weather info to the cell,
and
set_table_data
method to provide weather info:
class MoodHistoryScreen < PM::Screen
title "Mood History"
# MoodHistoryScreen code ...
def set_table_data
# set_table_data code ...
moods.each do |mood|
# Figure out what part of the day was on the time of adding a mood:
mood_hour = mood.created_at.hour
if mood_hour >= 0 && mood_hour < 14
day_part = "Morning"
elsif mood_hour >= 14 && mood_hour < 20
day_part = "Day"
else
day_part = "Evening"
end
# NEW:
# add forecast's weather image:
if mood.forecast.is_a?(Forecast)
weather_icon = mood.forecast.icon
else
weather_icon = nil
end
# create a cell from a given mood object and calculated part of the day:
section[:cells] << {
date: day_part,
mood: mood.level,
comment: mood.comment,
id: mood.id,
# NEW:
# include weather image:
weather_icon: weather_icon,
}
end
# rest of the set_table_data code
end
def tableView(table, cellForRowAtIndexPath: index)
# ...
# set cell's title to our item's title
cell.main_text = item[:date]
cell.mood_text = item[:mood]
cell.weather_icon = item[:weather_icon]
# return cell to the table so that it can render it:
cell
end
# rest of the MoodHistoryScreen ...
end
And the last thing to update is the cell and its layout. It will show
different icons according to
weather_icon
variable. First we will save image view into an instance variable
@weather_icon
to access it in the future,
and then we will create
weather_icon
method to set a picture:
# /app/cells/mood_history_cell.rb
class MoodHistoryCell < UITableViewCell
def initWithStyle(style, reuseIdentifier:reuseIdentifier)
super
@layout = MoodHistoryCellLayout.new(root: self.contentView).build
@main_text = @layout.get(:title)
@mood_text = @layout.get(:mood)
@weather_icon = @layout.get(:weather_icon)
self
end
# rest of the cell code ...
def weather_icon=(icon)
return if !icon.is_a?(UIImage)
@weather_icon.image = icon
end
end
Layout will be simple for now. We will show weather icon on the right
side
of a mood label:
# /app/layouts/cells/mood_history_layout.rb
class MoodHistoryCellLayout < MotionKit::Layout
def layout
root :cell do
add UILabel, :title
add UILabel, :mood
add UIImageView, :weather_icon
end
end
# rest of the layout code ...
def weather_icon_style
constraints do
center_y.equals(:superview, :center_y)
right.equals(:mood, :left).minus(15)
width(32)
height(32)
end
end
end
Finally, we can launch the app and create some new mood objects. Your
MoodHistoryScreen
will look similar to this now:
It is a good time to commit all changes. Let's do it with
git add .
git commit -m "Forecasts added"
In this chapter we added a new feature - the app can get and save forecasts with each new Mood objects. We have learned how to create One-to-One relationships in the Database, recalled how to create a new object in DB, worked with TableView, and more.