Skip to content

Commit

Permalink
Use Koin modules on Android and iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine Robiez committed Mar 13, 2024
1 parent f176663 commit df11777
Show file tree
Hide file tree
Showing 27 changed files with 187 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import io.openfeedback.android.OpenFeedback

class AndroidMakersApplication : Application() {

lateinit var urlOpener: UrlOpener

lateinit var openFeedback: OpenFeedback

override fun onCreate() {
Expand All @@ -34,8 +32,6 @@ class AndroidMakersApplication : Application() {
listOf(viewModelModule)
)

urlOpener = UrlOpener(this)

openFeedback = OpenFeedback(
context = this,
openFeedbackProjectId = "am2023",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import org.koin.dsl.module
val viewModelModule = module {
viewModel { MainActivityViewModel(get(), get(), get(), get(), get(), get(), get()) }
viewModel { PartnersViewModel(get()) }
viewModel { AgendaPagerViewModel(get(), get()) }
viewModel { AgendaPagerViewModel(get(), get(), get()) }
viewModel { AgendaLayoutViewModel(get()) }
viewModel { SpeakerViewModel(get()) }
viewModel { SpeakerDetailsViewModel(get(), get()) }
viewModel { SessionDetailViewModel(get(), get(), get(), get(), get())}
viewModel { SessionDetailViewModel(get(), get(), get(), get(), get(), get())}
viewModel { VenueViewModel(get(), get()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ class MainActivity : AppCompatActivity() {
val result = auth.signInWithCredential(firebaseCredential)
// Sign in success, update UI with the signed-in user's information
lifecycleScope.launch {
viewModel._user.value = result.user?.toUser()
viewModel.syncBookmarksUseCase(auth.currentUser!!.uid)
viewModel.setUser(result.user?.toUser())
}
}
}
Expand All @@ -153,7 +152,9 @@ class MainActivity : AppCompatActivity() {
}
} catch (e: ApiException) {
e.printStackTrace()
viewModel._user.value = null
lifecycleScope.launch {
viewModel.setUser(null)
}
}
}
}
Expand All @@ -178,7 +179,7 @@ class MainActivity : AppCompatActivity() {
Firebase.auth.signOut()
googleSignInClient.signOut()
googleSignInClient.revokeAccess()
viewModel._user.emit(null)
viewModel.setUser(null)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package fr.paug.androidmakers.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import fr.androidmakers.domain.interactor.OpenCocUseCase
import fr.androidmakers.domain.interactor.OpenFaqUseCase
Expand All @@ -25,7 +24,7 @@ class MainActivityViewModel(
val openCocUseCase: OpenCocUseCase

): ViewModel() {
val _user = MutableStateFlow<User?>(null)
private val _user = MutableStateFlow<User?>(null)
val user: Flow<User?> = _user

init {
Expand All @@ -40,4 +39,11 @@ class MainActivityViewModel(
}
}
}

suspend fun setUser(user: User?) {
_user.emit(user)
user?.let {
syncBookmarksUseCase(it.id)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import at.asitplus.KmmResult
import fr.androidmakers.domain.interactor.GetPartnersUseCase
import fr.androidmakers.domain.model.PartnerGroup
import fr.androidmakers.domain.model.SpeakerId
import fr.androidmakers.domain.model.User
Expand Down Expand Up @@ -286,10 +287,10 @@ private fun AVANavHost(
}

class PartnersViewModel(
private val partnersRepository: PartnersRepository
private val getPartnersUseCase: GetPartnersUseCase
) : LceViewModel<List<PartnerGroup>>() {
override fun produce(): Flow<KmmResult<List<PartnerGroup>>> {
return partnersRepository.getPartners()
return getPartnersUseCase()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import fr.paug.androidmakers.ui.util.stringResource
fun AgendaColumn(
sessionsPerStartTime: Map<String, List<UISession>>,
onSessionClicked: (UISession) -> Unit,
onSessionBookmarked: (UISession, Boolean) -> Unit,
) {
val listState = rememberLazyListState()

Expand Down Expand Up @@ -64,9 +65,7 @@ fun AgendaColumn(
modifier = Modifier.animateItemPlacement(),
uiSession = uiSession,
onSessionClicked = onSessionClicked,
onSessionBookmarked = { session, bookmarked ->

}
onSessionBookmarked = onSessionBookmarked
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ fun AgendaPager(
}
AgendaColumn(
sessionsPerStartTime = addSeparators(LocalContext.current, items),
onSessionClicked = onSessionClicked
onSessionClicked = onSessionClicked,
onSessionBookmarked = { uiSession, bookmarked ->
viewModel.setSessionBookmark(uiSession, bookmarked)
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
package fr.paug.androidmakers.ui.components.agenda

import androidx.lifecycle.viewModelScope
import at.asitplus.KmmResult
import fr.androidmakers.domain.interactor.GetAgendaUseCase
import fr.androidmakers.domain.interactor.SyncBookmarksUseCase
import fr.androidmakers.domain.interactor.GetFavoriteSessionsUseCase
import fr.androidmakers.domain.interactor.SetSessionBookmarkUseCase
import fr.androidmakers.domain.model.Agenda
import fr.androidmakers.domain.model.Session
import fr.androidmakers.domain.repo.BookmarksRepository
import fr.paug.androidmakers.ui.model.UISession
import fr.paug.androidmakers.ui.viewmodel.LceViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

class AgendaPagerViewModel(
private val getAgendaUseCase: GetAgendaUseCase,
private val bookmarksRepository: BookmarksRepository
private val setSessionBookmarkUseCase: SetSessionBookmarkUseCase,
private val getFavoriteSessionsUseCase: GetFavoriteSessionsUseCase
) : LceViewModel<Agenda>() {
override fun produce(): Flow<KmmResult<Agenda>> {
return getAgendaUseCase()
}

fun getFavoriteSessions() = bookmarksRepository.getFavoriteSessions()
fun getFavoriteSessions() = getFavoriteSessionsUseCase()

init {
launch(false)
}

fun setSessionBookmark(uiSession: UISession, bookmark: Boolean) = viewModelScope.launch {
setSessionBookmarkUseCase(uiSession.id, bookmark)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package fr.paug.androidmakers.ui.components.session
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.auth.FirebaseAuth
import fr.androidmakers.domain.interactor.SetSessionBookmarkUseCase
import fr.androidmakers.domain.model.Session
import fr.androidmakers.domain.model.Speaker
import fr.androidmakers.domain.repo.BookmarksRepository
Expand All @@ -12,8 +12,8 @@ import fr.androidmakers.domain.repo.SessionsRepository
import fr.androidmakers.domain.repo.SpeakersRepository
import fr.paug.androidmakers.ui.viewmodel.Lce
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flattenMerge
import kotlinx.coroutines.flow.mapNotNull
Expand All @@ -24,10 +24,12 @@ import kotlinx.datetime.toInstant
@OptIn(ExperimentalCoroutinesApi::class)
class SessionDetailViewModel(
savedStateHandle: SavedStateHandle,
private val sessionsRepository: SessionsRepository,
sessionsRepository: SessionsRepository,
private val roomsRepository: RoomsRepository,
private val bookmarksRepository: BookmarksRepository,
private val speakersRepository: SpeakersRepository
// TODO remove the reference to repositories here
bookmarksRepository: BookmarksRepository,
private val speakersRepository: SpeakersRepository,
private val setSessionBookmarkUseCase: SetSessionBookmarkUseCase,
) : ViewModel() {

private val sessionId: String = savedStateHandle["sessionId"]!!
Expand Down Expand Up @@ -74,13 +76,6 @@ class SessionDetailViewModel(
}

fun bookmark(bookmarked: Boolean) = viewModelScope.launch {
bookmarksRepository.setBookmarked(sessionId, bookmarked)

val userId = FirebaseAuth.getInstance().currentUser?.uid
if (userId != null) {
GlobalScope.launch {
sessionsRepository.setBookmark(userId, sessionId, bookmarked)
}
}
setSessionBookmarkUseCase(session.first().getOrThrow().id, bookmarked)
}
}
6 changes: 4 additions & 2 deletions iosApp/RobotConf.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
45EC5A9E29C254A500ABBB7F /* GraphQLTalksProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EC5A9D29C254A500ABBB7F /* GraphQLTalksProvider.swift */; };
45EC5AC229C26DCE00ABBB7F /* GraphQLDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EC5AC129C26DCE00ABBB7F /* GraphQLDataProvider.swift */; };
45EC5AC429C2701400ABBB7F /* SessionData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45EC5AC329C2701400ABBB7F /* SessionData.swift */; };
6418BE5D2BA1EC8900F3EA79 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6418BE5C2BA1EC8900F3EA79 /* GoogleService-Info.plist */; };
6440520B2B892F0200F87201 /* ResourcesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440520A2B892F0200F87201 /* ResourcesUtils.swift */; };
647899322B90937800887B74 /* Speaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647899312B90937800887B74 /* Speaker.swift */; };
647899342B9093A000887B74 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647899332B9093A000887B74 /* Session.swift */; };
Expand Down Expand Up @@ -112,6 +113,7 @@
45EC5A9D29C254A500ABBB7F /* GraphQLTalksProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLTalksProvider.swift; sourceTree = "<group>"; };
45EC5AC129C26DCE00ABBB7F /* GraphQLDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLDataProvider.swift; sourceTree = "<group>"; };
45EC5AC329C2701400ABBB7F /* SessionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionData.swift; sourceTree = "<group>"; };
6418BE5C2BA1EC8900F3EA79 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Downloads/GoogleService-Info.plist"; sourceTree = "<group>"; };
6440520A2B892F0200F87201 /* ResourcesUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcesUtils.swift; sourceTree = "<group>"; };
647899312B90937800887B74 /* Speaker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Speaker.swift; sourceTree = "<group>"; };
647899332B9093A000887B74 /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -155,7 +157,6 @@
B2CD2767234D12500016AA02 /* SessionRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRepository.swift; sourceTree = "<group>"; };
B2CD276B234D13380016AA02 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = "<group>"; };
B2CD276D234D18450016AA02 /* AgendaDayListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgendaDayListViewModel.swift; sourceTree = "<group>"; };
B2CD42612357876C009FE6B0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
B2CD4263235789B5009FE6B0 /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = "<group>"; };
B2CD426823578FBD009FE6B0 /* DocumentSnapshot+Decodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DocumentSnapshot+Decodable.swift"; sourceTree = "<group>"; };
B2ECA77324151B6E00489874 /* OpenFeedback-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "OpenFeedback-Info.plist"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -375,7 +376,7 @@
B2F270DA23DC869300FD6823 /* .swiftlint.yml */,
B2CD273C234D06560016AA02 /* Info.plist */,
B2ECA77324151B6E00489874 /* OpenFeedback-Info.plist */,
B2CD42612357876C009FE6B0 /* GoogleService-Info.plist */,
6418BE5C2BA1EC8900F3EA79 /* GoogleService-Info.plist */,
B2CD2736234D06560016AA02 /* Preview Content */,
);
path = RobotConf;
Expand Down Expand Up @@ -597,6 +598,7 @@
B2F270DB23DC869300FD6823 /* .swiftlint.yml in Resources */,
B2CD2738234D06560016AA02 /* Preview Assets.xcassets in Resources */,
B2CD2735234D06560016AA02 /* Assets.xcassets in Resources */,
6418BE5D2BA1EC8900F3EA79 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
3 changes: 3 additions & 0 deletions iosApp/RobotConf/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import UIKit
import FirebaseAnalytics
import FirebaseCrashlytics
import Firebase
import shared

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
Expand All @@ -27,6 +28,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
print("⚠️ Firebase descriptor for the main purpose is not embedded, crashlytics disabled.")
}

DependenciesBuilder().inject(platformModules: [])

return true
}

Expand Down
40 changes: 0 additions & 40 deletions iosApp/RobotConf/Model/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,6 @@ import shared

/// Singleton model
private(set) var model = Model()
private(set) var apolloClient = ApolloClientBuilder(
url: "https://androidmakers-2023.ew.r.appspot.com/graphql",
conference: "androidmakers2023",
token: "").build()

private(set) var datastore = createDataStore(migrations: []) {
do {
let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
return documentDir.path.appending("/bookmarks.preferences_pb")
} catch {
return ""
}
}

#if DEBUG
private var mockModel = Model(dataProvider: DataProvider(desiredProviderType: .json))
Expand All @@ -32,13 +19,6 @@ func injectMockModel() {
protocol RepositoryProvider {
var sessionRepository: SessionRepository { get }
var feedbackRepository: FeedbackRepository { get }
var partnersRepository: PartnersRepository { get }
var getConferenceVenueUC: GetConferenceVenueUseCase { get }
var getAfterpartyVenueUC: GetAfterpartyVenueUseCase { get }
var bookmarksRepository: BookmarksRepository { get }
var openXAccountUC: OpenXAccountUseCase { get }
var openXHashtagUC: OpenXHashtagUseCase { get }
var openYoutubeUC: OpenYoutubeUseCase { get }
}

/// The model API object
Expand All @@ -47,30 +27,10 @@ class Model: RepositoryProvider {
let sessionRepository: SessionRepository

let feedbackRepository: FeedbackRepository
let getConferenceVenueUC: GetConferenceVenueUseCase
let getAfterpartyVenueUC: GetAfterpartyVenueUseCase
let openXAccountUC: OpenXAccountUseCase
let openXHashtagUC: OpenXHashtagUseCase
let openYoutubeUC: OpenYoutubeUseCase

let partnersRepository: PartnersRepository
let bookmarksRepository: BookmarksRepository
let urlOpener: UrlOpener

fileprivate init(dataProvider: DataProvider = DataProvider()) {
self.dataProvider = dataProvider
sessionRepository = SessionRepository(dataProvider: dataProvider)
let venueRepository = VenueGraphQLRepository(apolloClient: apolloClient)
getConferenceVenueUC = GetConferenceVenueUseCase(venueRepository: venueRepository)
getAfterpartyVenueUC = GetAfterpartyVenueUseCase(venueRepository: venueRepository)

feedbackRepository = FeedbackRepository(dataProvider: dataProvider)
partnersRepository = PartnersGraphQLRepository(apolloClient: apolloClient)

bookmarksRepository = BookmarksDataStoreRepository(dataStore: datastore)
urlOpener = UrlOpener()
openXAccountUC = OpenXAccountUseCase(urlOpener: urlOpener)
openXHashtagUC = OpenXHashtagUseCase(urlOpener: urlOpener)
openYoutubeUC = OpenYoutubeUseCase(urlOpener: urlOpener)
}
}
10 changes: 5 additions & 5 deletions iosApp/RobotConf/UI/About/AboutViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ class AboutViewModel: ObservableObject, Identifiable {

@Published var partnerGroups = [PartnerGroup]()

private var disposables = Set<AnyCancellable>()
private let deps = DepContainer()

@MainActor
func activate() async {
let partnersFlow = model.partnersRepository.getPartners()
let partnersFlow = deps.getPartnersUseCase.invoke()
for await partnerResult in partnersFlow {
partnerResult.fold(
onSuccess: { [weak self] partnerGroups in
Expand All @@ -29,15 +29,15 @@ class AboutViewModel: ObservableObject, Identifiable {
}

func openTwitterPage() {
model.openXAccountUC.invoke()
deps.openXAccountUseCase.invoke()
}

func openHashtagPage() {
model.openXHashtagUC.invoke()
deps.openXHashtagUseCase.invoke()
}

func openYoutubePage() {
model.openYoutubeUC.invoke()
deps.openYoutubeUseCase.invoke()
}

func openPartnerPage(_ partner: Partner) {
Expand Down
Loading

0 comments on commit df11777

Please sign in to comment.