From 6fdaacf222ee3b2522b9ce311db46bc90885a641 Mon Sep 17 00:00:00 2001 From: sarveshsharma Date: Sun, 15 Dec 2024 13:10:23 +0530 Subject: [PATCH 1/4] build: Adds video id flow --- iosApp/Generated/SwiftBridgeCore.swift | 4 +- .../yral-mobile-swift-binding.h | 31 +- .../yral-mobile-swift-binding.swift | 378 +++++++++--------- .../xcshareddata/swiftpm/Package.resolved | 6 +- .../xcschemes/iosAppStaging.xcscheme | 7 + iosApp/iosApp/ContentView.swift | 52 ++- .../individual_user_template_ffi.rs | 12 +- .../individual_user_template_helper.rs | 26 +- .../src/individual_user_template/mod.rs | 37 +- 9 files changed, 327 insertions(+), 226 deletions(-) diff --git a/iosApp/Generated/SwiftBridgeCore.swift b/iosApp/Generated/SwiftBridgeCore.swift index ab78e1f..dc2e498 100644 --- a/iosApp/Generated/SwiftBridgeCore.swift +++ b/iosApp/Generated/SwiftBridgeCore.swift @@ -1,4 +1,4 @@ -// swiftlint:disable all +// swiftlint: disable all import Foundation extension RustString { @@ -1321,4 +1321,4 @@ extension Optional where Wrapped == Bool { __private__OptionBool(self) } } -// swiftlint:enable all +// swiftlint: enable all diff --git a/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.h b/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.h index 907d8d5..a0c5ff4 100644 --- a/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.h +++ b/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.h @@ -732,18 +732,6 @@ void* __swift_bridge__$Vec_Result11$get_mut(void* vec_ptr, uintptr_t index); uintptr_t __swift_bridge__$Vec_Result11$len(void* vec_ptr); void* __swift_bridge__$Vec_Result11$as_ptr(void* vec_ptr); -typedef struct PostDetailsForFrontend PostDetailsForFrontend; -void __swift_bridge__$PostDetailsForFrontend$_free(void* self); - -void* __swift_bridge__$Vec_PostDetailsForFrontend$new(void); -void __swift_bridge__$Vec_PostDetailsForFrontend$drop(void* vec_ptr); -void __swift_bridge__$Vec_PostDetailsForFrontend$push(void* vec_ptr, void* item_ptr); -void* __swift_bridge__$Vec_PostDetailsForFrontend$pop(void* vec_ptr); -void* __swift_bridge__$Vec_PostDetailsForFrontend$get(void* vec_ptr, uintptr_t index); -void* __swift_bridge__$Vec_PostDetailsForFrontend$get_mut(void* vec_ptr, uintptr_t index); -uintptr_t __swift_bridge__$Vec_PostDetailsForFrontend$len(void* vec_ptr); -void* __swift_bridge__$Vec_PostDetailsForFrontend$as_ptr(void* vec_ptr); - typedef struct PlacedBetDetailResult PlacedBetDetailResult; void __swift_bridge__$PlacedBetDetailResult$_free(void* self); @@ -1464,6 +1452,18 @@ void* __swift_bridge__$Vec_Result_$get_mut(void* vec_ptr, uintptr_t index); uintptr_t __swift_bridge__$Vec_Result_$len(void* vec_ptr); void* __swift_bridge__$Vec_Result_$as_ptr(void* vec_ptr); +typedef struct PostDetailsForFrontend PostDetailsForFrontend; +void __swift_bridge__$PostDetailsForFrontend$_free(void* self); + +void* __swift_bridge__$Vec_PostDetailsForFrontend$new(void); +void __swift_bridge__$Vec_PostDetailsForFrontend$drop(void* vec_ptr); +void __swift_bridge__$Vec_PostDetailsForFrontend$push(void* vec_ptr, void* item_ptr); +void* __swift_bridge__$Vec_PostDetailsForFrontend$pop(void* vec_ptr); +void* __swift_bridge__$Vec_PostDetailsForFrontend$get(void* vec_ptr, uintptr_t index); +void* __swift_bridge__$Vec_PostDetailsForFrontend$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_PostDetailsForFrontend$len(void* vec_ptr); +void* __swift_bridge__$Vec_PostDetailsForFrontend$as_ptr(void* vec_ptr); + typedef struct Service Service; void __swift_bridge__$Service$_free(void* self); @@ -1500,6 +1500,7 @@ void* __swift_bridge__$Vec_CanistersWrapper$get_mut(void* vec_ptr, uintptr_t ind uintptr_t __swift_bridge__$Vec_CanistersWrapper$len(void* vec_ptr); void* __swift_bridge__$Vec_CanistersWrapper$as_ptr(void* vec_ptr); +struct RustStr __swift_bridge__$PostDetailsForFrontend$video_uid(void* self); struct __private__ResultPtrAndPtr __swift_bridge__$Service$new(void* principal, void* identity); void __swift_bridge__$Service$add_device_id(void* callback_wrapper, void __swift_bridge__$Service$add_device_id$async(void* callback_wrapper, struct __private__ResultPtrAndPtr ret), void* self, void* arg0); void __swift_bridge__$Service$add_post_v_2(void* callback_wrapper, void __swift_bridge__$Service$add_post_v_2$async(void* callback_wrapper, struct __private__ResultPtrAndPtr ret), void* self, void* arg0); @@ -1582,9 +1583,11 @@ struct __private__ResultPtrAndPtr __swift_bridge__$get_jwk_ec_key(void* json_str struct __private__ResultPtrAndPtr __swift_bridge__$delegated_identity_from_bytes(struct __private__FfiSlice data); struct __private__ResultPtrAndPtr __swift_bridge__$delegated_identity_wire_from_bytes(struct __private__FfiSlice data); void __swift_bridge__$authenticate_with_network(void* callback_wrapper, void __swift_bridge__$authenticate_with_network$async(void* callback_wrapper, struct __private__ResultPtrAndPtr ret), void* auth, void* referrer); -void* __swift_bridge__$get_canister_principal(void* wrapper); -void* __swift_bridge__$get_user_principal(void* wrapper); +void* __swift_bridge__$CanistersWrapper$get_canister_principal(void* self); +void* __swift_bridge__$CanistersWrapper$get_canister_principal_string(void* self); +void* __swift_bridge__$CanistersWrapper$get_user_principal(void* self); struct __private__OptionU64 __swift_bridge__$extract_time_as_double(void* result); +struct __private__ResultPtrAndPtr __swift_bridge__$get_principal(void* text); typedef enum __swift_bridge__$ResultU32AndAgentError$Tag {__swift_bridge__$ResultU32AndAgentError$ResultOk, __swift_bridge__$ResultU32AndAgentError$ResultErr} __swift_bridge__$ResultU32AndAgentError$Tag; union __swift_bridge__$ResultU32AndAgentError$Fields {uint32_t ok; void* err;}; typedef struct __swift_bridge__$ResultU32AndAgentError{__swift_bridge__$ResultU32AndAgentError$Tag tag; union __swift_bridge__$ResultU32AndAgentError$Fields payload;} __swift_bridge__$ResultU32AndAgentError; diff --git a/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.swift b/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.swift index a850946..374d2bd 100644 --- a/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.swift +++ b/iosApp/Generated/yral-mobile-swift-binding/yral-mobile-swift-binding.swift @@ -1,4 +1,4 @@ -// swiftlint:disable all +// swiftlint: disable all public func get_secp256k1_identity(_ jwk_key: JwkEcKey) throws -> Secp256k1Identity { try { let val = __swift_bridge__$get_secp256k1_identity({jwk_key.isOwned = false; return jwk_key.ptr;}()); if val.is_ok { return Secp256k1Identity(ptr: val.ok_or_err!) } else { throw Secp256k1Error(ptr: val.ok_or_err!) } }() } @@ -39,15 +39,12 @@ class CbWrapper$authenticate_with_network { self.cb = cb } } -public func get_canister_principal(_ wrapper: CanistersWrapper) -> Principal { - Principal(ptr: __swift_bridge__$get_canister_principal({wrapper.isOwned = false; return wrapper.ptr;}())) -} -public func get_user_principal(_ wrapper: CanistersWrapper) -> Principal { - Principal(ptr: __swift_bridge__$get_user_principal({wrapper.isOwned = false; return wrapper.ptr;}())) -} public func extract_time_as_double(_ result: Result11) -> Optional { __swift_bridge__$extract_time_as_double({result.isOwned = false; return result.ptr;}()).intoSwiftRepr() } +public func get_principal(_ text: GenericIntoRustString) throws -> Principal { + try { let val = __swift_bridge__$get_principal({ let rustString = text.intoRustString(); rustString.isOwned = false; return rustString.ptr }()); if val.is_ok { return Principal(ptr: val.ok_or_err!) } else { throw PrincipalError(ptr: val.ok_or_err!) } }() +} public class KeyValuePair: KeyValuePairRefMut { var isOwned: Bool = true @@ -4624,81 +4621,6 @@ extension Result11: Vectorizable { } -public class PostDetailsForFrontend: PostDetailsForFrontendRefMut { - var isOwned: Bool = true - - public override init(ptr: UnsafeMutableRawPointer) { - super.init(ptr: ptr) - } - - deinit { - if isOwned { - __swift_bridge__$PostDetailsForFrontend$_free(ptr) - } - } -} -public class PostDetailsForFrontendRefMut: PostDetailsForFrontendRef { - public override init(ptr: UnsafeMutableRawPointer) { - super.init(ptr: ptr) - } -} -public class PostDetailsForFrontendRef { - var ptr: UnsafeMutableRawPointer - - public init(ptr: UnsafeMutableRawPointer) { - self.ptr = ptr - } -} -extension PostDetailsForFrontend: Vectorizable { - public static func vecOfSelfNew() -> UnsafeMutableRawPointer { - __swift_bridge__$Vec_PostDetailsForFrontend$new() - } - - public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { - __swift_bridge__$Vec_PostDetailsForFrontend$drop(vecPtr) - } - - public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: PostDetailsForFrontend) { - __swift_bridge__$Vec_PostDetailsForFrontend$push(vecPtr, {value.isOwned = false; return value.ptr;}()) - } - - public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { - let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$pop(vecPtr) - if pointer == nil { - return nil - } else { - return (PostDetailsForFrontend(ptr: pointer!) as! Self) - } - } - - public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { - let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$get(vecPtr, index) - if pointer == nil { - return nil - } else { - return PostDetailsForFrontendRef(ptr: pointer!) - } - } - - public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { - let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$get_mut(vecPtr, index) - if pointer == nil { - return nil - } else { - return PostDetailsForFrontendRefMut(ptr: pointer!) - } - } - - public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { - UnsafePointer(OpaquePointer(__swift_bridge__$Vec_PostDetailsForFrontend$as_ptr(vecPtr))) - } - - public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { - __swift_bridge__$Vec_PostDetailsForFrontend$len(vecPtr) - } -} - - public class PlacedBetDetailResult: PlacedBetDetailResultRefMut { var isOwned: Bool = true @@ -9199,6 +9121,86 @@ extension Result_: Vectorizable { } +public class PostDetailsForFrontend: PostDetailsForFrontendRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$PostDetailsForFrontend$_free(ptr) + } + } +} +public class PostDetailsForFrontendRefMut: PostDetailsForFrontendRef { + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } +} +public class PostDetailsForFrontendRef { + var ptr: UnsafeMutableRawPointer + + public init(ptr: UnsafeMutableRawPointer) { + self.ptr = ptr + } +} +extension PostDetailsForFrontendRef { + public func video_uid() -> RustStr { + __swift_bridge__$PostDetailsForFrontend$video_uid(ptr) + } +} +extension PostDetailsForFrontend: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_PostDetailsForFrontend$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_PostDetailsForFrontend$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: PostDetailsForFrontend) { + __swift_bridge__$Vec_PostDetailsForFrontend$push(vecPtr, {value.isOwned = false; return value.ptr;}()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$pop(vecPtr) + if pointer == nil { + return nil + } else { + return (PostDetailsForFrontend(ptr: pointer!) as! Self) + } + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$get(vecPtr, index) + if pointer == nil { + return nil + } else { + return PostDetailsForFrontendRef(ptr: pointer!) + } + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let pointer = __swift_bridge__$Vec_PostDetailsForFrontend$get_mut(vecPtr, index) + if pointer == nil { + return nil + } else { + return PostDetailsForFrontendRefMut(ptr: pointer!) + } + } + + public static func vecOfSelfAsPtr(vecPtr: UnsafeMutableRawPointer) -> UnsafePointer { + UnsafePointer(OpaquePointer(__swift_bridge__$Vec_PostDetailsForFrontend$as_ptr(vecPtr))) + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_PostDetailsForFrontend$len(vecPtr) + } +} + + public class Service: ServiceRefMut { var isOwned: Bool = true @@ -9214,13 +9216,13 @@ public class Service: ServiceRefMut { } extension Service { public convenience init(_ principal: Principal, _ identity: DelegatedIdentity) throws { - let val = try __swift_bridge__$Service$new( - { principal.isOwned = false; return principal.ptr; }(), - { identity.isOwned = false; return identity.ptr; }() + let val = __swift_bridge__$Service$new( + { principal.isOwned = false; return principal.ptr }(), + { identity.isOwned = false; return identity.ptr }() ) if val.is_ok { - self.init(ptr: val.ok_or_err!) // Correctly call self.init at the top level + self.init(ptr: val.ok_or_err!) } else { throw PrincipalError(ptr: val.ok_or_err!) } @@ -11589,6 +11591,19 @@ public class CanistersWrapperRef { self.ptr = ptr } } +extension CanistersWrapperRef { + public func get_canister_principal() -> Principal { + Principal(ptr: __swift_bridge__$CanistersWrapper$get_canister_principal(ptr)) + } + + public func get_canister_principal_string() -> RustString { + RustString(ptr: __swift_bridge__$CanistersWrapper$get_canister_principal_string(ptr)) + } + + public func get_user_principal() -> Principal { + Principal(ptr: __swift_bridge__$CanistersWrapper$get_user_principal(ptr)) + } +} extension CanistersWrapper: Vectorizable { public static func vecOfSelfNew() -> UnsafeMutableRawPointer { __swift_bridge__$Vec_CanistersWrapper$new() @@ -21865,6 +21880,102 @@ extension ServiceRef { } } + + + + + + + +extension ServiceRef { + public func get_account_transactions(_ arg0: GetAccountTransactionsArgs) async throws -> GetTransactionsResult { + func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { + let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() + if rustFnRetVal.is_ok { + wrapper.cb(.success(GetTransactionsResult(ptr: rustFnRetVal.ok_or_err!))) + } else { + wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) + } + } + + return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation) in + let callback = { rustFnRetVal in + continuation.resume(with: rustFnRetVal) + } + + let wrapper = CbWrapper$Service$get_account_transactions(cb: callback) + let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() + + __swift_bridge__$Service$get_account_transactions(wrapperPtr, onComplete, ptr, {arg0.isOwned = false; return arg0.ptr;}()) + }) + } + class CbWrapper$Service$get_account_transactions { + var cb: (Result) -> () + + public init(cb: @escaping (Result) -> ()) { + self.cb = cb + } + } + + public func ledger_id() async throws -> Principal { + func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { + let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() + if rustFnRetVal.is_ok { + wrapper.cb(.success(Principal(ptr: rustFnRetVal.ok_or_err!))) + } else { + wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) + } + } + + return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation) in + let callback = { rustFnRetVal in + continuation.resume(with: rustFnRetVal) + } + + let wrapper = CbWrapper$Service$ledger_id(cb: callback) + let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() + + __swift_bridge__$Service$ledger_id(wrapperPtr, onComplete, ptr) + }) + } + class CbWrapper$Service$ledger_id { + var cb: (Result) -> () + + public init(cb: @escaping (Result) -> ()) { + self.cb = cb + } + } + + public func list_subaccounts(_ arg0: ListSubaccountsArgs) async throws -> RustVec { + func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { + let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() + if rustFnRetVal.is_ok { + wrapper.cb(.success(RustVec(ptr: rustFnRetVal.ok_or_err!))) + } else { + wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) + } + } + + return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation, Error>) in + let callback = { rustFnRetVal in + continuation.resume(with: rustFnRetVal) + } + + let wrapper = CbWrapper$Service$list_subaccounts(cb: callback) + let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() + + __swift_bridge__$Service$list_subaccounts(wrapperPtr, onComplete, ptr, {arg0.isOwned = false; return arg0.ptr;}()) + }) + } + class CbWrapper$Service$list_subaccounts { + var cb: (Result, Error>) -> () + + public init(cb: @escaping (Result, Error>) -> ()) { + self.cb = cb + } + } +} + public class ListSubaccountsArgs: ListSubaccountsArgsRefMut { var isOwned: Bool = true @@ -22916,99 +23027,6 @@ extension InitArgs: Vectorizable { } } - - -extension ServiceRef { - public func get_account_transactions(_ arg0: GetAccountTransactionsArgs) async throws -> GetTransactionsResult { - func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { - let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() - if rustFnRetVal.is_ok { - wrapper.cb(.success(GetTransactionsResult(ptr: rustFnRetVal.ok_or_err!))) - } else { - wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) - } - } - - return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation) in - let callback = { rustFnRetVal in - continuation.resume(with: rustFnRetVal) - } - - let wrapper = CbWrapper$Service$get_account_transactions(cb: callback) - let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() - - __swift_bridge__$Service$get_account_transactions(wrapperPtr, onComplete, ptr, {arg0.isOwned = false; return arg0.ptr;}()) - }) - } - class CbWrapper$Service$get_account_transactions { - var cb: (Result) -> () - - public init(cb: @escaping (Result) -> ()) { - self.cb = cb - } - } - - public func ledger_id() async throws -> Principal { - func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { - let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() - if rustFnRetVal.is_ok { - wrapper.cb(.success(Principal(ptr: rustFnRetVal.ok_or_err!))) - } else { - wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) - } - } - - return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation) in - let callback = { rustFnRetVal in - continuation.resume(with: rustFnRetVal) - } - - let wrapper = CbWrapper$Service$ledger_id(cb: callback) - let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() - - __swift_bridge__$Service$ledger_id(wrapperPtr, onComplete, ptr) - }) - } - class CbWrapper$Service$ledger_id { - var cb: (Result) -> () - - public init(cb: @escaping (Result) -> ()) { - self.cb = cb - } - } - - public func list_subaccounts(_ arg0: ListSubaccountsArgs) async throws -> RustVec { - func onComplete(cbWrapperPtr: UnsafeMutableRawPointer?, rustFnRetVal: __private__ResultPtrAndPtr) { - let wrapper = Unmanaged.fromOpaque(cbWrapperPtr!).takeRetainedValue() - if rustFnRetVal.is_ok { - wrapper.cb(.success(RustVec(ptr: rustFnRetVal.ok_or_err!))) - } else { - wrapper.cb(.failure(AgentError(ptr: rustFnRetVal.ok_or_err!))) - } - } - - return try await withCheckedThrowingContinuation({ (continuation: CheckedContinuation, Error>) in - let callback = { rustFnRetVal in - continuation.resume(with: rustFnRetVal) - } - - let wrapper = CbWrapper$Service$list_subaccounts(cb: callback) - let wrapperPtr = Unmanaged.passRetained(wrapper).toOpaque() - - __swift_bridge__$Service$list_subaccounts(wrapperPtr, onComplete, ptr, {arg0.isOwned = false; return arg0.ptr;}()) - }) - } - class CbWrapper$Service$list_subaccounts { - var cb: (Result, Error>) -> () - - public init(cb: @escaping (Result, Error>) -> ()) { - self.cb = cb - } - } -} - - - public class Icrc3DataCertificateResult: Icrc3DataCertificateResultRefMut { var isOwned: Bool = true @@ -34496,4 +34514,4 @@ extension ServiceRef { } } } -// swiftlint:enable all +// swiftlint: enable all diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d7d8d0d..07f78ba 100644 --- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "a0965f4e9b93edf9c178ef89aced2538f8fa62f7e25153cc3136373d5a8ed2b1", + "originHash" : "e512d8ad12b4c625cf0559a56dc31a69996053bb018c5fefedea83a18fa9e77e", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/firebase-ios-sdk.git", "state" : { - "revision" : "2e02253fd1ce99145bcbf1bb367ccf61bd0ca46b", - "version" : "11.6.0" + "revision" : "dbdfdc44bee8b8e4eaa5ec27eb12b9338f3f2bc1", + "version" : "11.5.0" } }, { diff --git a/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosAppStaging.xcscheme b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosAppStaging.xcscheme index 8ecf1cb..670ab42 100644 --- a/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosAppStaging.xcscheme +++ b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosAppStaging.xcscheme @@ -73,6 +73,13 @@ ReferencedContainer = "container:iosApp.xcodeproj"> + + + + &str; + } extern "Rust" { type Service; #[swift_bridge(init)] @@ -404,11 +408,13 @@ mod ffi { referrer: Option, ) -> Result; - fn get_canister_principal(wrapper: CanistersWrapper) -> Principal; - fn get_user_principal(wrapper: CanistersWrapper) -> Principal; + fn get_canister_principal(&self) -> Principal; + fn get_canister_principal_string(&self) -> String; + fn get_user_principal(&self) -> Principal; } extern "Rust" { fn extract_time_as_double(result: Result11) -> Option; + fn get_principal(text: String) -> Result; } } diff --git a/rust-agent/src/individual_user_template/individual_user_template_helper.rs b/rust-agent/src/individual_user_template/individual_user_template_helper.rs index 8329192..5d8778a 100644 --- a/rust-agent/src/individual_user_template/individual_user_template_helper.rs +++ b/rust-agent/src/individual_user_template/individual_user_template_helper.rs @@ -64,6 +64,20 @@ pub struct CanistersWrapper { inner: Canisters, } +impl CanistersWrapper { + pub fn get_canister_principal( &self) -> Principal { + return self.inner.user_canister(); + } + + pub fn get_canister_principal_string(&self) -> String { + return self.inner.user_canister().to_string(); + } + + pub fn get_user_principal(&self) -> Principal { + return self.inner.user_principal(); + } +} + pub async fn authenticate_with_network( auth: DelegatedIdentityWire, referrer: Option, @@ -74,17 +88,13 @@ pub async fn authenticate_with_network( Ok(CanistersWrapper { inner: canisters }) } -pub fn get_canister_principal(wrapper: CanistersWrapper) -> Principal { - return wrapper.inner.user_canister(); -} - -pub fn get_user_principal(wrapper: CanistersWrapper) -> Principal { - return wrapper.inner.user_principal(); -} - pub fn extract_time_as_double(result: Result11) -> Option { match result { Result11::Ok(system_time) => Some(system_time.secs_since_epoch), Result11::Err(_) => None, } } + +pub fn get_principal(text: String) -> std::result::Result { + Principal::from_text(text) +} diff --git a/rust-agent/src/individual_user_template/mod.rs b/rust-agent/src/individual_user_template/mod.rs index feeb8f3..b79f3de 100644 --- a/rust-agent/src/individual_user_template/mod.rs +++ b/rust-agent/src/individual_user_template/mod.rs @@ -833,20 +833,19 @@ pub struct Service { } impl Service { - pub fn new( - principal: Principal, - identity: DelegatedIdentity, - ) -> std::result::Result { + pub fn new(principal: Principal, identity: DelegatedIdentity) -> std::result::Result { let agent = Agent::builder() .with_url("https://ic0.app/") .with_identity(identity) .build() .expect("Failed to create agent"); - RUNTIME + + RUNTIME .block_on(agent.fetch_root_key()) .expect("Failed to fetch root key"); - Ok(Self { - principal, + + Ok(Service { + principal: principal, agent: Arc::new(agent), }) } @@ -1082,13 +1081,31 @@ impl Service { arg0: u64, ) -> Result { let args = Encode!(&arg0)?; - let bytes = self + let call_result = self .agent .query(&self.principal, "get_individual_post_details_by_id") .with_arg(args) .call() - .await?; - Ok(Decode!(&bytes, PostDetailsForFrontend)?) + .await; + + match call_result { + Ok(bytes) => { + // Decode the bytes if the call succeeded + eprintln!("Raw bytes: {:?}", bytes); + match Decode!(&bytes, PostDetailsForFrontend) { + Ok(details) => Ok(details), + Err(e) => { + eprintln!("Failed to decode PostDetailsForFrontend: {:?}", e); + Err(e.into()) + } + } + } + Err(e) => { + // Log the error here + eprintln!("Error calling get_individual_post_details_by_id: {:?}", e); + Err(e.into()) + } + } } pub async fn get_last_access_time(&self) -> Result { let args = Encode!()?; From 3272ce9b88ecb5e1bf8b95d73f455bc6b35bc361 Mon Sep 17 00:00:00 2001 From: sarveshsharma Date: Sun, 15 Dec 2024 17:21:47 +0530 Subject: [PATCH 2/4] build: Adds auth module and feed repository --- iosApp/iosApp.xcodeproj/project.pbxproj | 183 ++++++++++++++++-- .../iosApp/DIContainer/AppDIContainer.swift | 36 ++++ .../iosApp/DIContainer/FeedDIContainer.swift | 21 ++ .../Extensions/Numbers+Extensions.swift | 27 +++ .../Feed/Domain/Entities/FeedQuery.swift | 16 ++ .../Feed/Domain/Entities/FeedResult.swift | 13 ++ .../Interfaces/FeedRepositoryProtocol.swift | 11 ++ .../Infrastructure/AppConfiguration.swift | 13 ++ .../Network/Auth/AuthClient.swift | 113 +++++++++++ .../Network/Auth/AuthConstants.swift | 18 ++ .../Network/Auth/AuthEndPoints.swift | 34 ++++ .../Infrastructure/Video/YralPlayer.swift | 10 + 12 files changed, 483 insertions(+), 12 deletions(-) create mode 100644 iosApp/iosApp/DIContainer/AppDIContainer.swift create mode 100644 iosApp/iosApp/DIContainer/FeedDIContainer.swift create mode 100644 iosApp/iosApp/Extensions/Numbers+Extensions.swift create mode 100644 iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift create mode 100644 iosApp/iosApp/Feed/Domain/Entities/FeedResult.swift create mode 100644 iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift create mode 100644 iosApp/iosApp/Infrastructure/AppConfiguration.swift create mode 100644 iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift create mode 100644 iosApp/iosApp/Infrastructure/Network/Auth/AuthConstants.swift create mode 100644 iosApp/iosApp/Infrastructure/Network/Auth/AuthEndPoints.swift create mode 100644 iosApp/iosApp/Infrastructure/Video/YralPlayer.swift diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 3f48e93..f77ba6d 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 70; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -31,6 +31,36 @@ AC6BECC22D09F91C002E2021 /* HTTPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC6BECC12D09F917002E2021 /* HTTPService.swift */; }; AC6BECC32D09F91C002E2021 /* HTTPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC6BECC12D09F917002E2021 /* HTTPService.swift */; }; AC6CBA802CE46CA6002454F8 /* libyral_mobile_swift_binding.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AC09A9F72CDB78B9009A39A4 /* libyral_mobile_swift_binding.a */; }; + ACB46A262D0EC0A3002E200C /* YralPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A252D0EC09C002E200C /* YralPlayer.swift */; }; + ACB46A272D0EC0A3002E200C /* YralPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A252D0EC09C002E200C /* YralPlayer.swift */; }; + ACB46A292D0EC76A002E200C /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A282D0EC765002E200C /* AppConfiguration.swift */; }; + ACB46A2A2D0EC76A002E200C /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A282D0EC765002E200C /* AppConfiguration.swift */; }; + ACB46A2C2D0EC833002E200C /* AppDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A2B2D0EC82F002E200C /* AppDIContainer.swift */; }; + ACB46A2D2D0EC833002E200C /* AppDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A2B2D0EC82F002E200C /* AppDIContainer.swift */; }; + ACB46A332D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A322D0EC8F9002E200C /* Numbers+Extensions.swift */; }; + ACB46A342D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A322D0EC8F9002E200C /* Numbers+Extensions.swift */; }; + ACB46A362D0EC998002E200C /* FeedDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A352D0EC991002E200C /* FeedDIContainer.swift */; }; + ACB46A372D0EC998002E200C /* FeedDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A352D0EC991002E200C /* FeedDIContainer.swift */; }; + ACB46A3F2D0ECBB3002E200C /* ml_feed.proto in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A392D0ECBB3002E200C /* ml_feed.proto */; }; + ACB46A402D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A3A2D0ECBB3002E200C /* ml_feed.grpc.swift */; }; + ACB46A412D0ECBB3002E200C /* ml_feed.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A3B2D0ECBB3002E200C /* ml_feed.pb.swift */; }; + ACB46A422D0ECBB3002E200C /* ml_feed.proto in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A392D0ECBB3002E200C /* ml_feed.proto */; }; + ACB46A432D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A3A2D0ECBB3002E200C /* ml_feed.grpc.swift */; }; + ACB46A442D0ECBB3002E200C /* ml_feed.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A3B2D0ECBB3002E200C /* ml_feed.pb.swift */; }; + ACB46A472D0ECBD5002E200C /* FeedsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A462D0ECBD1002E200C /* FeedsRepository.swift */; }; + ACB46A482D0ECBD5002E200C /* FeedsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A462D0ECBD1002E200C /* FeedsRepository.swift */; }; + ACB46A4C2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A4B2D0ED100002E200C /* FeedRepositoryProtocol.swift */; }; + ACB46A4D2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A4B2D0ED100002E200C /* FeedRepositoryProtocol.swift */; }; + ACB46A502D0ED195002E200C /* FeedResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A4F2D0ED18B002E200C /* FeedResult.swift */; }; + ACB46A512D0ED195002E200C /* FeedResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A4F2D0ED18B002E200C /* FeedResult.swift */; }; + ACB46A532D0ED22B002E200C /* FeedQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A522D0ED222002E200C /* FeedQuery.swift */; }; + ACB46A542D0ED22B002E200C /* FeedQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A522D0ED222002E200C /* FeedQuery.swift */; }; + ACB46A5A2D0EF91F002E200C /* AuthConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A592D0EF91A002E200C /* AuthConstants.swift */; }; + ACB46A5B2D0EF91F002E200C /* AuthConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A592D0EF91A002E200C /* AuthConstants.swift */; }; + ACB46A5D2D0EF92D002E200C /* AuthEndPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */; }; + ACB46A5E2D0EF92D002E200C /* AuthEndPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */; }; + ACB46A602D0EF94B002E200C /* AuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5F2D0EF947002E200C /* AuthClient.swift */; }; + ACB46A612D0EF94B002E200C /* AuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5F2D0EF947002E200C /* AuthClient.swift */; }; ACB815372CF9D4FF00104B7B /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = ACB815342CF9CE0800104B7B /* GoogleService-Info.plist */; }; ACB815482CFA146800104B7B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = ACB815472CFA146800104B7B /* FirebaseCore */; }; ACD941802CFF55BB0038FCB6 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */ = {isa = PBXBuildFile; productRef = ACD9417F2CFF55BB0038FCB6 /* FirebaseAnalyticsWithoutAdIdSupport */; }; @@ -74,6 +104,21 @@ AC6BECBB2D09F685002E2021 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; AC6BECBE2D09F866002E2021 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; }; AC6BECC12D09F917002E2021 /* HTTPService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPService.swift; sourceTree = ""; }; + ACB46A252D0EC09C002E200C /* YralPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YralPlayer.swift; sourceTree = ""; }; + ACB46A282D0EC765002E200C /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = ""; }; + ACB46A2B2D0EC82F002E200C /* AppDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDIContainer.swift; sourceTree = ""; }; + ACB46A322D0EC8F9002E200C /* Numbers+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Numbers+Extensions.swift"; sourceTree = ""; }; + ACB46A352D0EC991002E200C /* FeedDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedDIContainer.swift; sourceTree = ""; }; + ACB46A392D0ECBB3002E200C /* ml_feed.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = ml_feed.proto; sourceTree = ""; }; + ACB46A3A2D0ECBB3002E200C /* ml_feed.grpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ml_feed.grpc.swift; sourceTree = ""; }; + ACB46A3B2D0ECBB3002E200C /* ml_feed.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ml_feed.pb.swift; sourceTree = ""; }; + ACB46A462D0ECBD1002E200C /* FeedsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsRepository.swift; sourceTree = ""; }; + ACB46A4B2D0ED100002E200C /* FeedRepositoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedRepositoryProtocol.swift; sourceTree = ""; }; + ACB46A4F2D0ED18B002E200C /* FeedResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedResult.swift; sourceTree = ""; }; + ACB46A522D0ED222002E200C /* FeedQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedQuery.swift; sourceTree = ""; }; + ACB46A592D0EF91A002E200C /* AuthConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthConstants.swift; sourceTree = ""; }; + ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthEndPoints.swift; sourceTree = ""; }; + ACB46A5F2D0EF947002E200C /* AuthClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthClient.swift; sourceTree = ""; }; ACB815342CF9CE0800104B7B /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; ACD941B82CFF575C0038FCB6 /* iosAppStaging.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosAppStaging.app; sourceTree = BUILT_PRODUCTS_DIR; }; ACD941B92CFF575C0038FCB6 /* Info-Staging.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-Staging.plist"; path = "/Users/sarveshsharma/Desktop/yral-mobile/iosApp/Staging/Info-Staging.plist"; sourceTree = ""; }; @@ -81,10 +126,6 @@ ACD941BE2CFF58B10038FCB6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ -/* Begin PBXFileSystemSynchronizedRootGroup section */ - ACEBE9982D0C4AE90056F033 /* Data */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Data; sourceTree = ""; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - /* Begin PBXFrameworksBuildPhase section */ 7555FF78242A565900829871 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -159,6 +200,7 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( + ACB46A2E2D0EC838002E200C /* DIContainer */, ACEBE9962D0C4AA50056F033 /* Feed */, AC6BECB92D09F0F0002E2021 /* Infrastructure */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, @@ -201,6 +243,7 @@ AC09AA1E2CDBB69B009A39A4 /* Extensions */ = { isa = PBXGroup; children = ( + ACB46A322D0EC8F9002E200C /* Numbers+Extensions.swift */, AC09AA732CDD2107009A39A4 /* Data+Extensions.swift */, AC09A9F92CDB7A5D009A39A4 /* SwiftBindings+Extensions.swift */, ); @@ -210,6 +253,8 @@ AC6BECB92D09F0F0002E2021 /* Infrastructure */ = { isa = PBXGroup; children = ( + ACB46A282D0EC765002E200C /* AppConfiguration.swift */, + ACB46A242D0EC083002E200C /* Video */, AC6BECBA2D09F0FD002E2021 /* Network */, ); path = Infrastructure; @@ -218,6 +263,7 @@ AC6BECBA2D09F0FD002E2021 /* Network */ = { isa = PBXGroup; children = ( + ACB46A582D0EF912002E200C /* Auth */, AC6BECBE2D09F866002E2021 /* NetworkService.swift */, AC6BECBB2D09F685002E2021 /* Endpoint.swift */, AC6BECC12D09F917002E2021 /* HTTPService.swift */, @@ -225,6 +271,94 @@ path = Network; sourceTree = ""; }; + ACB46A242D0EC083002E200C /* Video */ = { + isa = PBXGroup; + children = ( + ACB46A252D0EC09C002E200C /* YralPlayer.swift */, + ); + path = Video; + sourceTree = ""; + }; + ACB46A2E2D0EC838002E200C /* DIContainer */ = { + isa = PBXGroup; + children = ( + ACB46A2B2D0EC82F002E200C /* AppDIContainer.swift */, + ACB46A352D0EC991002E200C /* FeedDIContainer.swift */, + ); + path = DIContainer; + sourceTree = ""; + }; + ACB46A3C2D0ECBB3002E200C /* grpc */ = { + isa = PBXGroup; + children = ( + ACB46A392D0ECBB3002E200C /* ml_feed.proto */, + ACB46A3A2D0ECBB3002E200C /* ml_feed.grpc.swift */, + ACB46A3B2D0ECBB3002E200C /* ml_feed.pb.swift */, + ); + path = grpc; + sourceTree = ""; + }; + ACB46A3D2D0ECBB3002E200C /* DTO */ = { + isa = PBXGroup; + children = ( + ACB46A3C2D0ECBB3002E200C /* grpc */, + ); + path = DTO; + sourceTree = ""; + }; + ACB46A3E2D0ECBB3002E200C /* Data */ = { + isa = PBXGroup; + children = ( + ACB46A452D0ECBB8002E200C /* Repositories */, + ACB46A3D2D0ECBB3002E200C /* DTO */, + ); + path = Data; + sourceTree = ""; + }; + ACB46A452D0ECBB8002E200C /* Repositories */ = { + isa = PBXGroup; + children = ( + ACB46A462D0ECBD1002E200C /* FeedsRepository.swift */, + ); + path = Repositories; + sourceTree = ""; + }; + ACB46A492D0ED0E9002E200C /* Domain */ = { + isa = PBXGroup; + children = ( + ACB46A4E2D0ED181002E200C /* Entities */, + ACB46A4A2D0ED0F8002E200C /* Interfaces */, + ); + path = Domain; + sourceTree = ""; + }; + ACB46A4A2D0ED0F8002E200C /* Interfaces */ = { + isa = PBXGroup; + children = ( + ACB46A4B2D0ED100002E200C /* FeedRepositoryProtocol.swift */, + ); + path = Interfaces; + sourceTree = ""; + }; + ACB46A4E2D0ED181002E200C /* Entities */ = { + isa = PBXGroup; + children = ( + ACB46A522D0ED222002E200C /* FeedQuery.swift */, + ACB46A4F2D0ED18B002E200C /* FeedResult.swift */, + ); + path = Entities; + sourceTree = ""; + }; + ACB46A582D0EF912002E200C /* Auth */ = { + isa = PBXGroup; + children = ( + ACB46A5F2D0EF947002E200C /* AuthClient.swift */, + ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */, + ACB46A592D0EF91A002E200C /* AuthConstants.swift */, + ); + path = Auth; + sourceTree = ""; + }; ACD941BC2CFF57A90038FCB6 /* Prod */ = { isa = PBXGroup; children = ( @@ -246,7 +380,8 @@ ACEBE9962D0C4AA50056F033 /* Feed */ = { isa = PBXGroup; children = ( - ACEBE9982D0C4AE90056F033 /* Data */, + ACB46A492D0ED0E9002E200C /* Domain */, + ACB46A3E2D0ECBB3002E200C /* Data */, ); path = Feed; sourceTree = ""; @@ -269,9 +404,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - ACEBE9982D0C4AE90056F033 /* Data */, - ); name = iosApp; productName = iosApp; productReference = 7555FF7B242A565900829871 /* iosApp.app */; @@ -292,9 +424,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - ACEBE9982D0C4AE90056F033 /* Data */, - ); name = iosAppStaging; productName = iosApp; productReference = ACD941B82CFF575C0038FCB6 /* iosAppStaging.app */; @@ -489,6 +618,17 @@ buildActionMask = 2147483647; files = ( AC6BECC02D09F86C002E2021 /* NetworkService.swift in Sources */, + ACB46A262D0EC0A3002E200C /* YralPlayer.swift in Sources */, + ACB46A422D0ECBB3002E200C /* ml_feed.proto in Sources */, + ACB46A5E2D0EF92D002E200C /* AuthEndPoints.swift in Sources */, + ACB46A612D0EF94B002E200C /* AuthClient.swift in Sources */, + ACB46A432D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */, + ACB46A442D0ECBB3002E200C /* ml_feed.pb.swift in Sources */, + ACB46A482D0ECBD5002E200C /* FeedsRepository.swift in Sources */, + ACB46A502D0ED195002E200C /* FeedResult.swift in Sources */, + ACB46A532D0ED22B002E200C /* FeedQuery.swift in Sources */, + ACB46A2D2D0EC833002E200C /* AppDIContainer.swift in Sources */, + ACB46A362D0EC998002E200C /* FeedDIContainer.swift in Sources */, 2152FB042600AC8F00CF470E /* IosApp.swift in Sources */, AC09A9B22CDB775C009A39A4 /* yral-mobile-swift-binding.swift in Sources */, AC6BECBD2D09F68C002E2021 /* Endpoint.swift in Sources */, @@ -496,7 +636,11 @@ AC09AA742CDD210D009A39A4 /* Data+Extensions.swift in Sources */, AC09A9FA2CDB7A5D009A39A4 /* SwiftBindings+Extensions.swift in Sources */, AC6BECC22D09F91C002E2021 /* HTTPService.swift in Sources */, + ACB46A5B2D0EF91F002E200C /* AuthConstants.swift in Sources */, + ACB46A292D0EC76A002E200C /* AppConfiguration.swift in Sources */, + ACB46A342D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */, 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ACB46A4C2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -505,6 +649,17 @@ buildActionMask = 2147483647; files = ( AC6BECBF2D09F86C002E2021 /* NetworkService.swift in Sources */, + ACB46A272D0EC0A3002E200C /* YralPlayer.swift in Sources */, + ACB46A3F2D0ECBB3002E200C /* ml_feed.proto in Sources */, + ACB46A5D2D0EF92D002E200C /* AuthEndPoints.swift in Sources */, + ACB46A602D0EF94B002E200C /* AuthClient.swift in Sources */, + ACB46A402D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */, + ACB46A412D0ECBB3002E200C /* ml_feed.pb.swift in Sources */, + ACB46A472D0ECBD5002E200C /* FeedsRepository.swift in Sources */, + ACB46A512D0ED195002E200C /* FeedResult.swift in Sources */, + ACB46A542D0ED22B002E200C /* FeedQuery.swift in Sources */, + ACB46A2C2D0EC833002E200C /* AppDIContainer.swift in Sources */, + ACB46A372D0EC998002E200C /* FeedDIContainer.swift in Sources */, ACD941872CFF575C0038FCB6 /* IosApp.swift in Sources */, ACD941882CFF575C0038FCB6 /* yral-mobile-swift-binding.swift in Sources */, AC6BECBC2D09F68C002E2021 /* Endpoint.swift in Sources */, @@ -512,7 +667,11 @@ ACD9418A2CFF575C0038FCB6 /* Data+Extensions.swift in Sources */, ACD9418B2CFF575C0038FCB6 /* SwiftBindings+Extensions.swift in Sources */, AC6BECC32D09F91C002E2021 /* HTTPService.swift in Sources */, + ACB46A5A2D0EF91F002E200C /* AuthConstants.swift in Sources */, + ACB46A2A2D0EC76A002E200C /* AppConfiguration.swift in Sources */, + ACB46A332D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */, ACD9418C2CFF575C0038FCB6 /* ContentView.swift in Sources */, + ACB46A4D2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iosApp/iosApp/DIContainer/AppDIContainer.swift b/iosApp/iosApp/DIContainer/AppDIContainer.swift new file mode 100644 index 0000000..9723bde --- /dev/null +++ b/iosApp/iosApp/DIContainer/AppDIContainer.swift @@ -0,0 +1,36 @@ +// +// AppDIContainer.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation +import GRPC + +final class AppDIContainer { + let appConfiguration = AppConfiguration() + + lazy var mlFeedClient: MlFeed_MLFeedNIOClient = { + let group = PlatformSupport.makeEventLoopGroup(loopCount: .one) + var configuration = ClientConnection.Configuration.default( + target: appConfiguration.mlFeedConnectionTarget, + eventLoopGroup: group + ) + let tlsConfig = GRPCTLSConfiguration.makeClientConfigurationBackedByNIOSSL( + certificateChain: [], + privateKey: nil, + trustRoots: .default, + certificateVerification: .fullVerification, + hostnameOverride: nil, + customVerificationCallback: nil + ) + configuration.tlsConfiguration = tlsConfig + let channel = ClientConnection(configuration: configuration) + return MlFeed_MLFeedNIOClient(channel: channel) + }() + + func makeFeedDIContainer() -> FeedDIContainer { + FeedDIContainer(dependencies: FeedDIContainer.Dependencies(mlfeedService: mlFeedClient, httpService: HTTPService())) + } +} diff --git a/iosApp/iosApp/DIContainer/FeedDIContainer.swift b/iosApp/iosApp/DIContainer/FeedDIContainer.swift new file mode 100644 index 0000000..5076359 --- /dev/null +++ b/iosApp/iosApp/DIContainer/FeedDIContainer.swift @@ -0,0 +1,21 @@ +// +// FeedDIContainer.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation + +final class FeedDIContainer { + struct Dependencies { + let mlfeedService: MlFeed_MLFeedNIOClient + let httpService: HTTPService + } + + private let dependencies: Dependencies + + init(dependencies: Dependencies) { + self.dependencies = dependencies + } +} diff --git a/iosApp/iosApp/Extensions/Numbers+Extensions.swift b/iosApp/iosApp/Extensions/Numbers+Extensions.swift new file mode 100644 index 0000000..a0090ee --- /dev/null +++ b/iosApp/iosApp/Extensions/Numbers+Extensions.swift @@ -0,0 +1,27 @@ +// +// CGFloat+Extensions.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation + +extension CGFloat { + static let zero: CGFloat = 0 + static let pointOne: CGFloat = 0.1 + static let animationPeriod: CGFloat = 0.3 + static let half: CGFloat = 0.5 + static let one: CGFloat = 1 + static let two: CGFloat = 2 + static let three: CGFloat = 3 + static let hundred: CGFloat = 100.0 + static let apiDelay: CGFloat = 0.2 +} + +extension Int { + static let zero: Int = 0 + static let one: Int = 1 + static let two: Int = 2 + static let three: Int = 3 +} diff --git a/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift b/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift new file mode 100644 index 0000000..759bfaf --- /dev/null +++ b/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift @@ -0,0 +1,16 @@ +// +// FeedQuery.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +import Foundation + +struct FeedRequest { + let canisterId: String + let filteredPosts: [MlFeed_PostItem] + let numResults: Int + let wire: DelegatedIdentityWire +} diff --git a/iosApp/iosApp/Feed/Domain/Entities/FeedResult.swift b/iosApp/iosApp/Feed/Domain/Entities/FeedResult.swift new file mode 100644 index 0000000..acb10b3 --- /dev/null +++ b/iosApp/iosApp/Feed/Domain/Entities/FeedResult.swift @@ -0,0 +1,13 @@ +// +// FeedResult.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation + +struct FeedResult { + let id: String + let url: URL +} diff --git a/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift b/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift new file mode 100644 index 0000000..e3f3608 --- /dev/null +++ b/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift @@ -0,0 +1,11 @@ +// +// FeedRepositoryProtocol.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +protocol FeedRepositoryProtocol { + func fetchFeed(request: FeedRequest) async -> [FeedResult] +} diff --git a/iosApp/iosApp/Infrastructure/AppConfiguration.swift b/iosApp/iosApp/Infrastructure/AppConfiguration.swift new file mode 100644 index 0000000..1374d27 --- /dev/null +++ b/iosApp/iosApp/Infrastructure/AppConfiguration.swift @@ -0,0 +1,13 @@ +// +// AppConfiguration.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation +import GRPC + +final class AppConfiguration { + let mlFeedConnectionTarget = ConnectionTarget.hostAndPort("yral-ml-feed-server.fly.dev", 443) +} diff --git a/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift new file mode 100644 index 0000000..64ff885 --- /dev/null +++ b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift @@ -0,0 +1,113 @@ +// +// AuthClient.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +import Foundation +import secp256k1 + +class DefaultAuthClient: AuthClient { + private(set) var identity: DelegatedIdentity? + private(set) var principal: Principal? + private let networkService: NetworkService + private let cookieStorage = HTTPCookieStorage.shared + + init(networkService: NetworkService) { + self.networkService = networkService + } + + func initialize() async throws { + if let existingCookie = cookieStorage.cookies?.first(where: { $0.name == AuthConstants.cookieName }) { + try await refreshAuthIfNeeded(using: existingCookie) + } else { + try await fetchAndSetAuthCookie() + } + } + + func refreshAuthIfNeeded() async throws { + guard let existingCookie = cookieStorage.cookies?.first(where: { $0.name == AuthConstants.cookieName }) else { + try await fetchAndSetAuthCookie() + return + } + + if let expiresDate = existingCookie.expiresDate, expiresDate < Date() { + try await fetchAndSetAuthCookie() + } else { + try await extractIdentity(from: existingCookie) + } + } + + private func refreshAuthIfNeeded(using cookie: HTTPCookie) async throws { + if let expiresDate = cookie.expiresDate, expiresDate < Date() { + try await fetchAndSetAuthCookie() + } else { + try await extractIdentity(from: cookie) + } + } + + private func fetchAndSetAuthCookie() async throws { + let payload = try createAuthPayload() + let endpoint = AuthEndpoints.setAnonymousIdentityCookie(payload: payload) + + _ = try await networkService.performRequest(for: endpoint) + + guard let newCookie = cookieStorage.cookies?.first(where: { $0.name == AuthConstants.cookieName }) else { + throw NetworkError.invalidResponse("Failed to fetch cookie") + } + try await extractIdentity(from: newCookie) + } + + private func extractIdentity(from cookie: HTTPCookie) async throws { + let endpoint = AuthEndpoints.extractIdentity(cookie: cookie) + + let data = try await networkService.performRequest(for: endpoint) + try await handleExtractIdentityResponse(from: data) + } + + private func handleExtractIdentityResponse(from data: Data) async throws { + try data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in + if buffer.count > 0 { + let uint8Buffer = buffer.bindMemory(to: UInt8.self) + let wire = try delegated_identity_wire_from_bytes(uint8Buffer) + let identity = try delegated_identity_from_bytes(uint8Buffer) + + Task { @MainActor in + self.identity = identity + self.principal = principal + } + } else { + throw NetworkError.invalidResponse("Empty data received") + } + } + } + + private func createAuthPayload() throws -> Data { + let privateKey = try secp256k1.Signing.PrivateKey(format: .uncompressed) + let publicKeyData = privateKey.publicKey.dataRepresentation + + let xData = publicKeyData[1...32].base64URLEncodedString() + let yData = publicKeyData[33...64].base64URLEncodedString() + let dData = privateKey.dataRepresentation.base64URLEncodedString() + + let jwk: [String: Any] = [ + "kty": "EC", + "crv": "secp256k1", + "x": xData, + "y": yData, + "d": dData + ] + + let payload: [String: Any] = ["anonymous_identity": jwk] + return try JSONSerialization.data(withJSONObject: payload) + } +} + +protocol AuthClient { + var identity: DelegatedIdentity? { get } + var principal: Principal? { get } + func initialize() async throws + func refreshAuthIfNeeded() async throws +} diff --git a/iosApp/iosApp/Infrastructure/Network/Auth/AuthConstants.swift b/iosApp/iosApp/Infrastructure/Network/Auth/AuthConstants.swift new file mode 100644 index 0000000..70d292b --- /dev/null +++ b/iosApp/iosApp/Infrastructure/Network/Auth/AuthConstants.swift @@ -0,0 +1,18 @@ +// +// AuthConstants.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +struct AuthConstants { + static let baseURL = "https://yral.com/api" + static let setAnonymousIdentityCookiePath = "set_anonymous_identity_cookie" + static let extractIdentityPath = "extract_identity" + static let cookieName = "user-identity" + static let contentTypeHeader = "Content-Type" + static let cookieHeader = "Cookie" + static let contentTypeJSON = "application/json" + static let emptyRequestBody = "{}" +} diff --git a/iosApp/iosApp/Infrastructure/Network/Auth/AuthEndPoints.swift b/iosApp/iosApp/Infrastructure/Network/Auth/AuthEndPoints.swift new file mode 100644 index 0000000..c35a14a --- /dev/null +++ b/iosApp/iosApp/Infrastructure/Network/Auth/AuthEndPoints.swift @@ -0,0 +1,34 @@ +// +// AuthEndPoints.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import UIKit +class AuthEndpoints { + static func setAnonymousIdentityCookie(payload: Data) -> Endpoint { + return Endpoint( + http: "SetAnonymousIdentityCookie", + baseURL: URL(string: AuthConstants.baseURL)!, + path: AuthConstants.setAnonymousIdentityCookiePath, + method: .post, + headers: [AuthConstants.contentTypeHeader: AuthConstants.contentTypeJSON], + body: payload + ) + } + + static func extractIdentity(cookie: HTTPCookie) -> Endpoint { + return Endpoint( + http: "ExtractIdentity", + baseURL: URL(string: AuthConstants.baseURL)!, + path: AuthConstants.extractIdentityPath, + method: .post, + headers: [ + AuthConstants.contentTypeHeader: AuthConstants.contentTypeJSON, + AuthConstants.cookieHeader: "\(cookie.name)=\(cookie.value)" + ], + body: Data(AuthConstants.emptyRequestBody.utf8) + ) + } +} diff --git a/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift b/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift new file mode 100644 index 0000000..bbf934e --- /dev/null +++ b/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift @@ -0,0 +1,10 @@ +// +// YralPlayer.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +import UIKit +import AVFoundation From d7ae2749262575868ae69c024c427ed3246ff83d Mon Sep 17 00:00:00 2001 From: sarveshsharma Date: Sun, 15 Dec 2024 22:30:05 +0530 Subject: [PATCH 3/4] build: Adds view model setup for feeds --- iosApp/iosApp.xcodeproj/project.pbxproj | 50 ++++++++++++++++- .../iosApp/Extensions/Data+Extensions.swift | 20 +++++-- .../Data/Repositories/FeedsRepository.swift | 55 +++++++++++++++++++ .../Feed/Domain/Entities/FeedQuery.swift | 1 - .../Interfaces/FeedRepositoryProtocol.swift | 2 +- .../Feed/Domain/Usecase/FeedsUseCase.swift | 23 ++++++++ .../Feed/View/FeedsViewController.swift | 41 ++++++++++++++ .../View/FeedsViewControllerWrapper.swift | 7 +++ .../Feed/ViewModel/FeedsViewModel.swift | 44 +++++++++++++++ .../Network/Auth/AuthClient.swift | 17 ++++++ 10 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 iosApp/iosApp/Feed/Data/Repositories/FeedsRepository.swift create mode 100644 iosApp/iosApp/Feed/Domain/Usecase/FeedsUseCase.swift create mode 100644 iosApp/iosApp/Feed/View/FeedsViewController.swift create mode 100644 iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift create mode 100644 iosApp/iosApp/Feed/ViewModel/FeedsViewModel.swift diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index f77ba6d..bf9a1f7 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -61,6 +61,14 @@ ACB46A5E2D0EF92D002E200C /* AuthEndPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */; }; ACB46A602D0EF94B002E200C /* AuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5F2D0EF947002E200C /* AuthClient.swift */; }; ACB46A612D0EF94B002E200C /* AuthClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A5F2D0EF947002E200C /* AuthClient.swift */; }; + ACB46A642D0F0B75002E200C /* FeedsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A632D0F0B70002E200C /* FeedsUseCase.swift */; }; + ACB46A652D0F0B75002E200C /* FeedsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A632D0F0B70002E200C /* FeedsUseCase.swift */; }; + ACB46A682D0F3609002E200C /* FeedsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A672D0F3604002E200C /* FeedsViewModel.swift */; }; + ACB46A692D0F3609002E200C /* FeedsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A672D0F3604002E200C /* FeedsViewModel.swift */; }; + ACB46A6B2D0F3F8E002E200C /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A6A2D0F3F89002E200C /* FeedsViewController.swift */; }; + ACB46A6C2D0F3F8E002E200C /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A6A2D0F3F89002E200C /* FeedsViewController.swift */; }; + ACB46A6F2D0F417F002E200C /* FeedsViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A6E2D0F4179002E200C /* FeedsViewControllerWrapper.swift */; }; + ACB46A702D0F417F002E200C /* FeedsViewControllerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB46A6E2D0F4179002E200C /* FeedsViewControllerWrapper.swift */; }; ACB815372CF9D4FF00104B7B /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = ACB815342CF9CE0800104B7B /* GoogleService-Info.plist */; }; ACB815482CFA146800104B7B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = ACB815472CFA146800104B7B /* FirebaseCore */; }; ACD941802CFF55BB0038FCB6 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */ = {isa = PBXBuildFile; productRef = ACD9417F2CFF55BB0038FCB6 /* FirebaseAnalyticsWithoutAdIdSupport */; }; @@ -119,6 +127,10 @@ ACB46A592D0EF91A002E200C /* AuthConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthConstants.swift; sourceTree = ""; }; ACB46A5C2D0EF927002E200C /* AuthEndPoints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthEndPoints.swift; sourceTree = ""; }; ACB46A5F2D0EF947002E200C /* AuthClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthClient.swift; sourceTree = ""; }; + ACB46A632D0F0B70002E200C /* FeedsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsUseCase.swift; sourceTree = ""; }; + ACB46A672D0F3604002E200C /* FeedsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsViewModel.swift; sourceTree = ""; }; + ACB46A6A2D0F3F89002E200C /* FeedsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsViewController.swift; sourceTree = ""; }; + ACB46A6E2D0F4179002E200C /* FeedsViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsViewControllerWrapper.swift; sourceTree = ""; }; ACB815342CF9CE0800104B7B /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; ACD941B82CFF575C0038FCB6 /* iosAppStaging.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosAppStaging.app; sourceTree = BUILT_PRODUCTS_DIR; }; ACD941B92CFF575C0038FCB6 /* Info-Staging.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-Staging.plist"; path = "/Users/sarveshsharma/Desktop/yral-mobile/iosApp/Staging/Info-Staging.plist"; sourceTree = ""; }; @@ -200,13 +212,13 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( + 2152FB032600AC8F00CF470E /* IosApp.swift */, ACB46A2E2D0EC838002E200C /* DIContainer */, ACEBE9962D0C4AA50056F033 /* Feed */, AC6BECB92D09F0F0002E2021 /* Infrastructure */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, 7555FF82242A565900829871 /* ContentView.swift */, AC09AA1E2CDBB69B009A39A4 /* Extensions */, - 2152FB032600AC8F00CF470E /* IosApp.swift */, 058557D7273AAEEB004C7B11 /* Preview Content */, ); path = iosApp; @@ -326,6 +338,7 @@ ACB46A492D0ED0E9002E200C /* Domain */ = { isa = PBXGroup; children = ( + ACB46A622D0F0B67002E200C /* Usecase */, ACB46A4E2D0ED181002E200C /* Entities */, ACB46A4A2D0ED0F8002E200C /* Interfaces */, ); @@ -359,6 +372,31 @@ path = Auth; sourceTree = ""; }; + ACB46A622D0F0B67002E200C /* Usecase */ = { + isa = PBXGroup; + children = ( + ACB46A632D0F0B70002E200C /* FeedsUseCase.swift */, + ); + path = Usecase; + sourceTree = ""; + }; + ACB46A662D0F35FD002E200C /* ViewModel */ = { + isa = PBXGroup; + children = ( + ACB46A672D0F3604002E200C /* FeedsViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + ACB46A6D2D0F3F9E002E200C /* View */ = { + isa = PBXGroup; + children = ( + ACB46A6E2D0F4179002E200C /* FeedsViewControllerWrapper.swift */, + ACB46A6A2D0F3F89002E200C /* FeedsViewController.swift */, + ); + path = View; + sourceTree = ""; + }; ACD941BC2CFF57A90038FCB6 /* Prod */ = { isa = PBXGroup; children = ( @@ -380,6 +418,8 @@ ACEBE9962D0C4AA50056F033 /* Feed */ = { isa = PBXGroup; children = ( + ACB46A6D2D0F3F9E002E200C /* View */, + ACB46A662D0F35FD002E200C /* ViewModel */, ACB46A492D0ED0E9002E200C /* Domain */, ACB46A3E2D0ECBB3002E200C /* Data */, ); @@ -619,10 +659,12 @@ files = ( AC6BECC02D09F86C002E2021 /* NetworkService.swift in Sources */, ACB46A262D0EC0A3002E200C /* YralPlayer.swift in Sources */, + ACB46A682D0F3609002E200C /* FeedsViewModel.swift in Sources */, ACB46A422D0ECBB3002E200C /* ml_feed.proto in Sources */, ACB46A5E2D0EF92D002E200C /* AuthEndPoints.swift in Sources */, ACB46A612D0EF94B002E200C /* AuthClient.swift in Sources */, ACB46A432D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */, + ACB46A652D0F0B75002E200C /* FeedsUseCase.swift in Sources */, ACB46A442D0ECBB3002E200C /* ml_feed.pb.swift in Sources */, ACB46A482D0ECBD5002E200C /* FeedsRepository.swift in Sources */, ACB46A502D0ED195002E200C /* FeedResult.swift in Sources */, @@ -639,6 +681,8 @@ ACB46A5B2D0EF91F002E200C /* AuthConstants.swift in Sources */, ACB46A292D0EC76A002E200C /* AppConfiguration.swift in Sources */, ACB46A342D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */, + ACB46A6F2D0F417F002E200C /* FeedsViewControllerWrapper.swift in Sources */, + ACB46A6C2D0F3F8E002E200C /* FeedsViewController.swift in Sources */, 7555FF83242A565900829871 /* ContentView.swift in Sources */, ACB46A4C2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */, ); @@ -650,10 +694,12 @@ files = ( AC6BECBF2D09F86C002E2021 /* NetworkService.swift in Sources */, ACB46A272D0EC0A3002E200C /* YralPlayer.swift in Sources */, + ACB46A692D0F3609002E200C /* FeedsViewModel.swift in Sources */, ACB46A3F2D0ECBB3002E200C /* ml_feed.proto in Sources */, ACB46A5D2D0EF92D002E200C /* AuthEndPoints.swift in Sources */, ACB46A602D0EF94B002E200C /* AuthClient.swift in Sources */, ACB46A402D0ECBB3002E200C /* ml_feed.grpc.swift in Sources */, + ACB46A642D0F0B75002E200C /* FeedsUseCase.swift in Sources */, ACB46A412D0ECBB3002E200C /* ml_feed.pb.swift in Sources */, ACB46A472D0ECBD5002E200C /* FeedsRepository.swift in Sources */, ACB46A512D0ED195002E200C /* FeedResult.swift in Sources */, @@ -670,6 +716,8 @@ ACB46A5A2D0EF91F002E200C /* AuthConstants.swift in Sources */, ACB46A2A2D0EC76A002E200C /* AppConfiguration.swift in Sources */, ACB46A332D0EC8FF002E200C /* Numbers+Extensions.swift in Sources */, + ACB46A702D0F417F002E200C /* FeedsViewControllerWrapper.swift in Sources */, + ACB46A6B2D0F3F8E002E200C /* FeedsViewController.swift in Sources */, ACD9418C2CFF575C0038FCB6 /* ContentView.swift in Sources */, ACB46A4D2D0ED110002E200C /* FeedRepositoryProtocol.swift in Sources */, ); diff --git a/iosApp/iosApp/Extensions/Data+Extensions.swift b/iosApp/iosApp/Extensions/Data+Extensions.swift index 73ef1d0..fcc28da 100644 --- a/iosApp/iosApp/Extensions/Data+Extensions.swift +++ b/iosApp/iosApp/Extensions/Data+Extensions.swift @@ -9,10 +9,20 @@ import Foundation extension Data { public func base64URLEncodedString() -> String { - let base64String = self.base64EncodedString() - return base64String - .replacingOccurrences(of: "=", with: "") - .replacingOccurrences(of: "+", with: "-") - .replacingOccurrences(of: "/", with: "_") + let base64String = self.base64EncodedString() + return base64String + .replacingOccurrences(of: "=", with: "") + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + } +} + +extension Sequence { + func asyncMap(_ transform: @escaping (Element) async throws -> T) async throws -> [T] { + var results = [T]() + for element in self { + try await results.append(transform(element)) + } + return results } } diff --git a/iosApp/iosApp/Feed/Data/Repositories/FeedsRepository.swift b/iosApp/iosApp/Feed/Data/Repositories/FeedsRepository.swift new file mode 100644 index 0000000..b4e6199 --- /dev/null +++ b/iosApp/iosApp/Feed/Data/Repositories/FeedsRepository.swift @@ -0,0 +1,55 @@ +// +// FeedsRepository.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import Foundation + +class FeedsRepository: FeedRepositoryProtocol { + private let httpService: HTTPService + private let mlClient: MlFeed_MLFeedNIOClient + private let authClient: AuthClient + + init(httpService: HTTPService, mlClient: MlFeed_MLFeedNIOClient, authClient: AuthClient) { + self.httpService = httpService + self.mlClient = mlClient + self.authClient = authClient + } + + func fetchFeed(request: FeedRequest) async -> Result<[FeedResult], Error> { + var mlRequest = MlFeed_FeedRequest() + mlRequest.canisterID = request.canisterId + mlRequest.filterPosts = request.filteredPosts + mlRequest.numResults = UInt32(request.numResults) + do { + let response = try mlClient.get_feed_clean( + mlRequest + ).response.wait() + let feeds = try await response.feed.asyncMap { feed in + let principal = try get_principal(feed.canisterID) + do { + let identity = try self.authClient.generateNewDelegatedIdentity() + let service = try Service(principal, identity) + let result = try await service.get_individual_post_details_by_id(UInt64(feed.postID)) + let videoURL = URL( + string: "\(Constants.cloudfarePrefix)\(result.video_uid().toString())\(Constants.cloudflareSuffix)" + ) ?? URL(fileURLWithPath: "") + return FeedResult(id: String(feed.postID), url: videoURL) + } + } + return .success(feeds) + } catch { + print(error) + return .failure(error) + } + } +} + +extension FeedsRepository { + enum Constants { + static let cloudfarePrefix = "https://customer-2p3jflss4r4hmpnz.cloudflarestream.com/" + static let cloudflareSuffix = "/manifest/video.m3u8" + } +} diff --git a/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift b/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift index 759bfaf..675d418 100644 --- a/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift +++ b/iosApp/iosApp/Feed/Domain/Entities/FeedQuery.swift @@ -12,5 +12,4 @@ struct FeedRequest { let canisterId: String let filteredPosts: [MlFeed_PostItem] let numResults: Int - let wire: DelegatedIdentityWire } diff --git a/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift b/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift index e3f3608..605dfda 100644 --- a/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift +++ b/iosApp/iosApp/Feed/Domain/Interfaces/FeedRepositoryProtocol.swift @@ -7,5 +7,5 @@ // protocol FeedRepositoryProtocol { - func fetchFeed(request: FeedRequest) async -> [FeedResult] + func fetchFeed(request: FeedRequest) async -> Result<[FeedResult], Error> } diff --git a/iosApp/iosApp/Feed/Domain/Usecase/FeedsUseCase.swift b/iosApp/iosApp/Feed/Domain/Usecase/FeedsUseCase.swift new file mode 100644 index 0000000..d7487d6 --- /dev/null +++ b/iosApp/iosApp/Feed/Domain/Usecase/FeedsUseCase.swift @@ -0,0 +1,23 @@ +// +// FeedsUseCase.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +protocol FeedsUseCaseProtocol { + func execute(request: FeedRequest) async throws -> Result<[FeedResult], Error> +} + +class FeedsUseCase: FeedsUseCaseProtocol { + private let feedRepository: FeedRepositoryProtocol + + init(feedRepository: FeedRepositoryProtocol) { + self.feedRepository = feedRepository + } + + func execute(request: FeedRequest) async throws -> Result<[FeedResult], Error> { + await feedRepository.fetchFeed(request: request) + } +} diff --git a/iosApp/iosApp/Feed/View/FeedsViewController.swift b/iosApp/iosApp/Feed/View/FeedsViewController.swift new file mode 100644 index 0000000..851e947 --- /dev/null +++ b/iosApp/iosApp/Feed/View/FeedsViewController.swift @@ -0,0 +1,41 @@ +// +// FeedsViewController.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// +import UIKit + +class FeedsViewController: UIViewController { + private var viewModel: FeedsViewModel + + init(viewModel: FeedsViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + bindViewModel(viewModel: viewModel) + } + + func bindViewModel(viewModel: FeedsViewModel) { + viewModel.$state.receive(on: RunLoop.main).sink { [weak self] state in + switch state { + case .initalized: + break + case .loading: + break + case .successfullyFetched(let feeds): + print(feeds) + case .failure(let error): + print(error) + } + } + } +} diff --git a/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift b/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift new file mode 100644 index 0000000..1225803 --- /dev/null +++ b/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift @@ -0,0 +1,7 @@ +// +// FeedsViewControllerWrapper.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// diff --git a/iosApp/iosApp/Feed/ViewModel/FeedsViewModel.swift b/iosApp/iosApp/Feed/ViewModel/FeedsViewModel.swift new file mode 100644 index 0000000..5bcaced --- /dev/null +++ b/iosApp/iosApp/Feed/ViewModel/FeedsViewModel.swift @@ -0,0 +1,44 @@ +// +// FeedsViewModel.swift +// iosApp +// +// Created by Sarvesh Sharma on 15/12/24. +// Copyright © 2024 orgName. All rights reserved. +// + +import Foundation + +enum FeedsPageState { + case initalized + case loading + case successfullyFetched([FeedResult]) + case failure(Error) +} + +enum FeedsPageEvent { + case reachedEnd +} + +class FeedsViewModel: ObservableObject { + @Published var state: FeedsPageState = .initalized + var feedsUseCase: FeedsUseCase + + init(useCase: FeedsUseCase) { + self.feedsUseCase = useCase + } + + func fetchFeeds(request: FeedRequest) async { + state = .loading + do { + let result = try await feedsUseCase.execute(request: request) + switch result { + case .success(let response): + state = .successfullyFetched(response) + case .failure(let error): + state = .failure(error) + } + } catch { + state = .failure(error) + } + } +} diff --git a/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift index 64ff885..c7e0e52 100644 --- a/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift +++ b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift @@ -14,6 +14,7 @@ class DefaultAuthClient: AuthClient { private(set) var principal: Principal? private let networkService: NetworkService private let cookieStorage = HTTPCookieStorage.shared + private(set) var identityData: Data? init(networkService: NetworkService) { self.networkService = networkService @@ -64,6 +65,7 @@ class DefaultAuthClient: AuthClient { let endpoint = AuthEndpoints.extractIdentity(cookie: cookie) let data = try await networkService.performRequest(for: endpoint) + identityData = data try await handleExtractIdentityResponse(from: data) } @@ -103,6 +105,20 @@ class DefaultAuthClient: AuthClient { let payload: [String: Any] = ["anonymous_identity": jwk] return try JSONSerialization.data(withJSONObject: payload) } + + func generateNewDelegatedIdentity() throws -> DelegatedIdentity { + guard let data = identityData else { + throw NetworkError.invalidResponse("No identity data available") + } + return try data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in + if buffer.count > 0 { + let uint8Buffer = buffer.bindMemory(to: UInt8.self) + return try delegated_identity_from_bytes(uint8Buffer) + } else { + throw NetworkError.invalidResponse("Empty data received") + } + } + } } protocol AuthClient { @@ -110,4 +126,5 @@ protocol AuthClient { var principal: Principal? { get } func initialize() async throws func refreshAuthIfNeeded() async throws + func generateNewDelegatedIdentity() throws -> DelegatedIdentity } From b0e03bc86f4ba6ce197d845a631fb369292fb4bf Mon Sep 17 00:00:00 2001 From: sarveshsharma Date: Sun, 15 Dec 2024 23:30:14 +0530 Subject: [PATCH 4/4] build: Adds flow setup and YralPlaye --- .../iosApp/DIContainer/AppDIContainer.swift | 13 ++- .../iosApp/DIContainer/FeedDIContainer.swift | 17 ++++ .../View/FeedsViewControllerWrapper.swift | 14 ++++ .../Network/Auth/AuthClient.swift | 2 +- .../Infrastructure/Video/YralPlayer.swift | 81 +++++++++++++++++++ iosApp/iosApp/IosApp.swift | 33 ++++++-- 6 files changed, 153 insertions(+), 7 deletions(-) diff --git a/iosApp/iosApp/DIContainer/AppDIContainer.swift b/iosApp/iosApp/DIContainer/AppDIContainer.swift index 9723bde..81283b0 100644 --- a/iosApp/iosApp/DIContainer/AppDIContainer.swift +++ b/iosApp/iosApp/DIContainer/AppDIContainer.swift @@ -30,7 +30,18 @@ final class AppDIContainer { return MlFeed_MLFeedNIOClient(channel: channel) }() + lazy var authClient: DefaultAuthClient = { + let client = DefaultAuthClient(networkService: HTTPService()) + return client + }() + func makeFeedDIContainer() -> FeedDIContainer { - FeedDIContainer(dependencies: FeedDIContainer.Dependencies(mlfeedService: mlFeedClient, httpService: HTTPService())) + return FeedDIContainer( + dependencies: FeedDIContainer.Dependencies( + mlfeedService: mlFeedClient, + httpService: HTTPService(), + authClient: authClient + ) + ) } } diff --git a/iosApp/iosApp/DIContainer/FeedDIContainer.swift b/iosApp/iosApp/DIContainer/FeedDIContainer.swift index 5076359..e6766a0 100644 --- a/iosApp/iosApp/DIContainer/FeedDIContainer.swift +++ b/iosApp/iosApp/DIContainer/FeedDIContainer.swift @@ -11,6 +11,7 @@ final class FeedDIContainer { struct Dependencies { let mlfeedService: MlFeed_MLFeedNIOClient let httpService: HTTPService + let authClient: AuthClient } private let dependencies: Dependencies @@ -18,4 +19,20 @@ final class FeedDIContainer { init(dependencies: Dependencies) { self.dependencies = dependencies } + + func makeFeedsViewControllerWrapper() -> FeedsViewControllerWrapper { + FeedsViewControllerWrapper(feedsViewController: FeedsViewController(viewModel: makeFeedsViewModel())) + } + + func makeFeedsViewModel() -> FeedsViewModel { + FeedsViewModel(useCase: FeedsUseCase(feedRepository: makeFeedsRepository())) + } + + func makeFeedsRepository() -> FeedsRepository { + FeedsRepository( + httpService: dependencies.httpService, + mlClient: dependencies.mlfeedService, + authClient: dependencies.authClient + ) + } } diff --git a/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift b/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift index 1225803..707d1d5 100644 --- a/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift +++ b/iosApp/iosApp/Feed/View/FeedsViewControllerWrapper.swift @@ -5,3 +5,17 @@ // Created by Sarvesh Sharma on 15/12/24. // Copyright © 2024 orgName. All rights reserved. // + +import UIKit +import SwiftUI + +struct FeedsViewControllerWrapper: UIViewControllerRepresentable { + let feedsViewController: FeedsViewController + + func makeUIViewController(context: Context) -> UINavigationController { + let navigationController = UINavigationController(rootViewController: feedsViewController) + return navigationController + } + + func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { } +} diff --git a/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift index c7e0e52..9d784f5 100644 --- a/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift +++ b/iosApp/iosApp/Infrastructure/Network/Auth/AuthClient.swift @@ -20,7 +20,7 @@ class DefaultAuthClient: AuthClient { self.networkService = networkService } - func initialize() async throws { + @MainActor func initialize() async throws { if let existingCookie = cookieStorage.cookies?.first(where: { $0.name == AuthConstants.cookieName }) { try await refreshAuthIfNeeded(using: existingCookie) } else { diff --git a/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift b/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift index bbf934e..c67add3 100644 --- a/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift +++ b/iosApp/iosApp/Infrastructure/Video/YralPlayer.swift @@ -8,3 +8,84 @@ import UIKit import AVFoundation + +class YralPlayer { + private var currentPlayerItem: AVPlayerItem? + private var nextPlayerItem: AVPlayerItem? + private var feedResults: [FeedResult] = [] + private var currentIndex: Int = 0 + let player = AVPlayer() + + func loadInitialVideos(_ feedResults: [FeedResult]) { + self.feedResults = feedResults + currentIndex = 0 + prepareCurrentVideo() + } + + private func prepareCurrentVideo() { + guard currentIndex < feedResults.count else { return } + + let currentVideo = feedResults[currentIndex] + let asset = AVURLAsset(url: currentVideo.url) + + asset.loadValuesAsynchronously(forKeys: ["playable"]) { [weak self] in + guard let self = self else { return } + + var error: NSError? + let status = asset.statusOfValue(forKey: "playable", error: &error) + DispatchQueue.main.async { + if status == .loaded { + let item = AVPlayerItem(asset: asset) + self.currentPlayerItem = item + self.player.replaceCurrentItem(with: item) + self.player.play() + + self.preloadNextVideo() + } else { + print("Failed to load asset: \(error?.localizedDescription ?? "Unknown error")") + } + } + } + } + + private func preloadNextVideo() { + let nextIndex = currentIndex + 1 + guard nextIndex < feedResults.count else { + nextPlayerItem = nil + return + } + + let nextVideo = feedResults[nextIndex] + let nextAsset = AVURLAsset(url: nextVideo.url) + + nextAsset.loadValuesAsynchronously(forKeys: ["playable"]) { [weak self] in + guard let self = self else { return } + var error: NSError? + let status = nextAsset.statusOfValue(forKey: "playable", error: &error) + + DispatchQueue.main.async { + if status == .loaded { + let nextItem = AVPlayerItem(asset: nextAsset) + self.nextPlayerItem = nextItem + } else { + print("Failed to preload next asset: \(error?.localizedDescription ?? "Unknown error")") + } + } + } + } + + // Call this when the user scrolls to the next video + func advanceToNextVideo() { + currentIndex += 1 + if let nextItem = nextPlayerItem { + currentPlayerItem = nextItem + player.replaceCurrentItem(with: nextItem) + player.play() + // Now preload the next next video + preloadNextVideo() + } else { + // If we don’t have a preloaded item, just prepare it normally + prepareCurrentVideo() + } + } +} diff --git a/iosApp/iosApp/IosApp.swift b/iosApp/iosApp/IosApp.swift index bce00a7..27b4dd3 100644 --- a/iosApp/iosApp/IosApp.swift +++ b/iosApp/iosApp/IosApp.swift @@ -2,21 +2,44 @@ import SwiftUI import Firebase class AppDelegate: NSObject, UIApplicationDelegate { - func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { FirebaseApp.configure() return true } } - @main struct IosApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate - + let appDIContainer = AppDIContainer() + @State private var feedsDIContainer: FeedDIContainer? + @State private var initializationError: Error? var body: some Scene { WindowGroup { - ContentView() + if let feedsDIContainer = feedsDIContainer { + feedsDIContainer.makeFeedsViewControllerWrapper() + } else if let error = initializationError { + Text("Failed to initialize: \(error.localizedDescription)") + .foregroundColor(.red) + } else { + ProgressView("Initializing...") + .task { + await initializeDependencies() + } + } + } + } + + @MainActor + private func initializeDependencies() async { + do { + try await appDIContainer.authClient.initialize() + feedsDIContainer = appDIContainer.makeFeedDIContainer() + } catch { + initializationError = error } } }