Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
1
1weather
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
1weather
Commits
8e13f07c
Commit
8e13f07c
authored
Apr 07, 2021
by
Demid Merzlyakov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Storage: optimized saving.
parent
2cfcb142
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
91 additions
and
37 deletions
+91
-37
1Weather/Model/LocationManager.swift
+56
-29
1Weather/Model/ModelObjects/Location.swift
+10
-0
1Weather/Storage/CoreData/CoreDataStorage.swift
+12
-7
1Weather/Storage/CoreData/Objects/AppData.swift
+13
-1
No files found.
1Weather/Model/LocationManager.swift
View file @
8e13f07c
...
@@ -45,49 +45,70 @@ public class LocationManager {
...
@@ -45,49 +45,70 @@ public class LocationManager {
public
typealias
CurrentLocationCompletion
=
(
LocationRequestResult
)
->
()
public
typealias
CurrentLocationCompletion
=
(
LocationRequestResult
)
->
()
public
private(set)
var
locations
=
[
Location
]()
{
private
var
_locations
=
[
Location
]()
didSet
{
private
var
_selectedLocationIndex
:
Int
?
log
.
info
(
"Locations list updated:
\(
locations
.
map
{
$0
.
description
}
.
joined
(
separator
:
", "
)
)
"
)
private
func
set
(
locations
:
[
Location
],
selectedIndex
:
Int
?)
{
let
newValue
=
locations
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
if
let
selectedIndex
=
self
.
selectedLocationIndex
,
selectedIndex
>=
newValue
.
count
{
self
?
.
_locations
=
locations
self
.
selectedLocationIndex
=
newValue
.
count
>
0
?
0
:
nil
self
?
.
_selectedLocationIndex
=
selectedIndex
}
self
?
.
handleLocationsChange
(
locationsChanged
:
true
,
selectedLocationChanged
:
true
)
else
{
updateAllWeatherIfNeeded
()
//TODO: the whole flow is not optimal.
self
.
delegates
.
invoke
{
(
delegate
)
in
delegate
.
locationManager
(
self
,
changedSelectedLocation
:
self
.
selectedLocation
)
}
}
}
}
private
func
handleLocationsChange
(
locationsChanged
:
Bool
,
selectedLocationChanged
:
Bool
)
{
if
locationsChanged
{
log
.
info
(
"Locations list updated:
\(
locations
.
map
{
$0
.
description
}
.
joined
(
separator
:
", "
)
)
"
)
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
guard
let
self
=
self
else
{
return
}
guard
let
self
=
self
else
{
return
}
delegate
.
locationManager
(
self
,
updatedLocationsList
:
self
.
locations
)
delegate
.
locationManager
(
self
,
updatedLocationsList
:
self
.
locations
)
}
}
}
if
selectedLocationChanged
{
let
newLocation
=
self
.
selectedLocation
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
guard
let
self
=
self
else
{
return
}
delegate
.
locationManager
(
self
,
changedSelectedLocation
:
newLocation
)
}
}
storage
.
save
(
locations
:
locations
,
selectedIndex
:
selectedLocationIndex
)
storage
.
save
(
locations
:
locations
,
selectedIndex
:
selectedLocationIndex
)
updateAllWeatherIfNeeded
()
}
}
public
private(set)
var
locations
:
[
Location
]
{
get
{
return
_locations
}
}
set
{
let
oldSelectedLocation
=
self
.
selectedLocation
private
var
selectedLocationIndex
:
Int
?
{
if
let
selectedLocation
=
self
.
selectedLocation
{
didSet
{
if
let
newSelectedIndex
=
newValue
.
firstIndex
(
where
:
{
$0
==
selectedLocation
})
{
var
oldLocation
:
Location
?
_selectedLocationIndex
=
newSelectedIndex
if
let
oldValue
=
oldValue
,
oldValue
<
locations
.
count
{
}
oldLocation
=
locations
[
oldValue
]
else
{
_selectedLocationIndex
=
nil
}
}
var
newLocation
:
Location
?
if
let
newValue
=
selectedLocationIndex
,
newValue
<
locations
.
count
{
newLocation
=
locations
[
newValue
]
}
}
if
oldLocation
?
.
description
!=
newLocation
?
.
description
{
if
_selectedLocationIndex
==
nil
&&
!
newValue
.
isEmpty
{
log
.
info
(
"Current location changed to:
\(
selectedLocation
?
.
description
??
"nil"
)
"
)
_selectedLocationIndex
=
0
}
}
log
.
info
(
"Location updated."
)
_locations
=
newValue
DispatchQueue
.
main
.
async
{
let
selectedLocationChanged
=
(
oldSelectedLocation
!=
self
.
selectedLocation
)
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
handleLocationsChange
(
locationsChanged
:
true
,
selectedLocationChanged
:
selectedLocationChanged
)
guard
let
self
=
self
else
{
return
}
delegate
.
locationManager
(
self
,
changedSelectedLocation
:
newLocation
)
}
}
}
}
updateAllWeatherIfNeeded
()
private
var
selectedLocationIndex
:
Int
?
{
get
{
return
_selectedLocationIndex
}
set
{
_selectedLocationIndex
=
newValue
if
newValue
==
nil
&&
!
locations
.
isEmpty
{
_selectedLocationIndex
=
0
}
handleLocationsChange
(
locationsChanged
:
false
,
selectedLocationChanged
:
true
)
}
}
}
}
...
@@ -95,8 +116,13 @@ public class LocationManager {
...
@@ -95,8 +116,13 @@ public class LocationManager {
get
{
get
{
guard
let
index
=
selectedLocationIndex
else
{
guard
let
index
=
selectedLocationIndex
else
{
// TODO: don't do it this way, because we won't be able to tell that no location is currently selected!
// TODO: don't do it this way, because we won't be able to tell that no location is currently selected!
if
locations
.
count
>
0
{
return
locations
.
first
}
else
{
return
defaultLocation
return
defaultLocation
}
}
}
guard
index
<
locations
.
count
else
{
guard
index
<
locations
.
count
else
{
assertionFailure
(
"This shouldn't happen. Got to investigate."
)
assertionFailure
(
"This shouldn't happen. Got to investigate."
)
// But in runtime we can handle it gracefully
// But in runtime we can handle it gracefully
...
@@ -137,6 +163,7 @@ public class LocationManager {
...
@@ -137,6 +163,7 @@ public class LocationManager {
self
.
deviceLocationMonitor
.
delegate
=
self
self
.
deviceLocationMonitor
.
delegate
=
self
storage
.
load
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
storage
.
load
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
DispatchQueue
.
main
.
async
{
guard
let
self
=
self
else
{
return
}
guard
let
self
=
self
else
{
return
}
guard
error
==
nil
else
{
guard
error
==
nil
else
{
self
.
log
.
error
(
"Error while loading locations:
\(
error
!
)
"
)
self
.
log
.
error
(
"Error while loading locations:
\(
error
!
)
"
)
...
@@ -146,8 +173,8 @@ public class LocationManager {
...
@@ -146,8 +173,8 @@ public class LocationManager {
assertionFailure
(
"Either error or locations have to be nil, not both"
)
assertionFailure
(
"Either error or locations have to be nil, not both"
)
return
return
}
}
self
.
locations
=
locations
self
.
set
(
locations
:
locations
,
selectedIndex
:
selectedIndex
)
self
.
selectedLocationIndex
=
selectedIndex
}
}
}
}
}
...
...
1Weather/Model/ModelObjects/Location.swift
View file @
8e13f07c
...
@@ -65,6 +65,16 @@ public struct Location {
...
@@ -65,6 +65,16 @@ public struct Location {
public
var
cityId
:
String
{
public
var
cityId
:
String
{
return
"
\(
self
.
countryCode
??
""
)
:
\(
self
.
region
??
""
)
:
\(
self
.
cityName
??
""
)
"
return
"
\(
self
.
countryCode
??
""
)
:
\(
self
.
region
??
""
)
:
\(
self
.
cityName
??
""
)
"
}
}
public
func
equals
(
to
other
:
Location
,
onlyCompareLocationInfo
:
Bool
)
->
Bool
{
guard
!
onlyCompareLocationInfo
else
{
return
self
==
other
}
guard
self
==
other
else
{
return
false
}
guard
self
.
lastWeatherUpdateDate
==
other
.
lastWeatherUpdateDate
else
{
return
false
}
guard
self
.
health
?
.
lastUpdateTime
==
other
.
health
?
.
lastUpdateTime
else
{
return
false
}
return
true
}
}
}
extension
Location
:
Equatable
,
Hashable
{
extension
Location
:
Equatable
,
Hashable
{
...
...
1Weather/Storage/CoreData/CoreDataStorage.swift
View file @
8e13f07c
...
@@ -9,6 +9,7 @@ import Foundation
...
@@ -9,6 +9,7 @@ import Foundation
import
CoreData
import
CoreData
public
class
CoreDataStorage
:
Storage
{
public
class
CoreDataStorage
:
Storage
{
private
var
lastSavedAppData
:
AppData
?
=
nil
private
let
log
=
Logger
(
componentName
:
"CoreDataStorage 💾"
)
private
let
log
=
Logger
(
componentName
:
"CoreDataStorage 💾"
)
private
lazy
var
managedContext
:
NSManagedObjectContext
?
=
{
private
lazy
var
managedContext
:
NSManagedObjectContext
?
=
{
persistentContainer
.
newBackgroundContext
()
persistentContainer
.
newBackgroundContext
()
...
@@ -27,15 +28,20 @@ public class CoreDataStorage: Storage {
...
@@ -27,15 +28,20 @@ public class CoreDataStorage: Storage {
log
.
info
(
"Save: start"
)
log
.
info
(
"Save: start"
)
managedContext
?
.
perform
{
[
weak
self
]
in
managedContext
?
.
perform
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
guard
let
self
=
self
else
{
return
}
let
appData
=
AppData
(
selectedIndex
:
selectedIndex
,
locations
:
locations
)
guard
appData
!=
self
.
lastSavedAppData
else
{
self
.
log
.
info
(
"Save: no changes, skip"
)
return
}
guard
let
context
=
self
.
managedContext
else
{
guard
let
context
=
self
.
managedContext
else
{
return
return
}
}
do
{
do
{
try
self
.
deleteAll
(
in
:
context
)
try
self
.
deleteAll
(
in
:
context
)
let
appData
=
AppData
(
selectedIndex
:
selectedIndex
,
locations
:
locations
)
if
let
coreAppData
=
try
CoreAppData
(
context
:
context
,
appModel
:
appData
)
{
if
let
coreAppData
=
try
CoreAppData
(
context
:
context
,
appModel
:
appData
)
{
context
.
insert
(
coreAppData
)
context
.
insert
(
coreAppData
)
try
self
.
save
(
context
:
context
)
try
self
.
save
(
context
:
context
)
self
.
lastSavedAppData
=
appData
}
}
self
.
log
.
info
(
"Save: success"
)
self
.
log
.
info
(
"Save: success"
)
}
}
...
@@ -52,31 +58,30 @@ public class CoreDataStorage: Storage {
...
@@ -52,31 +58,30 @@ public class CoreDataStorage: Storage {
guard
let
context
=
self
.
managedContext
else
{
guard
let
context
=
self
.
managedContext
else
{
return
return
}
}
let
completion
OnMain
:
StorageCompletion
=
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
let
completion
WithErrorHandling
:
StorageCompletion
=
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
if
error
!=
nil
{
if
error
!=
nil
{
self
?
.
log
.
error
(
"Load: error."
)
self
?
.
log
.
error
(
"Load: error."
)
}
}
else
{
else
{
self
?
.
log
.
info
(
"Load: success.
\(
String
(
describing
:
locations
?
.
count
)
)
locations, selected:
\(
String
(
describing
:
selectedIndex
)
)
"
)
self
?
.
log
.
info
(
"Load: success.
\(
String
(
describing
:
locations
?
.
count
)
)
locations, selected:
\(
String
(
describing
:
selectedIndex
)
)
"
)
}
}
DispatchQueue
.
main
.
async
{
completion
(
locations
,
selectedIndex
,
error
)
completion
(
locations
,
selectedIndex
,
error
)
}
}
}
do
{
do
{
let
fetchRequest
:
NSFetchRequest
<
CoreAppData
>
=
CoreAppData
.
fetchRequest
()
let
fetchRequest
:
NSFetchRequest
<
CoreAppData
>
=
CoreAppData
.
fetchRequest
()
fetchRequest
.
fetchLimit
=
1
fetchRequest
.
fetchLimit
=
1
let
results
=
try
context
.
fetch
(
fetchRequest
)
let
results
=
try
context
.
fetch
(
fetchRequest
)
guard
let
coreAppData
=
results
.
first
else
{
guard
let
coreAppData
=
results
.
first
else
{
completion
OnMain
([],
nil
,
nil
)
completion
WithErrorHandling
([],
nil
,
nil
)
return
return
}
}
let
appData
:
AppData
=
try
coreAppData
.
toAppModel
()
let
appData
:
AppData
=
try
coreAppData
.
toAppModel
()
completionOnMain
(
appData
.
locations
,
appData
.
selectedIndex
,
nil
)
self
.
lastSavedAppData
=
appData
completionWithErrorHandling
(
appData
.
locations
,
appData
.
selectedIndex
,
nil
)
}
}
catch
{
catch
{
self
.
log
.
error
(
"Error during load:
\(
error
)
"
)
self
.
log
.
error
(
"Error during load:
\(
error
)
"
)
completion
OnMain
(
nil
,
nil
,
error
)
completion
WithErrorHandling
(
nil
,
nil
,
error
)
}
}
}
}
}
}
...
...
1Weather/Storage/CoreData/Objects/AppData.swift
View file @
8e13f07c
...
@@ -9,7 +9,19 @@ import Foundation
...
@@ -9,7 +9,19 @@ import Foundation
/// A helper structure, so that we could work with CoreAppData the same way we work with everything else.
/// A helper structure, so that we could work with CoreAppData the same way we work with everything else.
public
struct
AppData
{
public
struct
AppData
:
Equatable
{
public
let
selectedIndex
:
Int
?
public
let
selectedIndex
:
Int
?
public
let
locations
:
[
Location
]
public
let
locations
:
[
Location
]
public
static
func
==
(
lhs
:
Self
,
rhs
:
Self
)
->
Bool
{
guard
lhs
.
selectedIndex
==
rhs
.
selectedIndex
else
{
return
false
}
guard
lhs
.
locations
.
count
==
rhs
.
locations
.
count
else
{
return
false
}
for
(
i
,
location
)
in
lhs
.
locations
.
enumerated
()
{
let
otherLocation
=
rhs
.
locations
[
i
]
guard
location
.
equals
(
to
:
otherLocation
,
onlyCompareLocationInfo
:
false
)
else
{
return
false
}
}
return
true
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment