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
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
105 additions
and
51 deletions
+105
-51
1Weather/Model/LocationManager.swift
+69
-42
1Weather/Model/ModelObjects/Location.swift
+10
-0
1Weather/Storage/CoreData/CoreDataStorage.swift
+13
-8
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 {
public
typealias
CurrentLocationCompletion
=
(
LocationRequestResult
)
->
()
public
private(set)
var
locations
=
[
Location
]()
{
didSet
{
private
var
_locations
=
[
Location
]()
private
var
_selectedLocationIndex
:
Int
?
private
func
set
(
locations
:
[
Location
],
selectedIndex
:
Int
?)
{
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
self
?
.
_locations
=
locations
self
?
.
_selectedLocationIndex
=
selectedIndex
self
?
.
handleLocationsChange
(
locationsChanged
:
true
,
selectedLocationChanged
:
true
)
}
}
private
func
handleLocationsChange
(
locationsChanged
:
Bool
,
selectedLocationChanged
:
Bool
)
{
if
locationsChanged
{
log
.
info
(
"Locations list updated:
\(
locations
.
map
{
$0
.
description
}
.
joined
(
separator
:
", "
)
)
"
)
let
newValue
=
locations
if
let
selectedIndex
=
self
.
selectedLocationIndex
,
selectedIndex
>=
newValue
.
count
{
self
.
selectedLocationIndex
=
newValue
.
count
>
0
?
0
:
nil
}
else
{
updateAllWeatherIfNeeded
()
//TODO: the whole flow is not optimal.
self
.
delegates
.
invoke
{
(
delegate
)
in
delegate
.
locationManager
(
self
,
changedSelectedLocation
:
self
.
selectedLocation
)
}
}
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
guard
let
self
=
self
else
{
return
}
delegate
.
locationManager
(
self
,
updatedLocationsList
:
self
.
locations
)
}
storage
.
save
(
locations
:
locations
,
selectedIndex
:
selectedLocationIndex
)
}
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
)
updateAllWeatherIfNeeded
()
}
private
var
selectedLocationIndex
:
Int
?
{
didSet
{
var
oldLocation
:
Location
?
if
let
oldValue
=
oldValue
,
oldValue
<
locations
.
count
{
oldLocation
=
locations
[
oldValue
]
}
var
newLocation
:
Location
?
if
let
newValue
=
selectedLocationIndex
,
newValue
<
locations
.
count
{
newLocation
=
locations
[
newValue
]
public
private(set)
var
locations
:
[
Location
]
{
get
{
return
_locations
}
set
{
let
oldSelectedLocation
=
self
.
selectedLocation
if
let
selectedLocation
=
self
.
selectedLocation
{
if
let
newSelectedIndex
=
newValue
.
firstIndex
(
where
:
{
$0
==
selectedLocation
})
{
_selectedLocationIndex
=
newSelectedIndex
}
else
{
_selectedLocationIndex
=
nil
}
}
if
oldLocation
?
.
description
!=
newLocation
?
.
description
{
log
.
info
(
"Current location changed to:
\(
selectedLocation
?
.
description
??
"nil"
)
"
)
if
_selectedLocationIndex
==
nil
&&
!
newValue
.
isEmpty
{
_selectedLocationIndex
=
0
}
log
.
info
(
"Location updated."
)
DispatchQueue
.
main
.
async
{
self
.
delegates
.
invoke
{
[
weak
self
]
(
delegate
)
in
guard
let
self
=
self
else
{
return
}
delegate
.
locationManager
(
self
,
changedSelectedLocation
:
newLocation
)
}
_locations
=
newValue
let
selectedLocationChanged
=
(
oldSelectedLocation
!=
self
.
selectedLocation
)
handleLocationsChange
(
locationsChanged
:
true
,
selectedLocationChanged
:
selectedLocationChanged
)
}
}
private
var
selectedLocationIndex
:
Int
?
{
get
{
return
_selectedLocationIndex
}
set
{
_selectedLocationIndex
=
newValue
if
newValue
==
nil
&&
!
locations
.
isEmpty
{
_selectedLocationIndex
=
0
}
updateAllWeatherIfNeeded
(
)
handleLocationsChange
(
locationsChanged
:
false
,
selectedLocationChanged
:
true
)
}
}
...
...
@@ -95,7 +116,12 @@ public class LocationManager {
get
{
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!
return
defaultLocation
if
locations
.
count
>
0
{
return
locations
.
first
}
else
{
return
defaultLocation
}
}
guard
index
<
locations
.
count
else
{
assertionFailure
(
"This shouldn't happen. Got to investigate."
)
...
...
@@ -137,17 +163,18 @@ public class LocationManager {
self
.
deviceLocationMonitor
.
delegate
=
self
storage
.
load
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
guard
let
self
=
self
else
{
return
}
guard
error
==
nil
else
{
self
.
log
.
error
(
"Error while loading locations:
\(
error
!
)
"
)
return
}
guard
let
locations
=
locations
else
{
assertionFailure
(
"Either error or locations have to be nil, not both"
)
return
DispatchQueue
.
main
.
async
{
guard
let
self
=
self
else
{
return
}
guard
error
==
nil
else
{
self
.
log
.
error
(
"Error while loading locations:
\(
error
!
)
"
)
return
}
guard
let
locations
=
locations
else
{
assertionFailure
(
"Either error or locations have to be nil, not both"
)
return
}
self
.
set
(
locations
:
locations
,
selectedIndex
:
selectedIndex
)
}
self
.
locations
=
locations
self
.
selectedLocationIndex
=
selectedIndex
}
}
...
...
1Weather/Model/ModelObjects/Location.swift
View file @
8e13f07c
...
...
@@ -65,6 +65,16 @@ public struct Location {
public
var
cityId
:
String
{
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
{
...
...
1Weather/Storage/CoreData/CoreDataStorage.swift
View file @
8e13f07c
...
...
@@ -9,6 +9,7 @@ import Foundation
import
CoreData
public
class
CoreDataStorage
:
Storage
{
private
var
lastSavedAppData
:
AppData
?
=
nil
private
let
log
=
Logger
(
componentName
:
"CoreDataStorage 💾"
)
private
lazy
var
managedContext
:
NSManagedObjectContext
?
=
{
persistentContainer
.
newBackgroundContext
()
...
...
@@ -27,15 +28,20 @@ public class CoreDataStorage: Storage {
log
.
info
(
"Save: start"
)
managedContext
?
.
perform
{
[
weak
self
]
in
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
{
return
}
do
{
try
self
.
deleteAll
(
in
:
context
)
let
appData
=
AppData
(
selectedIndex
:
selectedIndex
,
locations
:
locations
)
if
let
coreAppData
=
try
CoreAppData
(
context
:
context
,
appModel
:
appData
)
{
context
.
insert
(
coreAppData
)
try
self
.
save
(
context
:
context
)
self
.
lastSavedAppData
=
appData
}
self
.
log
.
info
(
"Save: success"
)
}
...
...
@@ -52,31 +58,30 @@ public class CoreDataStorage: Storage {
guard
let
context
=
self
.
managedContext
else
{
return
}
let
completion
OnMain
:
StorageCompletion
=
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
let
completion
WithErrorHandling
:
StorageCompletion
=
{
[
weak
self
]
(
locations
,
selectedIndex
,
error
)
in
if
error
!=
nil
{
self
?
.
log
.
error
(
"Load: error."
)
}
else
{
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
{
let
fetchRequest
:
NSFetchRequest
<
CoreAppData
>
=
CoreAppData
.
fetchRequest
()
fetchRequest
.
fetchLimit
=
1
let
results
=
try
context
.
fetch
(
fetchRequest
)
guard
let
coreAppData
=
results
.
first
else
{
completion
OnMain
([],
nil
,
nil
)
completion
WithErrorHandling
([],
nil
,
nil
)
return
}
let
appData
:
AppData
=
try
coreAppData
.
toAppModel
()
completionOnMain
(
appData
.
locations
,
appData
.
selectedIndex
,
nil
)
self
.
lastSavedAppData
=
appData
completionWithErrorHandling
(
appData
.
locations
,
appData
.
selectedIndex
,
nil
)
}
catch
{
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
/// 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
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