Skip to content
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

Implement push notifications for each activity #67

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .firebaserc
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
}
},
"dataconnectEmulatorConfig": {}
}
}
5 changes: 4 additions & 1 deletion Data/Data/Model/AppUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public struct AppUser: Identifiable, Codable, Hashable {
public var emailId: String?
public var phoneNumber: String?
public var imageUrl: String?
public var deviceFcmToken: String?
public let loginType: LoginType
public var totalOweAmount: Double
public let isActive: Bool
Expand All @@ -34,13 +35,14 @@ public struct AppUser: Identifiable, Codable, Hashable {
}

public init(id: String, firstName: String?, lastName: String?, emailId: String?, phoneNumber: String?,
profileImageUrl: String? = nil, loginType: LoginType, totalOweAmount: Double = 0, isActive: Bool = true) {
profileImageUrl: String? = nil, deviceFcmToken: String? = nil, loginType: LoginType, totalOweAmount: Double = 0, isActive: Bool = true) {
self.id = id
self.firstName = firstName
self.lastName = lastName
self.emailId = emailId
self.phoneNumber = phoneNumber
self.imageUrl = profileImageUrl
self.deviceFcmToken = deviceFcmToken
self.loginType = loginType
self.totalOweAmount = totalOweAmount
self.isActive = isActive
Expand All @@ -53,6 +55,7 @@ public struct AppUser: Identifiable, Codable, Hashable {
case emailId = "email_id"
case phoneNumber = "phone_number"
case imageUrl = "image_url"
case deviceFcmToken = "device_fcm_token"
case loginType = "login_type"
case totalOweAmount = "total_owe_amount"
case isActive = "is_active"
Expand Down
6 changes: 4 additions & 2 deletions Data/Data/Model/Groups.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@ public struct Groups: Codable, Identifiable {

public var name: String
public var createdBy: String
public var updatedBy: String
public var imageUrl: String?
public var members: [String]
public var balances: [GroupMemberBalance]
public let createdAt: Timestamp
public var hasExpenses: Bool
public var isActive: Bool

public init(name: String, createdBy: String, imageUrl: String? = nil, members: [String],
public init(name: String, createdBy: String, updatedBy: String, imageUrl: String? = nil, members: [String],
balances: [GroupMemberBalance], createdAt: Timestamp, hasExpenses: Bool = false, isActive: Bool = true) {
self.name = name
self.createdBy = createdBy
self.updatedBy = updatedBy
self.members = members
self.balances = balances

self.imageUrl = imageUrl
self.createdAt = createdAt
self.hasExpenses = hasExpenses
Expand All @@ -37,6 +38,7 @@ public struct Groups: Codable, Identifiable {
case id
case name
case createdBy = "created_by"
case updatedBy = "updated_by"
case members
case balances
case imageUrl = "image_url"
Expand Down
5 changes: 4 additions & 1 deletion Data/Data/Model/Transaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ public struct Transactions: Codable, Hashable {
public let payerId: String
public let receiverId: String
public let addedBy: String
public var updatedBy: String
public var amount: Double
public var date: Timestamp

public init(payerId: String, receiverId: String, addedBy: String, amount: Double, date: Timestamp) {
public init(payerId: String, receiverId: String, addedBy: String, updatedBy: String, amount: Double, date: Timestamp) {
self.payerId = payerId
self.receiverId = receiverId
self.addedBy = addedBy
self.updatedBy = updatedBy
self.amount = amount
self.date = date
}
Expand All @@ -30,6 +32,7 @@ public struct Transactions: Codable, Hashable {
case payerId = "payer_id"
case receiverId = "receiver_id"
case addedBy = "added_by"
case updatedBy = "updated_by"
case amount
case date
}
Expand Down
1 change: 1 addition & 0 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ end
def splito_pods
data_pods
base_style_pods
pod 'Firebase/Messaging'
cp-nirali-s marked this conversation as resolved.
Show resolved Hide resolved
end

target 'Data' do
Expand Down
39 changes: 38 additions & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,11 @@ PODS:
- CocoaLumberjack/Core (3.8.5)
- CocoaLumberjack/Swift (3.8.5):
- CocoaLumberjack/Core
- Firebase/CoreOnly (11.0.0):
- FirebaseCore (= 11.0.0)
- Firebase/Messaging (11.0.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.0.0)
- FirebaseAppCheckInterop (11.0.0)
- FirebaseAuth (11.0.0):
- FirebaseAppCheckInterop (~> 11.0)
Expand Down Expand Up @@ -1199,6 +1204,20 @@ PODS:
- gRPC-Core (~> 1.65.0)
- leveldb-library (~> 1.22)
- nanopb (~> 3.30910.0)
- FirebaseInstallations (11.0.0):
- FirebaseCore (~> 11.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (11.0.0):
- FirebaseCore (~> 11.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Reachability (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- nanopb (~> 3.30910.0)
- FirebaseSharedSwift (11.0.0)
- FirebaseStorage (11.0.0):
- FirebaseAppCheckInterop (~> 11.0)
Expand All @@ -1207,6 +1226,9 @@ PODS:
- FirebaseCoreExtension (~> 11.0)
- GoogleUtilities/Environment (~> 8.0)
- GTMSessionFetcher/Core (~> 3.4)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleSignIn (7.1.0):
- AppAuth (< 2.0, >= 1.7.3)
- GTMAppAuth (< 5.0, >= 4.1.1)
Expand All @@ -1232,6 +1254,9 @@ PODS:
- GoogleUtilities/Reachability (8.0.2):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.0.2):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- "gRPC-C++ (1.65.4)":
- "gRPC-C++/Implementation (= 1.65.4)"
- "gRPC-C++/Interface (= 1.65.4)"
Expand Down Expand Up @@ -1333,13 +1358,15 @@ PODS:
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- PromisesObjC (2.4.0)
- RecaptchaInterop (100.0.0)
- SSZipArchive (2.5.5)
- SwiftLint (0.56.1)
- Swinject (2.9.1)

DEPENDENCIES:
- CocoaLumberjack/Swift
- Firebase/Messaging
- FirebaseAuth
- FirebaseFirestore
- FirebaseStorage
Expand All @@ -1355,6 +1382,7 @@ SPEC REPOS:
- AppAuth
- BoringSSL-GRPC
- CocoaLumberjack
- Firebase
- FirebaseAppCheckInterop
- FirebaseAuth
- FirebaseAuthInterop
Expand All @@ -1363,8 +1391,11 @@ SPEC REPOS:
- FirebaseCoreInternal
- FirebaseFirestore
- FirebaseFirestoreInternal
- FirebaseInstallations
- FirebaseMessaging
- FirebaseSharedSwift
- FirebaseStorage
- GoogleDataTransport
- GoogleSignIn
- GoogleUtilities
- "gRPC-C++"
Expand All @@ -1374,6 +1405,7 @@ SPEC REPOS:
- Kingfisher
- leveldb-library
- nanopb
- PromisesObjC
- RecaptchaInterop
- SSZipArchive
- SwiftLint
Expand All @@ -1384,6 +1416,7 @@ SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
BoringSSL-GRPC: ca6a8e5d04812fce8ffd6437810c2d46f925eaeb
CocoaLumberjack: 6a459bc897d6d80bd1b8c78482ec7ad05dffc3f0
Firebase: 9f574c08c2396885b5e7e100ed4293d956218af9
FirebaseAppCheckInterop: 6f63b3cf004e8bfd8f577e2221a95b4f0b6dc4c5
FirebaseAuth: d5cf28be74d7e82257f6a3f717509eff70d3cf4a
FirebaseAuthInterop: 45548371ea75fa7ba6c18c93cc6102bf43fb8152
Expand All @@ -1392,8 +1425,11 @@ SPEC CHECKSUMS:
FirebaseCoreInternal: cac45dbc4824e152fe93cdd5dd46bd503c3fdffb
FirebaseFirestore: a1758850668dbb503537b7780a2a1fdc5e37c6ce
FirebaseFirestoreInternal: 9fcc0ccb987ab73163f2249444e4bfd9eac63748
FirebaseInstallations: 833536e8ff1dfadd4b5f80e8dfef69e8edbd071d
FirebaseMessaging: d2d1d9c62c46dd2db49a952f7deb5b16ad2c9742
FirebaseSharedSwift: 9d0e7130af2f0cf162ecd136971f9661f2a3f769
FirebaseStorage: f9e2bf027d549db18b6195a37b31c85f56e40200
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
"gRPC-C++": 049b3605db691c85d65fbde5b0750b5f54ce9b26
Expand All @@ -1403,11 +1439,12 @@ SPEC CHECKSUMS:
Kingfisher: 53a10ea35051a436b5fb626ca2dd8f3144d755e9
leveldb-library: e8eadf9008a61f9e1dde3978c086d2b6d9b9dc28
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21
SSZipArchive: c69881e8ac5521f0e622291387add5f60f30f3c4
SwiftLint: c5fa0b7eece474d43d2178b581a1242a16267347
Swinject: a827d508c6270da03ec74e558e728917a888fa9b

PODFILE CHECKSUM: 10370f2b03f8e9ad8893964fe46dc5645d6026ac
PODFILE CHECKSUM: 80312cb782eb86cd07b7898765ab97e5c9c7ae93

COCOAPODS: 1.15.2
9 changes: 3 additions & 6 deletions Splito.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1350,13 +1350,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Splito/Splito.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution: CANOPAS SOFTWARE LLP (S985H2T7J8)";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10000000;
DEVELOPMENT_ASSET_PATHS = "\"Splito/Preview Content\"";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = S985H2T7J8;
DEVELOPMENT_TEAM = S985H2T7J8;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = NO;
Expand Down Expand Up @@ -1437,7 +1435,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.canopas.splito;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "provision-splito-distribution-2024-03-04";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
46 changes: 39 additions & 7 deletions Splito/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,59 @@ import Data
import Foundation
import FirebaseCore
import GoogleSignIn
import FirebaseMessaging
import UserNotifications
import FirebaseFirestore

class AppDelegate: NSObject, UIApplicationDelegate {
class AppDelegate: NSObject, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
addDDLoggers()
FirebaseProvider.configureFirebase()
Messaging.messaging().delegate = self
registerForPushNotifications(application: application)
return true
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance.handle(url)
private func registerForPushNotifications(application: UIApplication) {
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]

UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { (granted, _) in
guard granted else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
FirebaseProvider.auth.setAPNSToken(deviceToken, type: .sandbox)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if FirebaseProvider.auth.canHandleNotification(userInfo) {
completionHandler(.noData)
return
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
LogE("Fail to register for remote notifications with error: \(error)")
}

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let fcmToken else { return }
// Update the FCM token in Firestore for the current user
updateFCMTokenInFirestore(token: fcmToken)
}

func updateFCMTokenInFirestore(token: String) {
@Inject var preference: SplitoPreference
guard let userId = preference.user?.id else { return }

Firestore.firestore().collection("users").document(userId).setData([
"deviceFcmToken": token
], merge: true) { error in
if let error {
LogE("Error updating FCM token: \(error)")
} else {
LogI("FCM token successfully updated in Firestore")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject {
guard let userId = preference.user?.id else { return }

let memberBalance = GroupMemberBalance(id: userId, balance: 0, totalSummary: [])
let group = Groups(name: groupName.trimming(spaces: .leadingAndTrailing), createdBy: userId,
let group = Groups(name: groupName.trimming(spaces: .leadingAndTrailing), createdBy: userId, updatedBy: userId,
imageUrl: nil, members: [userId], balances: [memberBalance], createdAt: Timestamp())

let resizedImage = profileImage?.aspectFittedToHeight(200)
Expand All @@ -112,8 +112,11 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject {
}

private func updateGroup(group: Groups, completion: (Bool) -> Void) async {
guard let userId = preference.user?.id else { return }

var newGroup = group
newGroup.name = groupName.trimming(spaces: .leadingAndTrailing)
newGroup.updatedBy = userId

let resizedImage = profileImage?.aspectFittedToHeight(200)
let imageData = resizedImage?.jpegData(compressionQuality: 0.2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class GroupPaymentViewModel: BaseViewModel, ObservableObject {
await updateTransaction(transaction: newTransaction, oldTransaction: transaction, completion: completion)
} else {
let transaction = Transactions(payerId: payerId, receiverId: receiverId, addedBy: userId,
amount: amount, date: .init(date: paymentDate))
updatedBy: userId, amount: amount, date: .init(date: paymentDate))
await addTransaction(transaction: transaction, completion: completion)
}
}
Expand All @@ -154,11 +154,16 @@ class GroupPaymentViewModel: BaseViewModel, ObservableObject {
}

private func updateTransaction(transaction: Transactions, oldTransaction: Transactions, completion: (Bool) -> Void) async {
guard let userId = preference.user?.id else { return }

do {
showLoader = true
try await transactionRepository.updateTransaction(groupId: groupId, transaction: transaction)
await updateGroupMemberBalance(transaction: transaction, updateType: .Update(oldTransaction: oldTransaction))
NotificationCenter.default.post(name: .updateTransaction, object: transaction)
var newTransaction = transaction
newTransaction.updatedBy = userId

try await transactionRepository.updateTransaction(groupId: groupId, transaction: newTransaction)
await updateGroupMemberBalance(transaction: newTransaction, updateType: .Update(oldTransaction: oldTransaction))
NotificationCenter.default.post(name: .updateTransaction, object: newTransaction)
showLoader = false
completion(true)
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,13 @@ class GroupSettingViewModel: BaseViewModel, ObservableObject {
}

private func deleteGroup() async {
guard let group else { return }
guard let group, let userId = preference.user?.id else { return }
do {
currentViewState = .loading
try await groupRepository.deleteGroup(group: group)
NotificationCenter.default.post(name: .deleteGroup, object: group)
var deletedGroup = group
deletedGroup.updatedBy = userId
try await groupRepository.deleteGroup(group: deletedGroup)
NotificationCenter.default.post(name: .deleteGroup, object: deletedGroup)
currentViewState = .initial
goBackToGroupList()
} catch {
Expand Down
Loading
Loading