generated from 0xOpenBytes/ios-base
-
Notifications
You must be signed in to change notification settings - Fork 2
Feature 5 & 6 #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Feature 5 & 6 #22
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
e721aac
Initial TDD for DatabaseService
devahmedshendy 8d8d6d3
Add GRDB dependency using project.yml instead
devahmedshendy b2d0189
Write test for inserting favorite location
devahmedshendy 21ec3e6
Remove constants of sqlColumn/sqlTable
devahmedshendy 77009ef
Fix: uuid shouldn't be saved, but is string representation
devahmedshendy 9515f49
Clean up
devahmedshendy e98c6c1
Refactor sql statements to find location by name, lat, long instead
devahmedshendy 9674263
Use LocationProvider, and make view models as StateObject
devahmedshendy 5e45f7a
Add created_at to fetch favorites as sorted
devahmedshendy 6b89edd
Change some 'summary' namings to 'favorites'
devahmedshendy 7ef1412
Implement Feature 5 & 6
devahmedshendy 08d2f23
DatabaseError, memory leak, cleanup
devahmedshendy 4de9a0c
Apple fixes
devahmedshendy 35c8445
Rename database to databaseService
devahmedshendy 147f515
refactor LocationAdapterTest
devahmedshendy 491ce4b
Mentioned ticket #23
devahmedshendy fc37985
rewrite/add-more tests for SQLiteDatabaseServiceTests
devahmedshendy 301e586
Write unit test for toggleLocation of SearchLocationViewModel
devahmedshendy be30d6b
Divide unit test for toggleFavorite in SearchLocationViewModel to sea…
devahmedshendy 07fc7d6
Add tests for FavoritesViewModel
devahmedshendy a857cbc
Add test for weather summary for FavoritesViewModel
devahmedshendy fe86f10
Use tagged version for GRDB than master branch
devahmedshendy a71a787
Resolve conversation
devahmedshendy 58a025e
Fix project.yml syntax, 'from' instead of 'branch'
devahmedshendy 148f647
Sync unfavorites with search result
devahmedshendy 726c93a
clean up
devahmedshendy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// | ||
// OpenWeather template generated by OpenBytes on 16/03/2023. | ||
// | ||
// Created by Ahmed Shendy. | ||
// FavoritesScreen.swift | ||
// | ||
|
||
import SwiftUI | ||
import CoreLocation | ||
|
||
struct FavoritesScreen: View { | ||
@StateObject var viewModel: FavoritesViewModel | ||
|
||
var body: some View { | ||
viewModel.view { content in | ||
if content.summaries.isEmpty { | ||
Text("No Favorites Available") | ||
} else { | ||
List { | ||
ForEach(content.summaries) { favorite in | ||
LocationWeatherItem( | ||
locationName: favorite.locationName, | ||
temperature: favorite.temperature, | ||
symbolName: favorite.symbolName | ||
) | ||
.swipeActions(allowsFullSwipe: false) { | ||
Button(role: .destructive) { | ||
viewModel.removeFavoriteLocation(favorite.location) | ||
} label: { | ||
Label("Remove", systemImage: "heart.slash") | ||
} | ||
.tint(.red) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct LocationWeatherItem: View { | ||
let locationName: String | ||
let temperature: String | ||
let symbolName: String | ||
|
||
var body: some View { | ||
HStack { | ||
Text(locationName) | ||
Spacer() | ||
Text(temperature) | ||
Image(systemName: symbolName) | ||
} | ||
} | ||
} | ||
|
||
struct SummaryScreen_Previews: PreviewProvider { | ||
static var previews: some View { | ||
FavoritesScreen(viewModel: .mock) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// | ||
// OpenWeather template generated by OpenBytes on 16/03/2023. | ||
// | ||
// Created by Ahmed Shendy. | ||
// FavoritesViewModel.swift | ||
// | ||
|
||
import Combine | ||
import ViewModel | ||
import Foundation | ||
import CoreLocation | ||
|
||
final class FavoritesViewModel: ViewModel< | ||
FavoritesViewModel.Capabilities, | ||
FavoritesViewModel.Input, | ||
FavoritesViewModel.Content | ||
> { | ||
struct Capabilities { | ||
static var mock: Capabilities { | ||
.init( | ||
locationProviding: MockLocationProvider(), | ||
weatherProviding: MockWeatherProvider(), | ||
databaseService: MockDatabaseService() | ||
) | ||
} | ||
|
||
private let weatherProviding: WeatherProviding | ||
private let locationProviding: LocationProviding | ||
private let databaseService: DatabaseService | ||
|
||
init( | ||
locationProviding: LocationProviding, | ||
weatherProviding: WeatherProviding, | ||
databaseService: DatabaseService | ||
) { | ||
self.locationProviding = locationProviding | ||
self.weatherProviding = weatherProviding | ||
self.databaseService = databaseService | ||
} | ||
|
||
func locationName(for location: CLLocation) async throws -> String { | ||
try await locationProviding.locationName(for: location) | ||
} | ||
|
||
func weather(for location: CLLocation) async throws -> DeviceWeather { | ||
try await weatherProviding.weather(for: location) | ||
} | ||
|
||
func getFavoritesPublisher() -> AnyPublisher<[DeviceLocation], Error> { | ||
databaseService.fetchAllFavoritesPublisher() | ||
} | ||
|
||
func removeFavorite(_ location: DeviceLocation) async throws { | ||
try await databaseService.deleteOneFavorite(location) | ||
} | ||
} | ||
|
||
struct Input { } | ||
|
||
struct Content { | ||
let summaries: [DeviceWeatherSummary] | ||
} | ||
|
||
override var content: Content { | ||
Content(summaries: summaries) | ||
} | ||
|
||
static var mock: FavoritesViewModel { | ||
.init(capabilities: .mock, input: .init()) | ||
} | ||
|
||
private let errorHandler: ErrorHandler | ||
private var subscription: AnyCancellable? | ||
|
||
@Published private var summaries: [DeviceWeatherSummary] = [] | ||
|
||
init( | ||
errorHandler: ErrorHandler = ErrorHandler( | ||
plugins: [ToastErrorPlugin()] | ||
), | ||
capabilities: Capabilities, | ||
input: Input | ||
) { | ||
self.errorHandler = errorHandler | ||
|
||
super.init(capabilities: capabilities, input: input) | ||
|
||
observeChangesInFavorites() | ||
} | ||
|
||
private func observeChangesInFavorites() { | ||
subscription = self.capabilities | ||
.getFavoritesPublisher() | ||
.flatMap { $0 | ||
.publisher | ||
.setFailureType(to: Error.self) | ||
.asyncCompactMap(self.getWeatherSummary(for:)) | ||
devahmedshendy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.collect() | ||
} | ||
.receive(on: RunLoop.main) | ||
.sink( | ||
receiveCompletion: { [weak self] completion in | ||
if case let .failure(error) = completion { | ||
self?.errorHandler.handle(error: error) | ||
} | ||
}, | ||
receiveValue: { [weak self] summaries in | ||
self?.summaries = summaries | ||
} | ||
) | ||
} | ||
|
||
private func getWeatherSummary( | ||
for location: DeviceLocation | ||
) async throws -> DeviceWeatherSummary? { | ||
guard | ||
let weather = try? await self.capabilities.weather(for: location.location) | ||
else { return nil } | ||
|
||
return DeviceWeatherSummary( | ||
location: location, | ||
weather: weather | ||
) | ||
} | ||
|
||
func removeFavoriteLocation(_ location: DeviceLocation) { | ||
Task { | ||
do { | ||
try await capabilities.removeFavorite(location) | ||
} catch { | ||
errorHandler.handle(error: error) | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// | ||
// OpenWeather template generated by OpenBytes on 30/04/2023. | ||
// | ||
// Created by Ahmed Shendy. | ||
// RootViewModel.swift | ||
// | ||
|
||
import Foundation |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.