diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/Info.plist b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/Info.plist index 2d1b744cd..f6741846b 100644 --- a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/Info.plist +++ b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/Info.plist @@ -8,32 +8,32 @@ DebugSymbolsPath dSYMs LibraryIdentifier - ios-arm64_x86_64-simulator + ios-arm64 LibraryPath BridgeClient.framework SupportedArchitectures arm64 - x86_64 SupportedPlatform ios - SupportedPlatformVariant - simulator DebugSymbolsPath dSYMs LibraryIdentifier - ios-arm64 + ios-arm64_x86_64-simulator LibraryPath BridgeClient.framework SupportedArchitectures arm64 + x86_64 SupportedPlatform ios + SupportedPlatformVariant + simulator CFBundlePackageType diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient index 2e31dba79..be4d423df 100755 Binary files a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient and b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient differ diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h index ec804d922..0077b7c94 100644 --- a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h +++ b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h @@ -6,7 +6,7 @@ #import #import -@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; +@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUploadedFileRecordCompanion, BridgeClientUploadedFileRecord, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientPendingUploadFile, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; @protocol BridgeClientPlatformConfig, BridgeClientBridgeConfig, BridgeClientIOSPlatformConfig, BridgeClientNativeLogWriter, BridgeClientKotlinComparable, BridgeClientKotlinx_serialization_coreKSerializer, BridgeClientUploadFileIdentifiable, BridgeClientKoin_coreKoinComponent, BridgeClientParticipantScheduleMutator, BridgeClientKotlinx_coroutines_coreJob, BridgeClientEtagStorageCache, BridgeClientKtor_client_coreHttpClientPlugin, BridgeClientHttpUtil, BridgeClientRuntimeColumnAdapter, BridgeClientRuntimeTransactionWithoutReturn, BridgeClientRuntimeTransactionWithReturn, BridgeClientRuntimeTransacterBase, BridgeClientRuntimeTransacter, BridgeClientBridgeResourceDatabase, BridgeClientRuntimeSqlDriver, BridgeClientRuntimeSqlSchema, BridgeClientDbDriverFactory, BridgeClientKotlinx_coroutines_coreFlow, BridgeClientKotlinx_coroutines_coreCoroutineScope, BridgeClientBridgeErrorStatusNotifier, BridgeClientAuthenticationProvider, BridgeClientKotlinx_coroutines_coreStateFlow, BridgeClientKotlinIterator, BridgeClientKotlinx_serialization_coreEncoder, BridgeClientKotlinx_serialization_coreSerialDescriptor, BridgeClientKotlinx_serialization_coreSerializationStrategy, BridgeClientKotlinx_serialization_coreDecoder, BridgeClientKotlinx_serialization_coreDeserializationStrategy, BridgeClientKoin_coreKoinScopeComponent, BridgeClientKoin_coreQualifier, BridgeClientKotlinKClass, BridgeClientKotlinLazy, BridgeClientKotlinx_coroutines_coreChildHandle, BridgeClientKotlinx_coroutines_coreChildJob, BridgeClientKotlinx_coroutines_coreDisposableHandle, BridgeClientKotlinSequence, BridgeClientKotlinx_coroutines_coreSelectClause0, BridgeClientKotlinCoroutineContextKey, BridgeClientKotlinCoroutineContextElement, BridgeClientKotlinCoroutineContext, BridgeClientKtor_ioCloseable, BridgeClientKtor_client_coreHttpClientEngine, BridgeClientKtor_client_coreHttpClientEngineCapability, BridgeClientKtor_utilsAttributes, BridgeClientRuntimeTransactionCallbacks, BridgeClientRuntimeQueryListener, BridgeClientRuntimeQueryResult, BridgeClientRuntimeSqlPreparedStatement, BridgeClientRuntimeSqlCursor, BridgeClientRuntimeCloseable, BridgeClientKotlinx_coroutines_coreFlowCollector, BridgeClientSqliter_driverDatabaseManager, BridgeClientKotlinx_coroutines_coreSharedFlow, BridgeClientKotlinx_serialization_coreCompositeEncoder, BridgeClientKotlinAnnotation, BridgeClientKotlinx_serialization_coreCompositeDecoder, BridgeClientKoin_coreScopeCallback, BridgeClientKotlinKDeclarationContainer, BridgeClientKotlinKAnnotatedElement, BridgeClientKotlinKClassifier, BridgeClientKotlinx_coroutines_coreParentJob, BridgeClientKotlinx_coroutines_coreSelectInstance, BridgeClientKotlinx_coroutines_coreSelectClause, BridgeClientKotlinSuspendFunction2, BridgeClientSqliter_driverDatabaseConnection, BridgeClientKotlinx_serialization_coreSerializersModuleCollector, BridgeClientKtor_httpHeaders, BridgeClientKotlinContinuation, BridgeClientKotlinContinuationInterceptor, BridgeClientKotlinx_coroutines_coreRunnable, BridgeClientKotlinFunction, BridgeClientKtor_httpHttpMessage, BridgeClientKtor_ioByteReadChannel, BridgeClientKtor_httpHttpMessageBuilder, BridgeClientKtor_client_coreHttpRequest, BridgeClientSqliter_driverStatement, BridgeClientSqliter_driverLogger, BridgeClientKtor_httpParameters, BridgeClientKotlinMapEntry, BridgeClientKtor_utilsStringValues, BridgeClientKtor_ioReadSession, BridgeClientKotlinSuspendFunction1, BridgeClientKotlinAppendable, BridgeClientKtor_utilsStringValuesBuilder, BridgeClientKtor_httpParametersBuilder, BridgeClientKotlinKType, BridgeClientSqliter_driverCursor, BridgeClientKtor_ioObjectPool; @@ -2069,10 +2069,8 @@ __attribute__((swift_name("UploadMetadata"))) @property (class, readonly, getter=companion) BridgeClientUploadMetadataCompanion *companion __attribute__((swift_name("companion"))); - (BridgeClientUploadMetadata *)doCopyInstanceGuid:(NSString * _Nullable)instanceGuid eventTimestamp:(NSString * _Nullable)eventTimestamp startedOn:(NSString * _Nullable)startedOn __attribute__((swift_name("doCopy(instanceGuid:eventTimestamp:startedOn:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); -- (NSString * _Nullable)getId __attribute__((swift_name("getId()"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); -- (NSDictionary *)toStringMap __attribute__((swift_name("toStringMap()"))); @property (readonly) NSString * _Nullable eventTimestamp __attribute__((swift_name("eventTimestamp"))); @property (readonly) NSString * _Nullable instanceGuid __attribute__((swift_name("instanceGuid"))); @property (readonly) NSString * _Nullable startedOn __attribute__((swift_name("startedOn"))); @@ -2119,6 +2117,36 @@ __attribute__((swift_name("UploadSession.Companion"))) @end +/** + * @note annotations + * kotlinx.serialization.Serializable +*/ +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord"))) +@interface BridgeClientUploadedFileRecord : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("init(filePath:uploadTimestamp:uploadSessionId:metadata:)"))) __attribute__((objc_designated_initializer)); +@property (class, readonly, getter=companion) BridgeClientUploadedFileRecordCompanion *companion __attribute__((swift_name("companion"))); +- (BridgeClientUploadedFileRecord *)doCopyFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("doCopy(filePath:uploadTimestamp:uploadSessionId:metadata:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) BridgeClientUploadMetadata * _Nullable metadata __attribute__((swift_name("metadata"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@property (readonly) BridgeClientKotlinx_datetimeInstant *uploadTimestamp __attribute__((swift_name("uploadTimestamp"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord.Companion"))) +@interface BridgeClientUploadedFileRecordCompanion : BridgeClientBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) BridgeClientUploadedFileRecordCompanion *shared __attribute__((swift_name("shared"))); +- (id)serializer __attribute__((swift_name("serializer()"))); +@end + + /** * @note annotations * kotlinx.serialization.Serializable @@ -2498,8 +2526,11 @@ __attribute__((swift_name("NativeUploadManager"))) @interface BridgeClientNativeUploadManager : BridgeClientBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); -- (NSArray *)getUploadFiles __attribute__((swift_name("getUploadFiles()"))); -- (void)markUploadFileFinishedFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:callBack:)"))); +- (void)getPendingUploadFilesCallBack:(void (^)(NSArray *))callBack __attribute__((swift_name("getPendingUploadFiles(callBack:)"))); +- (BOOL)hasMarkedFileAsUploadedFilePath:(NSString *)filePath __attribute__((swift_name("hasMarkedFileAsUploaded(filePath:)"))); +- (BOOL)hasPendingUploads __attribute__((swift_name("hasPendingUploads()"))); +- (void)markUploadFileFinishedFilePath:(NSString *)filePath uploadSessionId:(NSString *)uploadSessionId callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:uploadSessionId:callBack:)"))); +- (void)markUploadUnrecoverableFailureFilePath:(NSString *)filePath __attribute__((swift_name("markUploadUnrecoverableFailure(filePath:)"))); - (void)processFinishedUploadsCallBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("processFinishedUploads(callBack:)"))); - (void)queueAndRequestUploadSessionUploadFile:(BridgeClientUploadFile *)uploadFile callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("queueAndRequestUploadSession(uploadFile:callBack:)"))); - (void)requestUploadSessionFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("requestUploadSession(filePath:callBack:)"))); @@ -3095,6 +3126,7 @@ __attribute__((swift_name("ResourceType"))) @property (class, readonly) BridgeClientResourceType *study __attribute__((swift_name("study"))); @property (class, readonly) BridgeClientResourceType *studyInfo __attribute__((swift_name("studyInfo"))); @property (class, readonly) BridgeClientResourceType *participantReport __attribute__((swift_name("participantReport"))); +@property (class, readonly) BridgeClientResourceType *uploadedFileRecord __attribute__((swift_name("uploadedFileRecord"))); + (BridgeClientKotlinArray *)values __attribute__((swift_name("values()"))); @end @@ -3477,6 +3509,18 @@ __attribute__((swift_name("ParticipantScheduleMutator"))) - (BridgeClientParticipantSchedule *)mutateParticipantScheduleParticipantSchedule:(BridgeClientParticipantSchedule *)participantSchedule __attribute__((swift_name("mutateParticipantSchedule(participantSchedule:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("PendingUploadFile"))) +@interface BridgeClientPendingUploadFile : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("init(filePath:uploadSessionId:)"))) __attribute__((objc_designated_initializer)); +- (BridgeClientPendingUploadFile *)doCopyFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("doCopy(filePath:uploadSessionId:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Report"))) @interface BridgeClientReport : BridgeClientBase diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient index de910a5f3..953c44435 100644 Binary files a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient and b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient differ diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient index 44990561b..35891e56c 100755 Binary files a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient and b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient differ diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h index ec804d922..0077b7c94 100644 --- a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h +++ b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h @@ -6,7 +6,7 @@ #import #import -@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; +@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUploadedFileRecordCompanion, BridgeClientUploadedFileRecord, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientPendingUploadFile, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; @protocol BridgeClientPlatformConfig, BridgeClientBridgeConfig, BridgeClientIOSPlatformConfig, BridgeClientNativeLogWriter, BridgeClientKotlinComparable, BridgeClientKotlinx_serialization_coreKSerializer, BridgeClientUploadFileIdentifiable, BridgeClientKoin_coreKoinComponent, BridgeClientParticipantScheduleMutator, BridgeClientKotlinx_coroutines_coreJob, BridgeClientEtagStorageCache, BridgeClientKtor_client_coreHttpClientPlugin, BridgeClientHttpUtil, BridgeClientRuntimeColumnAdapter, BridgeClientRuntimeTransactionWithoutReturn, BridgeClientRuntimeTransactionWithReturn, BridgeClientRuntimeTransacterBase, BridgeClientRuntimeTransacter, BridgeClientBridgeResourceDatabase, BridgeClientRuntimeSqlDriver, BridgeClientRuntimeSqlSchema, BridgeClientDbDriverFactory, BridgeClientKotlinx_coroutines_coreFlow, BridgeClientKotlinx_coroutines_coreCoroutineScope, BridgeClientBridgeErrorStatusNotifier, BridgeClientAuthenticationProvider, BridgeClientKotlinx_coroutines_coreStateFlow, BridgeClientKotlinIterator, BridgeClientKotlinx_serialization_coreEncoder, BridgeClientKotlinx_serialization_coreSerialDescriptor, BridgeClientKotlinx_serialization_coreSerializationStrategy, BridgeClientKotlinx_serialization_coreDecoder, BridgeClientKotlinx_serialization_coreDeserializationStrategy, BridgeClientKoin_coreKoinScopeComponent, BridgeClientKoin_coreQualifier, BridgeClientKotlinKClass, BridgeClientKotlinLazy, BridgeClientKotlinx_coroutines_coreChildHandle, BridgeClientKotlinx_coroutines_coreChildJob, BridgeClientKotlinx_coroutines_coreDisposableHandle, BridgeClientKotlinSequence, BridgeClientKotlinx_coroutines_coreSelectClause0, BridgeClientKotlinCoroutineContextKey, BridgeClientKotlinCoroutineContextElement, BridgeClientKotlinCoroutineContext, BridgeClientKtor_ioCloseable, BridgeClientKtor_client_coreHttpClientEngine, BridgeClientKtor_client_coreHttpClientEngineCapability, BridgeClientKtor_utilsAttributes, BridgeClientRuntimeTransactionCallbacks, BridgeClientRuntimeQueryListener, BridgeClientRuntimeQueryResult, BridgeClientRuntimeSqlPreparedStatement, BridgeClientRuntimeSqlCursor, BridgeClientRuntimeCloseable, BridgeClientKotlinx_coroutines_coreFlowCollector, BridgeClientSqliter_driverDatabaseManager, BridgeClientKotlinx_coroutines_coreSharedFlow, BridgeClientKotlinx_serialization_coreCompositeEncoder, BridgeClientKotlinAnnotation, BridgeClientKotlinx_serialization_coreCompositeDecoder, BridgeClientKoin_coreScopeCallback, BridgeClientKotlinKDeclarationContainer, BridgeClientKotlinKAnnotatedElement, BridgeClientKotlinKClassifier, BridgeClientKotlinx_coroutines_coreParentJob, BridgeClientKotlinx_coroutines_coreSelectInstance, BridgeClientKotlinx_coroutines_coreSelectClause, BridgeClientKotlinSuspendFunction2, BridgeClientSqliter_driverDatabaseConnection, BridgeClientKotlinx_serialization_coreSerializersModuleCollector, BridgeClientKtor_httpHeaders, BridgeClientKotlinContinuation, BridgeClientKotlinContinuationInterceptor, BridgeClientKotlinx_coroutines_coreRunnable, BridgeClientKotlinFunction, BridgeClientKtor_httpHttpMessage, BridgeClientKtor_ioByteReadChannel, BridgeClientKtor_httpHttpMessageBuilder, BridgeClientKtor_client_coreHttpRequest, BridgeClientSqliter_driverStatement, BridgeClientSqliter_driverLogger, BridgeClientKtor_httpParameters, BridgeClientKotlinMapEntry, BridgeClientKtor_utilsStringValues, BridgeClientKtor_ioReadSession, BridgeClientKotlinSuspendFunction1, BridgeClientKotlinAppendable, BridgeClientKtor_utilsStringValuesBuilder, BridgeClientKtor_httpParametersBuilder, BridgeClientKotlinKType, BridgeClientSqliter_driverCursor, BridgeClientKtor_ioObjectPool; @@ -2069,10 +2069,8 @@ __attribute__((swift_name("UploadMetadata"))) @property (class, readonly, getter=companion) BridgeClientUploadMetadataCompanion *companion __attribute__((swift_name("companion"))); - (BridgeClientUploadMetadata *)doCopyInstanceGuid:(NSString * _Nullable)instanceGuid eventTimestamp:(NSString * _Nullable)eventTimestamp startedOn:(NSString * _Nullable)startedOn __attribute__((swift_name("doCopy(instanceGuid:eventTimestamp:startedOn:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); -- (NSString * _Nullable)getId __attribute__((swift_name("getId()"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); -- (NSDictionary *)toStringMap __attribute__((swift_name("toStringMap()"))); @property (readonly) NSString * _Nullable eventTimestamp __attribute__((swift_name("eventTimestamp"))); @property (readonly) NSString * _Nullable instanceGuid __attribute__((swift_name("instanceGuid"))); @property (readonly) NSString * _Nullable startedOn __attribute__((swift_name("startedOn"))); @@ -2119,6 +2117,36 @@ __attribute__((swift_name("UploadSession.Companion"))) @end +/** + * @note annotations + * kotlinx.serialization.Serializable +*/ +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord"))) +@interface BridgeClientUploadedFileRecord : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("init(filePath:uploadTimestamp:uploadSessionId:metadata:)"))) __attribute__((objc_designated_initializer)); +@property (class, readonly, getter=companion) BridgeClientUploadedFileRecordCompanion *companion __attribute__((swift_name("companion"))); +- (BridgeClientUploadedFileRecord *)doCopyFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("doCopy(filePath:uploadTimestamp:uploadSessionId:metadata:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) BridgeClientUploadMetadata * _Nullable metadata __attribute__((swift_name("metadata"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@property (readonly) BridgeClientKotlinx_datetimeInstant *uploadTimestamp __attribute__((swift_name("uploadTimestamp"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord.Companion"))) +@interface BridgeClientUploadedFileRecordCompanion : BridgeClientBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) BridgeClientUploadedFileRecordCompanion *shared __attribute__((swift_name("shared"))); +- (id)serializer __attribute__((swift_name("serializer()"))); +@end + + /** * @note annotations * kotlinx.serialization.Serializable @@ -2498,8 +2526,11 @@ __attribute__((swift_name("NativeUploadManager"))) @interface BridgeClientNativeUploadManager : BridgeClientBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); -- (NSArray *)getUploadFiles __attribute__((swift_name("getUploadFiles()"))); -- (void)markUploadFileFinishedFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:callBack:)"))); +- (void)getPendingUploadFilesCallBack:(void (^)(NSArray *))callBack __attribute__((swift_name("getPendingUploadFiles(callBack:)"))); +- (BOOL)hasMarkedFileAsUploadedFilePath:(NSString *)filePath __attribute__((swift_name("hasMarkedFileAsUploaded(filePath:)"))); +- (BOOL)hasPendingUploads __attribute__((swift_name("hasPendingUploads()"))); +- (void)markUploadFileFinishedFilePath:(NSString *)filePath uploadSessionId:(NSString *)uploadSessionId callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:uploadSessionId:callBack:)"))); +- (void)markUploadUnrecoverableFailureFilePath:(NSString *)filePath __attribute__((swift_name("markUploadUnrecoverableFailure(filePath:)"))); - (void)processFinishedUploadsCallBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("processFinishedUploads(callBack:)"))); - (void)queueAndRequestUploadSessionUploadFile:(BridgeClientUploadFile *)uploadFile callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("queueAndRequestUploadSession(uploadFile:callBack:)"))); - (void)requestUploadSessionFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("requestUploadSession(filePath:callBack:)"))); @@ -3095,6 +3126,7 @@ __attribute__((swift_name("ResourceType"))) @property (class, readonly) BridgeClientResourceType *study __attribute__((swift_name("study"))); @property (class, readonly) BridgeClientResourceType *studyInfo __attribute__((swift_name("studyInfo"))); @property (class, readonly) BridgeClientResourceType *participantReport __attribute__((swift_name("participantReport"))); +@property (class, readonly) BridgeClientResourceType *uploadedFileRecord __attribute__((swift_name("uploadedFileRecord"))); + (BridgeClientKotlinArray *)values __attribute__((swift_name("values()"))); @end @@ -3477,6 +3509,18 @@ __attribute__((swift_name("ParticipantScheduleMutator"))) - (BridgeClientParticipantSchedule *)mutateParticipantScheduleParticipantSchedule:(BridgeClientParticipantSchedule *)participantSchedule __attribute__((swift_name("mutateParticipantSchedule(participantSchedule:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("PendingUploadFile"))) +@interface BridgeClientPendingUploadFile : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("init(filePath:uploadSessionId:)"))) __attribute__((objc_designated_initializer)); +- (BridgeClientPendingUploadFile *)doCopyFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("doCopy(filePath:uploadSessionId:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Report"))) @interface BridgeClientReport : BridgeClientBase diff --git a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient index 7abf3f532..3f727d42e 100644 Binary files a/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient and b/SwiftPackage/Binaries/debug/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient differ diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient index 4b303d4a1..00b4d7b7c 100755 Binary files a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient and b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/BridgeClient differ diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h index ec804d922..0077b7c94 100644 --- a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h +++ b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/BridgeClient.framework/Headers/BridgeClient.h @@ -6,7 +6,7 @@ #import #import -@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; +@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUploadedFileRecordCompanion, BridgeClientUploadedFileRecord, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientPendingUploadFile, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; @protocol BridgeClientPlatformConfig, BridgeClientBridgeConfig, BridgeClientIOSPlatformConfig, BridgeClientNativeLogWriter, BridgeClientKotlinComparable, BridgeClientKotlinx_serialization_coreKSerializer, BridgeClientUploadFileIdentifiable, BridgeClientKoin_coreKoinComponent, BridgeClientParticipantScheduleMutator, BridgeClientKotlinx_coroutines_coreJob, BridgeClientEtagStorageCache, BridgeClientKtor_client_coreHttpClientPlugin, BridgeClientHttpUtil, BridgeClientRuntimeColumnAdapter, BridgeClientRuntimeTransactionWithoutReturn, BridgeClientRuntimeTransactionWithReturn, BridgeClientRuntimeTransacterBase, BridgeClientRuntimeTransacter, BridgeClientBridgeResourceDatabase, BridgeClientRuntimeSqlDriver, BridgeClientRuntimeSqlSchema, BridgeClientDbDriverFactory, BridgeClientKotlinx_coroutines_coreFlow, BridgeClientKotlinx_coroutines_coreCoroutineScope, BridgeClientBridgeErrorStatusNotifier, BridgeClientAuthenticationProvider, BridgeClientKotlinx_coroutines_coreStateFlow, BridgeClientKotlinIterator, BridgeClientKotlinx_serialization_coreEncoder, BridgeClientKotlinx_serialization_coreSerialDescriptor, BridgeClientKotlinx_serialization_coreSerializationStrategy, BridgeClientKotlinx_serialization_coreDecoder, BridgeClientKotlinx_serialization_coreDeserializationStrategy, BridgeClientKoin_coreKoinScopeComponent, BridgeClientKoin_coreQualifier, BridgeClientKotlinKClass, BridgeClientKotlinLazy, BridgeClientKotlinx_coroutines_coreChildHandle, BridgeClientKotlinx_coroutines_coreChildJob, BridgeClientKotlinx_coroutines_coreDisposableHandle, BridgeClientKotlinSequence, BridgeClientKotlinx_coroutines_coreSelectClause0, BridgeClientKotlinCoroutineContextKey, BridgeClientKotlinCoroutineContextElement, BridgeClientKotlinCoroutineContext, BridgeClientKtor_ioCloseable, BridgeClientKtor_client_coreHttpClientEngine, BridgeClientKtor_client_coreHttpClientEngineCapability, BridgeClientKtor_utilsAttributes, BridgeClientRuntimeTransactionCallbacks, BridgeClientRuntimeQueryListener, BridgeClientRuntimeQueryResult, BridgeClientRuntimeSqlPreparedStatement, BridgeClientRuntimeSqlCursor, BridgeClientRuntimeCloseable, BridgeClientKotlinx_coroutines_coreFlowCollector, BridgeClientSqliter_driverDatabaseManager, BridgeClientKotlinx_coroutines_coreSharedFlow, BridgeClientKotlinx_serialization_coreCompositeEncoder, BridgeClientKotlinAnnotation, BridgeClientKotlinx_serialization_coreCompositeDecoder, BridgeClientKoin_coreScopeCallback, BridgeClientKotlinKDeclarationContainer, BridgeClientKotlinKAnnotatedElement, BridgeClientKotlinKClassifier, BridgeClientKotlinx_coroutines_coreParentJob, BridgeClientKotlinx_coroutines_coreSelectInstance, BridgeClientKotlinx_coroutines_coreSelectClause, BridgeClientKotlinSuspendFunction2, BridgeClientSqliter_driverDatabaseConnection, BridgeClientKotlinx_serialization_coreSerializersModuleCollector, BridgeClientKtor_httpHeaders, BridgeClientKotlinContinuation, BridgeClientKotlinContinuationInterceptor, BridgeClientKotlinx_coroutines_coreRunnable, BridgeClientKotlinFunction, BridgeClientKtor_httpHttpMessage, BridgeClientKtor_ioByteReadChannel, BridgeClientKtor_httpHttpMessageBuilder, BridgeClientKtor_client_coreHttpRequest, BridgeClientSqliter_driverStatement, BridgeClientSqliter_driverLogger, BridgeClientKtor_httpParameters, BridgeClientKotlinMapEntry, BridgeClientKtor_utilsStringValues, BridgeClientKtor_ioReadSession, BridgeClientKotlinSuspendFunction1, BridgeClientKotlinAppendable, BridgeClientKtor_utilsStringValuesBuilder, BridgeClientKtor_httpParametersBuilder, BridgeClientKotlinKType, BridgeClientSqliter_driverCursor, BridgeClientKtor_ioObjectPool; @@ -2069,10 +2069,8 @@ __attribute__((swift_name("UploadMetadata"))) @property (class, readonly, getter=companion) BridgeClientUploadMetadataCompanion *companion __attribute__((swift_name("companion"))); - (BridgeClientUploadMetadata *)doCopyInstanceGuid:(NSString * _Nullable)instanceGuid eventTimestamp:(NSString * _Nullable)eventTimestamp startedOn:(NSString * _Nullable)startedOn __attribute__((swift_name("doCopy(instanceGuid:eventTimestamp:startedOn:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); -- (NSString * _Nullable)getId __attribute__((swift_name("getId()"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); -- (NSDictionary *)toStringMap __attribute__((swift_name("toStringMap()"))); @property (readonly) NSString * _Nullable eventTimestamp __attribute__((swift_name("eventTimestamp"))); @property (readonly) NSString * _Nullable instanceGuid __attribute__((swift_name("instanceGuid"))); @property (readonly) NSString * _Nullable startedOn __attribute__((swift_name("startedOn"))); @@ -2119,6 +2117,36 @@ __attribute__((swift_name("UploadSession.Companion"))) @end +/** + * @note annotations + * kotlinx.serialization.Serializable +*/ +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord"))) +@interface BridgeClientUploadedFileRecord : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("init(filePath:uploadTimestamp:uploadSessionId:metadata:)"))) __attribute__((objc_designated_initializer)); +@property (class, readonly, getter=companion) BridgeClientUploadedFileRecordCompanion *companion __attribute__((swift_name("companion"))); +- (BridgeClientUploadedFileRecord *)doCopyFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("doCopy(filePath:uploadTimestamp:uploadSessionId:metadata:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) BridgeClientUploadMetadata * _Nullable metadata __attribute__((swift_name("metadata"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@property (readonly) BridgeClientKotlinx_datetimeInstant *uploadTimestamp __attribute__((swift_name("uploadTimestamp"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord.Companion"))) +@interface BridgeClientUploadedFileRecordCompanion : BridgeClientBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) BridgeClientUploadedFileRecordCompanion *shared __attribute__((swift_name("shared"))); +- (id)serializer __attribute__((swift_name("serializer()"))); +@end + + /** * @note annotations * kotlinx.serialization.Serializable @@ -2498,8 +2526,11 @@ __attribute__((swift_name("NativeUploadManager"))) @interface BridgeClientNativeUploadManager : BridgeClientBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); -- (NSArray *)getUploadFiles __attribute__((swift_name("getUploadFiles()"))); -- (void)markUploadFileFinishedFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:callBack:)"))); +- (void)getPendingUploadFilesCallBack:(void (^)(NSArray *))callBack __attribute__((swift_name("getPendingUploadFiles(callBack:)"))); +- (BOOL)hasMarkedFileAsUploadedFilePath:(NSString *)filePath __attribute__((swift_name("hasMarkedFileAsUploaded(filePath:)"))); +- (BOOL)hasPendingUploads __attribute__((swift_name("hasPendingUploads()"))); +- (void)markUploadFileFinishedFilePath:(NSString *)filePath uploadSessionId:(NSString *)uploadSessionId callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:uploadSessionId:callBack:)"))); +- (void)markUploadUnrecoverableFailureFilePath:(NSString *)filePath __attribute__((swift_name("markUploadUnrecoverableFailure(filePath:)"))); - (void)processFinishedUploadsCallBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("processFinishedUploads(callBack:)"))); - (void)queueAndRequestUploadSessionUploadFile:(BridgeClientUploadFile *)uploadFile callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("queueAndRequestUploadSession(uploadFile:callBack:)"))); - (void)requestUploadSessionFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("requestUploadSession(filePath:callBack:)"))); @@ -3095,6 +3126,7 @@ __attribute__((swift_name("ResourceType"))) @property (class, readonly) BridgeClientResourceType *study __attribute__((swift_name("study"))); @property (class, readonly) BridgeClientResourceType *studyInfo __attribute__((swift_name("studyInfo"))); @property (class, readonly) BridgeClientResourceType *participantReport __attribute__((swift_name("participantReport"))); +@property (class, readonly) BridgeClientResourceType *uploadedFileRecord __attribute__((swift_name("uploadedFileRecord"))); + (BridgeClientKotlinArray *)values __attribute__((swift_name("values()"))); @end @@ -3477,6 +3509,18 @@ __attribute__((swift_name("ParticipantScheduleMutator"))) - (BridgeClientParticipantSchedule *)mutateParticipantScheduleParticipantSchedule:(BridgeClientParticipantSchedule *)participantSchedule __attribute__((swift_name("mutateParticipantSchedule(participantSchedule:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("PendingUploadFile"))) +@interface BridgeClientPendingUploadFile : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("init(filePath:uploadSessionId:)"))) __attribute__((objc_designated_initializer)); +- (BridgeClientPendingUploadFile *)doCopyFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("doCopy(filePath:uploadSessionId:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Report"))) @interface BridgeClientReport : BridgeClientBase diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient index 7db6335a4..7177054ef 100644 Binary files a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient and b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient differ diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient index 18231a321..08d3f7ac1 100755 Binary files a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient and b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/BridgeClient differ diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h index ec804d922..0077b7c94 100644 --- a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h +++ b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/BridgeClient.framework/Headers/BridgeClient.h @@ -6,7 +6,7 @@ #import #import -@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; +@class BridgeClientPlatformConfigBridgeEnvironment, BridgeClientIOSBridgeConfig, BridgeClientIOSLogger, NSData, BridgeClientAdherenceRecord, BridgeClientAppConfig, BridgeClientKotlinx_serialization_jsonJsonElement, BridgeClientStudy, BridgeClientUserSessionInfo, BridgeClientKotlinEnumCompanion, BridgeClientKotlinEnum, BridgeClientLogSeverity, BridgeClientKotlinArray, NSError, BridgeClientAccountStatusCompanion, BridgeClientAccountStatus, BridgeClientAddressCompanion, BridgeClientAddress, BridgeClientKotlinx_datetimeInstant, BridgeClientAdherenceRecordCompanion, BridgeClientAdherenceRecordTypeCompanion, BridgeClientAdherenceRecordType, BridgeClientAdherenceRecordUpdatesCompanion, BridgeClientAdherenceRecordUpdates, BridgeClientSortOrder, BridgeClientAdherenceRecordsSearchCompanion, BridgeClientAdherenceRecordsSearch, BridgeClientCriteria, BridgeClientSurveyReference, BridgeClientSchemaReference, BridgeClientConfigReference, BridgeClientFileReference, BridgeClientAppConfigCompanion, BridgeClientAssessmentConfigCompanion, BridgeClientAssessmentConfig, BridgeClientColorScheme, BridgeClientImageResource, BridgeClientAssessmentInfoCompanion, BridgeClientAssessmentInfo, BridgeClientClientInfoCompanion, BridgeClientClientInfo, BridgeClientColorSchemeCompanion, BridgeClientConfigReferenceCompanion, BridgeClientConsentStatusCompanion, BridgeClientConsentStatus, BridgeClientContactRole, BridgeClientPhone, BridgeClientContactCompanion, BridgeClientContact, BridgeClientContactRoleCompanion, BridgeClientCriteriaCompanion, BridgeClientDateRangeCompanion, BridgeClientDateRange, BridgeClientEnrollmentInfoCompanion, BridgeClientEnrollmentInfo, BridgeClientEnvironmentCompanion, BridgeClientEnvironment, BridgeClientFileReferenceCompanion, BridgeClientIdentifierCompanion, BridgeClientIdentifier, BridgeClientLabel, BridgeClientImageResourceCompanion, BridgeClientIrbDecisionTypeCompanion, BridgeClientIrbDecisionType, BridgeClientLabelCompanion, BridgeClientNotificationType, BridgeClientNotificationMessage, BridgeClientNotificationInfoCompanion, BridgeClientNotificationInfo, BridgeClientNotificationMessageCompanion, BridgeClientNotificationTypeCompanion, BridgeClientParticipantDataCompanion, BridgeClientParticipantData, BridgeClientScheduledSession, BridgeClientSessionInfo, BridgeClientStudyBurstInfo, BridgeClientParticipantScheduleCompanion, BridgeClientParticipantSchedule, BridgeClientPerformanceOrderCompanion, BridgeClientPerformanceOrder, BridgeClientPhoneCompanion, BridgeClientPhoneSignInRequestCompanion, BridgeClientPhoneSignInRequest, BridgeClientPhoneSigninCompanion, BridgeClientPhoneSignin, BridgeClientReminderTypeCompanion, BridgeClientReminderType, BridgeClientRoleCompanion, BridgeClientRole, BridgeClientS3UploadSession, BridgeClientS3UploadType, BridgeClientScheduledAssessmentCompanion, BridgeClientScheduledAssessment, BridgeClientKotlinx_datetimeLocalTime, BridgeClientKotlinx_datetimeDateTimePeriod, BridgeClientKotlinx_datetimeLocalDate, BridgeClientScheduledSessionCompanion, BridgeClientKotlinx_datetimeLocalDateTime, BridgeClientSchemaReferenceCompanion, BridgeClientSessionInfoCompanion, BridgeClientSharingScopeCompanion, BridgeClientSharingScope, BridgeClientSignInTypeCompanion, BridgeClientSignInType, BridgeClientSortOrderCompanion, BridgeClientStudyPhase, BridgeClientStudyCompanion, BridgeClientStudyActivityEventCompanion, BridgeClientStudyActivityEvent, BridgeClientStudyActivityEventListCompanion, BridgeClientStudyActivityEventList, BridgeClientStudyActivityEventRequestCompanion, BridgeClientStudyBurstInfoCompanion, BridgeClientStudyInfoCompanion, BridgeClientStudyInfo, BridgeClientUserConsentHistory, BridgeClientStudyParticipantCompanion, BridgeClientStudyParticipant, BridgeClientStudyPhaseCompanion, BridgeClientSurveyReferenceCompanion, BridgeClientTimelineCompanion, BridgeClientTimeline, BridgeClientUploadMetadata, BridgeClientUploadFileCompanion, BridgeClientUploadFile, BridgeClientUploadMetadataCompanion, BridgeClientUploadSessionCompanion, BridgeClientUploadSession, BridgeClientUploadedFileRecordCompanion, BridgeClientUploadedFileRecord, BridgeClientUserConsentHistoryCompanion, BridgeClientUserSessionInfoCompanion, BridgeClientKoin_coreKoin, BridgeClientNativeAssessmentConfig, NSDate, BridgeClientNativeAdherenceRecord, BridgeClientResourceStatus, BridgeClientAppStatus, BridgeClientParticipantRepoUpdateParticipantRecord, BridgeClientKtor_httpHttpStatusCode, BridgeClientKotlinError, BridgeClientUserSessionState, BridgeClientAssessmentHistoryRecord, BridgeClientNativeParticipantDataRecord, BridgeClientNativeScheduledAssessment, NSDateComponents, BridgeClientNativeScheduledNotification, NSTimeZone, BridgeClientNativeScheduledSessionWindow, BridgeClientNativeScheduledSessionTimelineSlice, BridgeClientNativeStudyBurst, BridgeClientNativeStudyBurstSchedule, BridgeClientAbstractNativeTimelineManager, BridgeClientPendingUploadFile, BridgeClientEtagFeatureFeature, BridgeClientEtagFeature, BridgeClientKtor_client_coreHttpClient, BridgeClientKtor_utilsAttributeKey, BridgeClientEtagFeatureConfig, BridgeClientAdherenceRecords, BridgeClientAllAssessmentAdherence, BridgeClientBridgeResourceDatabaseQueries, BridgeClientLocalDataCacheQueries, BridgeClientParticipantScheduleQueries, BridgeClientBridgeResourceDatabaseCompanion, BridgeClientAdherenceRecordsAdapter, BridgeClientJsonDataAdapter, BridgeClientResourceAdapter, BridgeClientKotlinUnit, BridgeClientRuntimeTransacterTransaction, BridgeClientKotlinThrowable, BridgeClientRuntimeBaseTransacterImpl, BridgeClientRuntimeTransacterImpl, BridgeClientRuntimeQuery<__covariant RowType>, BridgeClientResourceType, BridgeClientResource, BridgeClientEtagCache, BridgeClientCompletedAssessmentAdherence, BridgeClientFullScheduledSessions, BridgeClientGroupedFuturePendingNotifications, BridgeClientKotlinx_datetimeTimeZone, BridgeClientJsonData, BridgeClientResourceDatabaseHelper, BridgeClientScheduledNotification, BridgeClientParticipantScheduleDatabaseExpandedScheduledSession, BridgeClientParticipantScheduleDatabaseScheduledAssessment, BridgeClientParticipantScheduleDatabaseScheduledSessionHolder, BridgeClientScheduledNotifications, BridgeClientScheduledAssessmentAdherence, BridgeClientScheduleMetadata, BridgeClientStudyBurstSessions, BridgeClientResourceDatabaseHelperCompanion, BridgeClientResourceResult<__covariant T>, BridgeClientResourceResultFailed, BridgeClientKotlinNothing, BridgeClientResourceResultInProgress, BridgeClientResourceResultSuccess<__covariant T>, BridgeClientScheduledAssessments, BridgeClientScheduledSessions, BridgeClientSqliteDriverFactory, BridgeClientNative_driverNativeSqliteDriver, BridgeClientAbstractResourceRepo, BridgeClientScheduleTimelineRepo, BridgeClientAuthenticationRepository, BridgeClientParticipantRepoUpdateParticipantRecordCompanion, BridgeClientReport, BridgeClientAdherenceRecordRepo, BridgeClientAssessmentConfigRepo, BridgeClientScheduledAssessmentReference, BridgeClientScheduledNotificationCompanion, BridgeClientScheduledSessionWindow, BridgeClientScheduledSessionTimelineSlice, BridgeClientStudyBurst, BridgeClientStudyBurstSchedule, BridgeClientKoin_coreModule, BridgeClientKoin_coreKoinApplication, BridgeClientKotlinx_serialization_jsonJsonElementCompanion, BridgeClientKotlinx_datetimeInstantCompanion, BridgeClientKotlinx_datetimeLocalTimeCompanion, BridgeClientKotlinx_datetimeDateTimePeriodCompanion, BridgeClientKotlinx_datetimeMonth, BridgeClientKotlinx_datetimeLocalDateCompanion, BridgeClientKotlinx_datetimeDayOfWeek, BridgeClientKotlinx_datetimeLocalDateTimeCompanion, BridgeClientKoin_coreScope, BridgeClientKoin_coreParametersHolder, BridgeClientKotlinLazyThreadSafetyMode, BridgeClientKoin_coreLogger, BridgeClientKoin_coreInstanceRegistry, BridgeClientKoin_corePropertyRegistry, BridgeClientKoin_coreScopeRegistry, BridgeClientKtor_httpHttpStatusCodeCompanion, BridgeClientKotlinCancellationException, BridgeClientKtor_client_coreHttpClientEngineConfig, BridgeClientKtor_client_coreHttpClientConfig, BridgeClientKtor_eventsEvents, BridgeClientKtor_client_coreHttpReceivePipeline, BridgeClientKtor_client_coreHttpRequestPipeline, BridgeClientKtor_client_coreHttpResponsePipeline, BridgeClientKtor_client_coreHttpSendPipeline, BridgeClientRuntimeExecutableQuery<__covariant RowType>, BridgeClientKotlinx_datetimeTimeZoneCompanion, BridgeClientNative_driverConnectionWrapper, BridgeClientSqliter_driverDatabaseConfiguration, BridgeClientKotlinException, BridgeClientKotlinRuntimeException, BridgeClientKotlinIllegalStateException, BridgeClientKoin_coreInstanceFactory, BridgeClientKotlinPair<__covariant A, __covariant B>, BridgeClientKoin_coreScopeDSL, BridgeClientKoin_coreSingleInstanceFactory, BridgeClientKoin_coreKoinApplicationCompanion, BridgeClientKoin_coreLevel, BridgeClientKotlinx_serialization_coreSerializersModule, BridgeClientKotlinx_serialization_coreSerialKind, BridgeClientKoin_coreParametersHolderCompanion, BridgeClientKoin_coreScopeRegistryCompanion, BridgeClientKtor_client_coreHttpRequestData, BridgeClientKtor_client_coreHttpResponseData, BridgeClientKotlinx_coroutines_coreCoroutineDispatcher, BridgeClientKtor_client_coreProxyConfig, BridgeClientKtor_eventsEventDefinition, BridgeClientKtor_utilsPipelinePhase, BridgeClientKtor_utilsPipeline, BridgeClientKtor_client_coreHttpReceivePipelinePhases, BridgeClientKtor_client_coreHttpResponse, BridgeClientKtor_client_coreHttpRequestPipelinePhases, BridgeClientKtor_client_coreHttpRequestBuilder, BridgeClientKtor_client_coreHttpResponsePipelinePhases, BridgeClientKtor_client_coreHttpResponseContainer, BridgeClientKtor_client_coreHttpClientCall, BridgeClientKtor_client_coreHttpSendPipelinePhases, BridgeClientKotlinByteArray, BridgeClientKotlinx_datetimeFixedOffsetTimeZone, BridgeClientSqliter_driverJournalMode, BridgeClientSqliter_driverDatabaseConfigurationExtended, BridgeClientSqliter_driverDatabaseConfigurationLogging, BridgeClientSqliter_driverDatabaseConfigurationLifecycle, BridgeClientSqliter_driverDatabaseConfigurationEncryption, BridgeClientKoin_coreBeanDefinition, BridgeClientKoin_coreInstanceFactoryCompanion, BridgeClientKoin_coreInstanceContext, BridgeClientKtor_httpUrl, BridgeClientKtor_httpHttpMethod, BridgeClientKtor_httpOutgoingContent, BridgeClientKtor_utilsGMTDate, BridgeClientKtor_httpHttpProtocolVersion, BridgeClientKotlinAbstractCoroutineContextElement, BridgeClientKotlinx_coroutines_coreCoroutineDispatcherKey, BridgeClientKtor_httpHeadersBuilder, BridgeClientKtor_client_coreHttpRequestBuilderCompanion, BridgeClientKtor_httpURLBuilder, BridgeClientKtor_utilsTypeInfo, BridgeClientKtor_client_coreHttpClientCallCompanion, BridgeClientKotlinByteIterator, BridgeClientKotlinx_datetimeUtcOffset, BridgeClientKotlinx_datetimeFixedOffsetTimeZoneCompanion, BridgeClientSqliter_driverJournalModeCompanion, BridgeClientSqliter_driverSynchronousFlag, BridgeClientKoin_coreKind, BridgeClientKoin_coreCallbacks, BridgeClientKtor_httpUrlCompanion, BridgeClientKtor_httpURLProtocol, BridgeClientKtor_httpHttpMethodCompanion, BridgeClientKtor_httpContentType, BridgeClientKtor_utilsGMTDateCompanion, BridgeClientKtor_utilsWeekDay, BridgeClientKtor_utilsMonth, BridgeClientKtor_httpHttpProtocolVersionCompanion, BridgeClientKotlinAbstractCoroutineContextKey, BridgeClientKtor_ioMemory, BridgeClientKtor_ioChunkBuffer, BridgeClientKtor_ioBuffer, BridgeClientKtor_ioByteReadPacket, BridgeClientKtor_utilsStringValuesBuilderImpl, BridgeClientKtor_httpURLBuilderCompanion, BridgeClientKotlinx_datetimeUtcOffsetCompanion, BridgeClientKtor_httpURLProtocolCompanion, BridgeClientKtor_httpHeaderValueParam, BridgeClientKtor_httpHeaderValueWithParametersCompanion, BridgeClientKtor_httpHeaderValueWithParameters, BridgeClientKtor_httpContentTypeCompanion, BridgeClientKtor_utilsWeekDayCompanion, BridgeClientKtor_utilsMonthCompanion, BridgeClientKtor_ioMemoryCompanion, BridgeClientKtor_ioBufferCompanion, BridgeClientKtor_ioChunkBufferCompanion, BridgeClientKtor_ioInputCompanion, BridgeClientKtor_ioInput, BridgeClientKtor_ioByteReadPacketCompanion, BridgeClientKotlinKTypeProjection, BridgeClientSqliter_driverFieldType, BridgeClientKotlinKVariance, BridgeClientKotlinKTypeProjectionCompanion, BridgeClientSqliter_driverFieldTypeCompanion; @protocol BridgeClientPlatformConfig, BridgeClientBridgeConfig, BridgeClientIOSPlatformConfig, BridgeClientNativeLogWriter, BridgeClientKotlinComparable, BridgeClientKotlinx_serialization_coreKSerializer, BridgeClientUploadFileIdentifiable, BridgeClientKoin_coreKoinComponent, BridgeClientParticipantScheduleMutator, BridgeClientKotlinx_coroutines_coreJob, BridgeClientEtagStorageCache, BridgeClientKtor_client_coreHttpClientPlugin, BridgeClientHttpUtil, BridgeClientRuntimeColumnAdapter, BridgeClientRuntimeTransactionWithoutReturn, BridgeClientRuntimeTransactionWithReturn, BridgeClientRuntimeTransacterBase, BridgeClientRuntimeTransacter, BridgeClientBridgeResourceDatabase, BridgeClientRuntimeSqlDriver, BridgeClientRuntimeSqlSchema, BridgeClientDbDriverFactory, BridgeClientKotlinx_coroutines_coreFlow, BridgeClientKotlinx_coroutines_coreCoroutineScope, BridgeClientBridgeErrorStatusNotifier, BridgeClientAuthenticationProvider, BridgeClientKotlinx_coroutines_coreStateFlow, BridgeClientKotlinIterator, BridgeClientKotlinx_serialization_coreEncoder, BridgeClientKotlinx_serialization_coreSerialDescriptor, BridgeClientKotlinx_serialization_coreSerializationStrategy, BridgeClientKotlinx_serialization_coreDecoder, BridgeClientKotlinx_serialization_coreDeserializationStrategy, BridgeClientKoin_coreKoinScopeComponent, BridgeClientKoin_coreQualifier, BridgeClientKotlinKClass, BridgeClientKotlinLazy, BridgeClientKotlinx_coroutines_coreChildHandle, BridgeClientKotlinx_coroutines_coreChildJob, BridgeClientKotlinx_coroutines_coreDisposableHandle, BridgeClientKotlinSequence, BridgeClientKotlinx_coroutines_coreSelectClause0, BridgeClientKotlinCoroutineContextKey, BridgeClientKotlinCoroutineContextElement, BridgeClientKotlinCoroutineContext, BridgeClientKtor_ioCloseable, BridgeClientKtor_client_coreHttpClientEngine, BridgeClientKtor_client_coreHttpClientEngineCapability, BridgeClientKtor_utilsAttributes, BridgeClientRuntimeTransactionCallbacks, BridgeClientRuntimeQueryListener, BridgeClientRuntimeQueryResult, BridgeClientRuntimeSqlPreparedStatement, BridgeClientRuntimeSqlCursor, BridgeClientRuntimeCloseable, BridgeClientKotlinx_coroutines_coreFlowCollector, BridgeClientSqliter_driverDatabaseManager, BridgeClientKotlinx_coroutines_coreSharedFlow, BridgeClientKotlinx_serialization_coreCompositeEncoder, BridgeClientKotlinAnnotation, BridgeClientKotlinx_serialization_coreCompositeDecoder, BridgeClientKoin_coreScopeCallback, BridgeClientKotlinKDeclarationContainer, BridgeClientKotlinKAnnotatedElement, BridgeClientKotlinKClassifier, BridgeClientKotlinx_coroutines_coreParentJob, BridgeClientKotlinx_coroutines_coreSelectInstance, BridgeClientKotlinx_coroutines_coreSelectClause, BridgeClientKotlinSuspendFunction2, BridgeClientSqliter_driverDatabaseConnection, BridgeClientKotlinx_serialization_coreSerializersModuleCollector, BridgeClientKtor_httpHeaders, BridgeClientKotlinContinuation, BridgeClientKotlinContinuationInterceptor, BridgeClientKotlinx_coroutines_coreRunnable, BridgeClientKotlinFunction, BridgeClientKtor_httpHttpMessage, BridgeClientKtor_ioByteReadChannel, BridgeClientKtor_httpHttpMessageBuilder, BridgeClientKtor_client_coreHttpRequest, BridgeClientSqliter_driverStatement, BridgeClientSqliter_driverLogger, BridgeClientKtor_httpParameters, BridgeClientKotlinMapEntry, BridgeClientKtor_utilsStringValues, BridgeClientKtor_ioReadSession, BridgeClientKotlinSuspendFunction1, BridgeClientKotlinAppendable, BridgeClientKtor_utilsStringValuesBuilder, BridgeClientKtor_httpParametersBuilder, BridgeClientKotlinKType, BridgeClientSqliter_driverCursor, BridgeClientKtor_ioObjectPool; @@ -2069,10 +2069,8 @@ __attribute__((swift_name("UploadMetadata"))) @property (class, readonly, getter=companion) BridgeClientUploadMetadataCompanion *companion __attribute__((swift_name("companion"))); - (BridgeClientUploadMetadata *)doCopyInstanceGuid:(NSString * _Nullable)instanceGuid eventTimestamp:(NSString * _Nullable)eventTimestamp startedOn:(NSString * _Nullable)startedOn __attribute__((swift_name("doCopy(instanceGuid:eventTimestamp:startedOn:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); -- (NSString * _Nullable)getId __attribute__((swift_name("getId()"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); -- (NSDictionary *)toStringMap __attribute__((swift_name("toStringMap()"))); @property (readonly) NSString * _Nullable eventTimestamp __attribute__((swift_name("eventTimestamp"))); @property (readonly) NSString * _Nullable instanceGuid __attribute__((swift_name("instanceGuid"))); @property (readonly) NSString * _Nullable startedOn __attribute__((swift_name("startedOn"))); @@ -2119,6 +2117,36 @@ __attribute__((swift_name("UploadSession.Companion"))) @end +/** + * @note annotations + * kotlinx.serialization.Serializable +*/ +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord"))) +@interface BridgeClientUploadedFileRecord : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("init(filePath:uploadTimestamp:uploadSessionId:metadata:)"))) __attribute__((objc_designated_initializer)); +@property (class, readonly, getter=companion) BridgeClientUploadedFileRecordCompanion *companion __attribute__((swift_name("companion"))); +- (BridgeClientUploadedFileRecord *)doCopyFilePath:(NSString *)filePath uploadTimestamp:(BridgeClientKotlinx_datetimeInstant *)uploadTimestamp uploadSessionId:(NSString * _Nullable)uploadSessionId metadata:(BridgeClientUploadMetadata * _Nullable)metadata __attribute__((swift_name("doCopy(filePath:uploadTimestamp:uploadSessionId:metadata:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) BridgeClientUploadMetadata * _Nullable metadata __attribute__((swift_name("metadata"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@property (readonly) BridgeClientKotlinx_datetimeInstant *uploadTimestamp __attribute__((swift_name("uploadTimestamp"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("UploadedFileRecord.Companion"))) +@interface BridgeClientUploadedFileRecordCompanion : BridgeClientBase ++ (instancetype)alloc __attribute__((unavailable)); ++ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable)); ++ (instancetype)companion __attribute__((swift_name("init()"))); +@property (class, readonly, getter=shared) BridgeClientUploadedFileRecordCompanion *shared __attribute__((swift_name("shared"))); +- (id)serializer __attribute__((swift_name("serializer()"))); +@end + + /** * @note annotations * kotlinx.serialization.Serializable @@ -2498,8 +2526,11 @@ __attribute__((swift_name("NativeUploadManager"))) @interface BridgeClientNativeUploadManager : BridgeClientBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); -- (NSArray *)getUploadFiles __attribute__((swift_name("getUploadFiles()"))); -- (void)markUploadFileFinishedFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:callBack:)"))); +- (void)getPendingUploadFilesCallBack:(void (^)(NSArray *))callBack __attribute__((swift_name("getPendingUploadFiles(callBack:)"))); +- (BOOL)hasMarkedFileAsUploadedFilePath:(NSString *)filePath __attribute__((swift_name("hasMarkedFileAsUploaded(filePath:)"))); +- (BOOL)hasPendingUploads __attribute__((swift_name("hasPendingUploads()"))); +- (void)markUploadFileFinishedFilePath:(NSString *)filePath uploadSessionId:(NSString *)uploadSessionId callBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("markUploadFileFinished(filePath:uploadSessionId:callBack:)"))); +- (void)markUploadUnrecoverableFailureFilePath:(NSString *)filePath __attribute__((swift_name("markUploadUnrecoverableFailure(filePath:)"))); - (void)processFinishedUploadsCallBack:(void (^)(BridgeClientBoolean *))callBack __attribute__((swift_name("processFinishedUploads(callBack:)"))); - (void)queueAndRequestUploadSessionUploadFile:(BridgeClientUploadFile *)uploadFile callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("queueAndRequestUploadSession(uploadFile:callBack:)"))); - (void)requestUploadSessionFilePath:(NSString *)filePath callBack:(void (^)(BridgeClientS3UploadSession * _Nullable))callBack __attribute__((swift_name("requestUploadSession(filePath:callBack:)"))); @@ -3095,6 +3126,7 @@ __attribute__((swift_name("ResourceType"))) @property (class, readonly) BridgeClientResourceType *study __attribute__((swift_name("study"))); @property (class, readonly) BridgeClientResourceType *studyInfo __attribute__((swift_name("studyInfo"))); @property (class, readonly) BridgeClientResourceType *participantReport __attribute__((swift_name("participantReport"))); +@property (class, readonly) BridgeClientResourceType *uploadedFileRecord __attribute__((swift_name("uploadedFileRecord"))); + (BridgeClientKotlinArray *)values __attribute__((swift_name("values()"))); @end @@ -3477,6 +3509,18 @@ __attribute__((swift_name("ParticipantScheduleMutator"))) - (BridgeClientParticipantSchedule *)mutateParticipantScheduleParticipantSchedule:(BridgeClientParticipantSchedule *)participantSchedule __attribute__((swift_name("mutateParticipantSchedule(participantSchedule:)"))); @end +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("PendingUploadFile"))) +@interface BridgeClientPendingUploadFile : BridgeClientBase +- (instancetype)initWithFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("init(filePath:uploadSessionId:)"))) __attribute__((objc_designated_initializer)); +- (BridgeClientPendingUploadFile *)doCopyFilePath:(NSString *)filePath uploadSessionId:(NSString * _Nullable)uploadSessionId __attribute__((swift_name("doCopy(filePath:uploadSessionId:)"))); +- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); +- (NSUInteger)hash __attribute__((swift_name("hash()"))); +- (NSString *)description __attribute__((swift_name("description()"))); +@property (readonly) NSString *filePath __attribute__((swift_name("filePath"))); +@property (readonly) NSString * _Nullable uploadSessionId __attribute__((swift_name("uploadSessionId"))); +@end + __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Report"))) @interface BridgeClientReport : BridgeClientBase diff --git a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient index fb4f5432d..b4692746c 100644 Binary files a/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient and b/SwiftPackage/Binaries/release/BridgeClient.xcframework/ios-arm64_x86_64-simulator/dSYMs/BridgeClient.framework.dSYM/Contents/Resources/DWARF/BridgeClient differ diff --git a/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/DataArchive.swift b/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/DataArchive.swift index ee4a26088..733324ae1 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/DataArchive.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/DataArchive.swift @@ -216,8 +216,11 @@ open class DataArchive : NSObject, Identifiable { fileprivate let kBridgeV2InfoFilename = "info.json" +/// The info.json file is required for Exporter V1/V2 and by the Bridge Study +/// Manager in order to see an archive marked as "complete". public struct BridgeUploaderInfoV2 : Encodable { + let createdOn: String = ISO8601TimestampFormatter.string(from: Date()) let files: [FileEntry] let dataFilename: String let format: FormatVersion diff --git a/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/ScheduledAssessmentHandler.swift b/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/ScheduledAssessmentHandler.swift index 813497d2d..a8ddc2d24 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/ScheduledAssessmentHandler.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/Data Archiving/ScheduledAssessmentHandler.swift @@ -83,6 +83,13 @@ public struct AssessmentScheduleInfo : Identifiable, Hashable, Codable { label: assessment.assessmentInfo.label) } + /// Internal init that can be used for testing. + init(instanceGuid: String, session: Session, assessmentInfo: Info) { + self.instanceGuid = instanceGuid + self.session = session + self.assessmentInfo = assessmentInfo + } + /// A thread-safe struct with information required to load and save the results from an assessment. public struct Info : Hashable, Codable { /// The ``BridgeClient.AssessmentInfo/identifier``. diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/ArchiveUploadProcessor.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/ArchiveUploadProcessor.swift index 3dd47d4c7..91de60603 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/ArchiveUploadProcessor.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/ArchiveUploadProcessor.swift @@ -5,15 +5,20 @@ import Foundation import BridgeClient import JsonModel +#if canImport(UIKit) +import UIKit +#endif + fileprivate let UnprocessedUploadsKey = "unprocessedUploads" +/// This is a processor to use to handle archiving data and uploading in the background. final class ArchiveUploadProcessor { let pemPath: String? let sharedUserDefaults: UserDefaults var isTestUser: Bool = false - weak var uploadManager: BridgeFileUploadManager! + weak var uploadManager: BridgeUploader! init(pemPath: String?, sharedUserDefaults: UserDefaults) { self.pemPath = pemPath @@ -21,25 +26,48 @@ final class ArchiveUploadProcessor { } func encryptAndUpload(using builder: ArchiveBuilder) { - Task.detached(priority: .medium) { - await self._encryptAndUpload(using: builder) + Task.detached(priority: .userInitiated) { + await self.asyncEncryptAndUpload(using: builder) } } @MainActor - private func _encryptAndUpload(using builder: ArchiveBuilder) async { + func asyncEncryptAndUpload(using builder: ArchiveBuilder) async { + uploadManager.isArchiving = true + + #if canImport(UIKit) + // This allows the app to request about 30 seconds after the app has been + // backgrounded to prepare data for upload. + let taskId = UIApplication.shared.beginBackgroundTask { + Logger.log(tag: .upload, + error: BridgeUploadFailedError(category: .backgroundTaskTimeout, message: "Timed out when archiving and starting background upload.") + ) + self.uploadManager.isArchiving = false + } + #endif + do { let archive = try await builder.buildArchive() // Copy the startedOn date (if available) from the builder to the archive. + // This is a work-around for older code so that the adherence record can be + // marked for a requested archive. if let resultBuilder = builder as? ResultArchiveBuilder, archive.adherenceStartedOn == nil { archive.adherenceStartedOn = resultBuilder.startedOn } await _copyTest(archive: archive) - let encrypted = await _encrypt(archive: archive) - await _upload(archive: archive, encrypted: encrypted) + try await _encrypt(archive: archive) + try await _upload(archive: archive) } catch { Logger.log(error: error, message: "Failed to archive and upload \(builder.identifier)") } + + #if canImport(UIKit) + // Let the OS know that the app is done with the processing that needs to happen + // before ending. + UIApplication.shared.endBackgroundTask(taskId) + #endif + + uploadManager.isArchiving = false } private func _copyTest(archive: DataArchive) async { @@ -50,71 +78,55 @@ final class ArchiveUploadProcessor { #endif } - private func _encrypt(archive: DataArchive) async -> Bool { - guard let path = self.pemPath else { return false } - do { - try archive.encryptArchive(using: path) - return true - } catch { - Logger.log(error: error, message: "Failed to encrypt \(archive.identifier)") - return false + private func _encrypt(archive: DataArchive) async throws { + guard let path = self.pemPath else { + // Throw an assert to alert the developer that the pem file is missing. + let message = "Cannot upload archive. Missing pemPath." + assertionFailure(message) + throw BridgeUnexpectedNullError(category: .missingFile, message: message) } + try archive.encryptArchive(using: path) } @MainActor - private func _upload(archive: DataArchive, encrypted: Bool) async { + private func _upload(archive: DataArchive) async throws { guard let url = archive.encryptedURL else { let message = "Cannot upload archive. Missing encryption." assertionFailure(message) // Throw an assert to alert the developer that the pem file is missing. - Logger.log(error: BridgeUnexpectedNullError(category: .missingFile, message: message)) - return + throw BridgeUnexpectedNullError(category: .missingFile, message: message) } - _uploadEncrypted(id: archive.identifier, url: url, schedule: archive.schedule, startedOn: archive.adherenceStartedOn) + await _uploadEncrypted(id: archive.identifier, url: url, schedule: archive.schedule, startedOn: archive.adherenceStartedOn) } @MainActor - private func _uploadEncrypted(id: String, url: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) { - let dictionary = schedule.map { - var ret = [ - "instanceGuid": $0.instanceGuid, - "eventTimestamp": $0.session.eventTimestamp, - ] - if let startedOn = startedOn?.jsonObject() as? String { - ret["startedOn"] = startedOn + private func _uploadEncrypted(id: String, url: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) async { + let success = await uploadManager.uploadEncryptedArchive(fileUrl: url, schedule: schedule, startedOn: startedOn) + if (success) { + // Only if the file was successfully queued for upload should the original file be deleted. + do { + try FileManager.default.removeItem(at: url) + } catch let err { + Logger.log(error: err, message: "Failed to delete encrypted archive \(id) at \(url)") } - return ret } - let exporterV3Metadata: JsonElement? = dictionary.map { .object($0) } - let extras = StudyDataUploadExtras(encrypted: true, metadata: exporterV3Metadata, zipped: true) - Logger.log(severity: .info, message: "Uploading file: \(id)", metadata: dictionary) - uploadManager.studyDataUploadAPI.upload(fileId: id, fileUrl: url, contentType: "application/zip", extras: extras) - do { - try FileManager.default.removeItem(at: url) - } catch let err { - Logger.log(error: err, message: "Failed to delete encrypted archive \(id) at \(url)") + else { + // Store the path to the failed upload to allow potentially recovering the file and log error. + var failedUploads = (sharedUserDefaults.array(forKey: UnprocessedUploadsKey) as? [String]) ?? [] + failedUploads.append(url.path) + sharedUserDefaults.set(failedUploads, forKey: UnprocessedUploadsKey) + let err = BridgeUploadFailedError(category: .fileFailure, message: "Failed to queue encrypted file for upload. \(url)") + Logger.log(tag: .upload, error: err) } } - // TODO: syoung 07/27/2023 Replace upload with V2 uploader -// @MainActor -// private func _uploadEncrypted(id: String, url: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) { -// let success = uploader.uploadEncryptedArchive(fileUrl: url, schedule: schedule, startedOn: startedOn) -// if (success) { -// // Only if the file was successfully queued for upload should the original file be deleted. -// do { -// try FileManager.default.removeItem(at: url) -// } catch let err { -// Logger.log(error: err, message: "Failed to delete encrypted archive \(id) at \(url)") -// } -// } -// else { -// // Store the path to the failed upload to allow potentially recovering the file and log error. -// var failedUploads = (sharedUserDefaults.array(forKey: UnprocessedUploadsKey) as? [String]) ?? [] -// failedUploads.append(url.path) -// sharedUserDefaults.set(failedUploads, forKey: UnprocessedUploadsKey) -// let err = BridgeUploadFailedError(errorCode: -99, message: "Failed to queue encrypted file for upload. \(url)") -// Logger.log(tag: .upload, error: err) -// } -// } - +} + +/// Use a protocol to allow for unit tests to mock the Upload Manager. +protocol BridgeUploader : NSObjectProtocol { + var isArchiving: Bool { get set } + @MainActor func uploadEncryptedArchive(fileUrl: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) async -> Bool + @MainActor func upload(fileUrl: URL, contentType: String, encrypted: Bool, metadata: UploadMetadata?, s3UploadType: S3UploadType) async -> Bool +} + +extension UploadManager : BridgeUploader { } diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundNetworkManager.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundNetworkManager.swift index bef986ab3..9c14ed97e 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundNetworkManager.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundNetworkManager.swift @@ -165,7 +165,7 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR }.joined(separator: "&") } - func request(method: HTTPMethod, URLString: String, headers: [String : String]?) -> URLRequest { + func downloadRequest(method: HTTPMethod, URLString: String, headers: [String : String]?) -> URLRequest { var request = URLRequest(url: bridgeURL(for: URLString)) request.httpMethod = method.rawValue request.httpShouldHandleCookies = false @@ -175,7 +175,7 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR return request } - func request(method: HTTPMethod, URLString: String, headers: [String : String]?, parameters: T) -> URLRequest where T: Encodable { + func downloadRequest(method: HTTPMethod, URLString: String, headers: [String : String]?, parameters: T) -> URLRequest where T: Encodable { var URLString = URLString let isGet = (method == .GET) @@ -189,7 +189,7 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR } } - var request = self.request(method: method, URLString: URLString, headers: headers) + var request = self.downloadRequest(method: method, URLString: URLString, headers: headers) // for non-GET requests, the parameters (if any) go in the request body let contentTypeHeader = "Content-Type" @@ -212,30 +212,30 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR } @discardableResult - func downloadFile(from URLString: String, method: HTTPMethod, httpHeaders: [String : String]?, taskDescription: String) -> BridgeURLSessionDownloadTask { - let request = self.request(method: method, URLString: URLString, headers: httpHeaders) + func downloadFile(from URLString: String, method: HTTPMethod, httpHeaders: [String : String]?, taskDescription: String) -> Bool { + let request = self.downloadRequest(method: method, URLString: URLString, headers: httpHeaders) return self.downloadFile(with: request, taskDescription: taskDescription) } @discardableResult - func downloadFile(from URLString: String, method: HTTPMethod, httpHeaders: [String : String]?, parameters: T?, taskDescription: String) -> BridgeURLSessionDownloadTask where T: Encodable { - let request = self.request(method: method, URLString: URLString, headers: httpHeaders, parameters: parameters) + func downloadFile(from URLString: String, method: HTTPMethod, httpHeaders: [String : String]?, parameters: T?, taskDescription: String) -> Bool where T: Encodable { + let request = self.downloadRequest(method: method, URLString: URLString, headers: httpHeaders, parameters: parameters) return self.downloadFile(with: request, taskDescription: taskDescription) } - private func downloadFile(with request: URLRequest, taskDescription: String) -> BridgeURLSessionDownloadTask { + private func downloadFile(with request: URLRequest, taskDescription: String) -> Bool { let task = self.backgroundSession().downloadTask(with: request) task.taskDescription = taskDescription task.resume() - return task + return true } @discardableResult - func uploadFile(_ fileURL: URL, httpHeaders: [String : String]?, to urlString: String, taskDescription: String) -> BridgeURLSessionUploadTask? { + func uploadFile(_ fileURL: URL, httpHeaders: [String : String]?, to urlString: String, taskDescription: String) -> Bool { guard let url = URL(string: urlString) else { let message = "Error: Could not create URL from string '\(urlString)" Logger.log(tag: .upload, error: BridgeUnexpectedNullError(category: .invalidURL, message: message)) - return nil + return false } var request = URLRequest(url: url) request.allHTTPHeaderFields = httpHeaders @@ -243,7 +243,7 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR let task = backgroundSession().uploadTask(with: request, fromFile: fileURL) task.taskDescription = taskDescription task.resume() - return task + return true } func restore(backgroundSession: String, completionHandler: @escaping () -> Void) { @@ -339,6 +339,28 @@ class BackgroundNetworkManager: NSObject, URLSessionBackgroundDelegate, BridgeUR $0.bridgeUrlSession(session, didBecomeInvalidWithError: error) } } + + // MARK: Async task handling + + func getAllTasks() async -> [BridgeURLSessionTask] { + let mainSession = backgroundSession() + var tasks: [BridgeURLSessionTask] = await withCheckedContinuation { continuation in + mainSession.getAllTasks { + continuation.resume(returning: $0 as [BridgeURLSessionTask]) + } + } + for pair in self.restoredSessions { + let session = pair.value as any BridgeURLSession + if session.identifier != mainSession.identifier { + tasks.append(contentsOf: await withCheckedContinuation { continuation in + session.getAllTasks { + continuation.resume(returning: $0 as [BridgeURLSessionTask]) + } + }) + } + } + return tasks + } } // MARK: URLSession Mocking @@ -364,6 +386,7 @@ protocol BridgeURLSession : NSObjectProtocol { protocol BridgeURLSessionTask : NSObjectProtocol { var taskDescription: String? { get set } + var state: URLSessionTask.State { get } var taskType: BridgeURLSessionTaskType { get } var originalRequest: URLRequest? { get } var response: URLResponse? { get } diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundProcessSyncManager.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundProcessSyncManager.swift new file mode 100644 index 000000000..9b8e4258d --- /dev/null +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundProcessSyncManager.swift @@ -0,0 +1,269 @@ +// Created 8/28/23 +// swift-tools-version:5.0 + +import Foundation +import BackgroundTasks + +/// The delegate is responsible for the actual work that needs to be processed. +protocol BackgroundProcessSyncDelegate : AnyObject { + + /// Should a background process be scheduled? + var shouldScheduleBackgroundProcess: Bool { get } + + /// The method to call to process work. The method should be able to handle having the task + /// cancelled (because of timing out) and exit gracefully. + /// + /// - Parameter isBackground: Is the process being called while the app is already in the background? + func retryBackgroundProcess(isBackground: Bool) async throws +} + +#if canImport(UIKit) +import UIKit + +/// The `BackgroundProcessSyncManager` is set up to allow syncing a background processing +/// task scheduled with `BGTaskScheduler` while **also** allowing the process to run while +/// the app is active with a timing that is more "chatty". +/// +/// - Note: syoung 08/28/2023 The background process sync manager was set up this way +/// to allow testing using this manager in a simplified app. The use of `BGTaskScheduler` +/// is a bit brittle - as of this writing, a background process that runs when the app is +/// backgrounded will stall on tasks that are running on the `MainActor` so only +/// ``onLaunch(backgroundProcessId:)`` is ever called from that thread. +/// +actor BackgroundProcessSyncManager { + + weak private var delegate: BackgroundProcessSyncDelegate! + + init(delegate: BackgroundProcessSyncDelegate) { + self.delegate = delegate + } + + /// Toggle flag used to check whether or not the app is running in the background. + var isAppBackground: Bool = false + + /// **Required:** This method should be called on app launch to register the background + /// process id and set up the listeners for app becoming active and entering the + /// background. + @MainActor + func onLaunch(backgroundProcessId: String?) { + // Register the background process + if let backgroundProcessId = backgroundProcessId { + registerBackgroundTasks(backgroundProcessId) + } + setupAppListeners() + } + + /// Retry the background process immediately. Calls to this method are ignored if the app + /// is in the background or the retry is already running. + func retryBackgroundProcessAsync() { + guard !isAppBackground else { + Logger.log(severity: .info, tag: .upload, message: "Attempting to check and retry uploads from the background") + return + } + guard checkRetryTask?.isCancelled ?? true else { + Logger.log(severity: .info, tag: .upload, message: "Check and retry already running. Ignoring.") + return + } + + checkRetryTask = Task(priority: .background) { + + // retry all pending uploads + let task = taskToRetryAllPendingUploads(isBackground: false) + let _ = await task.result + + // check if we still have pending uploads and schedule a retry + if !isAppBackground, + (delegate?.shouldScheduleBackgroundProcess ?? false) { + enqueueForRetry() + } + + // nil out the check retry task so it can be restarted + checkRetryTask = nil + } + } + + /// Queue up a delay timer for retrying the background process. This will only trigger + /// if the app is active and there isn't already a retry queued up. + func enqueueForRetry() { + guard !isAppBackground else { + Logger.log(severity: .info, tag: .upload, message: "Attempting to enqueue file for retry in the background. Ignoring.") + return + } + guard waitRetryTask?.isCancelled ?? true else { return } + waitRetryTask = Task(priority: .background) { + do { + try await Task.sleep(nanoseconds: UInt64(delayForRetry * 1_000_000_000)) + retryBackgroundProcessAsync() + } catch is CancellationError { + Logger.log(severity: .info, tag: .upload, message: "Wait for check and retry uploads cancelled") + } catch { + Logger.log(tag: .upload, error: error, message: "Unexpected failure waiting to retry upload") + } + waitRetryTask = nil + } + } + + // MARK: Private methods and ivars + + /// The minimum delay before retrying a failed upload (in seconds). This is a + /// variable so that unit tests can run without waiting. + var delayForRetry: TimeInterval = 5 * 60 + + private var waitRetryTask: Task? + private var checkRetryTask: Task? + private var retryTask: Task? + private var backgroundProcessId: String? + + private func taskToRetryAllPendingUploads(isBackground: Bool) -> Task { + if retryTask == nil { + retryTask = Task(priority: .background) { + + // if launched from the foreground, then ask for time to finish in the background + if !isBackground { + await beginTimedBackgroundTask() + } + + // run the background process - catching all errors + do { + Logger.log(severity: .info, tag: .upload, message: "Retrying background process. isBackground=\(isBackground)") + try await delegate?.retryBackgroundProcess(isBackground: isBackground) + } catch is CancellationError { + Logger.log(severity: .info, tag: .upload, message: "Check and retry uploads cancelled") + } catch { + // TODO: syoung 08/29/2023 Check if the error is b/c of spotty network connectivity + Logger.log(tag: .upload, error: error, message: "Failure when checking and retrying uploads") + } + + // finally, let the app know that we are done with our background process + // (either because it finished or because it timed out) + if !isBackground { + await endTimedBackgroundTask() + } + + // nil out the finished task so it can be restarted + retryTask = nil + } + } + return retryTask! + } + + private func setBackgroundProcessId(_ backgroundProcessId: String) { + self.backgroundProcessId = backgroundProcessId + } + + private func didBecomeActive() { + Logger.log(severity: .info, tag: .upload, message: "App did become active. Check and retry uploads.") + // when becoming active, we want to retry running the process to clear the queue + isAppBackground = false + retryBackgroundProcessAsync() + } + + private func didEnterBackground() { + Logger.log(severity: .info, tag: .upload, message: "App did enter background. Scheduling background processing if needed.") + // when entering the background, we want to cancel waiting to retry uploads + // and then schedule a background process. + isAppBackground = true + waitRetryTask?.cancel() + scheduleBackgroundProcessingIfNeeded() + } + + private func scheduleBackgroundProcessingIfNeeded() { + // Check that there is a registered background process id, that the + // app is still in the background, and that the delegate has work + // to do. + guard let taskId = self.backgroundProcessId, + isAppBackground, + delegate?.shouldScheduleBackgroundProcess ?? false + else { + return + } + Logger.log(severity: .info, tag: .upload, message: "Pending uploads detected. Scheduling background process: \(taskId)") + + let request = BGProcessingTaskRequest(identifier: taskId) + request.requiresNetworkConnectivity = true // Do not attempt processing without network connectivity + request.earliestBeginDate = Date(timeIntervalSinceNow: 15*60) // Wait 15 minutes to allow S3 uploads in progress to complete + + do { + try BGTaskScheduler.shared.submit(request) + } catch { + Logger.log(tag: .upload, error: error, message: "Error scheduling BG task: \(taskId)") + } + } + + // MARK: @MainActor methods used to handle set up and continuing a foreground process in the background + + @MainActor var timedBackgroundTaskId: UIBackgroundTaskIdentifier? + + @MainActor + private func registerBackgroundTasks(_ backgroundProcessId: String) { + Task { + await setBackgroundProcessId(backgroundProcessId) + } + BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundProcessId, using: .main) { task in + Task { + let retry = await self.taskToRetryAllPendingUploads(isBackground: true) + let _ = await retry.result + task.setTaskCompleted(success: true) + await self.scheduleBackgroundProcessingIfNeeded() + } + } + } + + @MainActor + private func setupAppListeners() { + // Set up a listener to retry temporarily-failed uploads whenever the app becomes active + NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] _ in + guard let strongSelf = self else { return } + Task { + await strongSelf.didBecomeActive() + } + } + // Set up a listener to handle entering background + NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in + guard let strongSelf = self else { return } + Task { + await strongSelf.didEnterBackground() + let _ = true // Use this as a breakpoint for simulating a background process + } + } + } + + @MainActor + private func beginTimedBackgroundTask() { + guard timedBackgroundTaskId == nil else { return } + timedBackgroundTaskId = UIApplication.shared.beginBackgroundTask { + Logger.log(severity: .info, tag: .upload, message: "Timed out when checking and retrying uploads.") + self.endTimedBackgroundTask() + Task { + await self.retryTask?.cancel() + } + } + } + + @MainActor + private func endTimedBackgroundTask() { + guard let taskId = timedBackgroundTaskId else { return } + timedBackgroundTaskId = nil + UIApplication.shared.endBackgroundTask(taskId) + } + +} + +#else + +/// This is included b/c a limitation of Swift PM is that you cannot explicitly specify +/// support for only certain platforms. syoung 08/29/2023 +actor BackgroundProcessSyncManager { + init(delegate: BackgroundProcessSyncDelegate) { } + var isAppBackground: Bool = false + @MainActor func onLaunch(backgroundProcessId: String?) { } + func retryBackgroundProcessAsync() { } + func enqueueForRetry() { } +} + +#endif + +/** Command for simulating launch of a background task: + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"org.sagebase.BackgroundProcessTest"] + + */ diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUnexpectedNullError.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUnexpectedNullError.swift index 0b15d523d..89654cf2c 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUnexpectedNullError.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUnexpectedNullError.swift @@ -28,5 +28,6 @@ struct BridgeUnexpectedNullError : Error, CustomNSError { case missingMetadata = -107 case missingMapping = -108 case notFound = -109 + case duplicateCall = -110 } } diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUploadFailedError.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUploadFailedError.swift index 0449bca64..c95ac0039 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUploadFailedError.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/Errors/BridgeUploadFailedError.swift @@ -6,12 +6,19 @@ import Foundation struct BridgeUploadFailedError : Error, CustomNSError { static var errorDomain: String { "BridgeClient.UploadFailedError" } - let errorCode: Int + let category: Category let message: String - init(errorCode: Int = -1, message: String = "Bridge upload failed") { - self.errorCode = errorCode - self.message = message + var errorCode: Int { + category.rawValue + } + + enum Category : Int { + case unknown = -1 + case backgroundTaskTimeout = -51 + case wrongHandler = -52 + case fileFailure = -53 + case unexpectedBackgroundCall = -54 } var errorUserInfo: [String : Any] { diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/SandboxFileManager.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/SandboxFileManager.swift index 4deef2b45..d430ea70a 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/SandboxFileManager.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/SandboxFileManager.swift @@ -42,7 +42,19 @@ class SandboxFileManager: NSObject { // Use a UUID for the temp file's name let tempFileURL = uploadDirURL.appendingPathComponent(UUID().uuidString) - + + // Copy the file to the temp file + guard copyFile(from: fileURL, to: tempFileURL) else { + return nil + } + + // "touch" the temp file for retry accounting purposes + self.touch(fileUrl: tempFileURL) + + return (fileURL, tempFileURL) + } + + func copyFile(from fileURL: URL, to tempFileURL: URL) -> Bool { // Use a NSFileCoordinator to make a temp local copy so the app can delete // the original as soon as the upload call returns. let coordinator = NSFileCoordinator(filePresenter: nil) @@ -57,17 +69,13 @@ class SandboxFileManager: NSObject { } } if copyError != nil { - return nil + return false } if let err = coordError { Logger.log(tag: .upload, error: err, message: "File coordinator error copying upload file \(fileURL) to temp file \(tempFileURL) for upload.") - return nil + return false } - - // "touch" the temp file for retry accounting purposes - self.touch(fileUrl: tempFileURL) - - return (fileURL, tempFileURL) + return true } func mimeTypeFor(fileUrl: URL) -> String { @@ -175,6 +183,10 @@ class SandboxFileManager: NSObject { return normalizedPath } + func fileURL(of path: String) -> URL { + URL(fileURLWithPath: fullyQualifiedPath(of: path)) + } + /// Return the file size of the file at this URL. func fileContentLength(_ fileUrl: URL) -> Int? { do { @@ -201,6 +213,32 @@ class SandboxFileManager: NSObject { return nil } } + + /// Check that the file exists + func fileExists(atPath filePath: String) -> Bool { + FileManager.default.fileExists(atPath: filePath) + } + + /// Check that the file exists + func fileExists(at url: URL) -> Bool { + fileExists(atPath: url.absoluteString) + } + + /// Append the base directory with a subdirectory and create if needed. + /// - Parameters: + /// - baseURL: The base directory + /// - subdir: The subdirectory to append + /// - Returns: The subdirectory or nil it fails. + func createSubdirectory(baseURL: URL, subdir: String) throws -> URL { + let url: URL + if #available(iOS 16.0, *) { + url = baseURL.appending(component: subdir, directoryHint: .isDirectory) + } else { + url = baseURL.appendingPathComponent(subdir) + } + try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) + return url + } } diff --git a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/UploadManager.swift b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/UploadManager.swift index 9fe0dc35b..666613173 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/File Uploading/UploadManager.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/File Uploading/UploadManager.swift @@ -2,55 +2,71 @@ // swift-tools-version:5.0 import Foundation +import BackgroundTasks import BridgeClient -protocol BridgeUploader { - - /// Upload an encrypted archive. - /// - Parameters: - /// - fileUrl: The file url of the original encrypted archive. - /// - schedule: The schedule (if any) associated with this archive. - /// - startedOn: The `startedOn` value for the associated adherence record that is used to uniquely identify it. - /// - Returns: `true` if the file was successfully copied and queued for upload. - @MainActor func uploadEncryptedArchive(fileUrl: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) -> Bool - - - /// Upload a file to Bridge. - /// - Parameters: - /// - fileUrl: The original file url for the file to upload. - /// - contentType: The content-type for the file. - /// - encrypted: Whether or not the file is encrypted. - /// - metadata: The metadata associated with this file. - /// - s3UploadType: The upload type. - /// - Returns: `true` if the file was successfully copied and queued for upload. - @MainActor func upload(fileUrl: URL, contentType: String, encrypted: Bool, metadata: UploadMetadata?, s3UploadType: S3UploadType) -> Bool -} +#if canImport(UIKit) +import UIKit +#endif + +class UploadManager : NSObject, BridgeURLSessionHandler, BackgroundProcessSyncDelegate { -class UploadManager : NSObject, BridgeUploader { - static let shared: UploadManager = .init() + weak private(set) var backgroundNetworkManager: SharedBackgroundUploadManager! + let sandboxFileManager: SandboxFileManager + let nativeUploadManager: BridgeClientUploadManager + lazy private(set) var syncManager: BackgroundProcessSyncManager = .init(delegate: self) - lazy var sandboxFileManager: SandboxFileManager = .init() - lazy var nativeUploadManager: BridgeClientUploadManager = NativeUploadManager() + init(backgroundNetworkManager: SharedBackgroundUploadManager, + sandboxFileManager: SandboxFileManager = .init(), + nativeUploadManager: BridgeClientUploadManager = NativeUploadManager() + ) { + // Set up pointers + self.sandboxFileManager = sandboxFileManager + self.nativeUploadManager = nativeUploadManager + super.init() + + // register self with the background network manager + self.backgroundNetworkManager = backgroundNetworkManager + backgroundNetworkManager.registerBackgroundTransferHandler(self) + } + + let subdir = "BridgeUploadsV2" lazy var uploadDirURL: URL? = { do { let baseURL = try FileManager.default.sharedAppSupportDirectory() - let url: URL - let subdir = "BridgeUploads" - if #available(iOS 16.0, *) { - url = baseURL.appending(component: subdir, directoryHint: .isDirectory) - } else { - url = baseURL.appendingPathComponent(subdir) - } - try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) - return url + return try sandboxFileManager.createSubdirectory(baseURL: baseURL, subdir: subdir) } catch { - Logger.log(tag: .upload, error: error, message: "Error trying to create the uploads directory.") + Logger.log(tag: .upload, error: error, message: "Error trying to create the shared app support directory.") return nil } }() - func uploadEncryptedArchive(fileUrl: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) -> Bool { + var isArchiving: Bool = false + + /// **Required:** This method should be called on app launch to register the background + /// process id and set up the listeners for app becoming active and entering the + /// background. + @MainActor + func onLaunch(backgroundProcessId: String?) { + syncManager.onLaunch(backgroundProcessId: backgroundProcessId) + } + + /// Force check and retry uploads. + func checkAndRetryUploads() { + Task { + await syncManager.retryBackgroundProcessAsync() + } + } + + /// Upload an encrypted archive. + /// - Parameters: + /// - fileUrl: The file url of the original encrypted archive. + /// - schedule: The schedule (if any) associated with this archive. + /// - startedOn: The `startedOn` value for the associated adherence record that is used to uniquely identify it. + /// - Returns: `true` if the file was successfully copied and queued for upload. + @MainActor + func uploadEncryptedArchive(fileUrl: URL, schedule: AssessmentScheduleInfo?, startedOn: Date?) async -> Bool { let metadata: UploadMetadata? = schedule.map { UploadMetadata( instanceGuid: $0.instanceGuid, @@ -58,13 +74,22 @@ class UploadManager : NSObject, BridgeUploader { startedOn: startedOn?.jsonObject() as? String ) } - return upload(fileUrl: fileUrl, contentType: "application/zip", encrypted: true, metadata: metadata, s3UploadType: .studyData) + return await upload(fileUrl: fileUrl, contentType: "application/zip", encrypted: true, metadata: metadata, s3UploadType: .studyData) } - func upload(fileUrl: URL, contentType: String, encrypted: Bool, metadata: UploadMetadata?, s3UploadType: S3UploadType) -> Bool { + /// Upload a file to Bridge. + /// - Parameters: + /// - fileUrl: The original file url for the file to upload. + /// - contentType: The content-type for the file. + /// - encrypted: Whether or not the file is encrypted. + /// - metadata: The metadata associated with this file. + /// - s3UploadType: The upload type. + /// - Returns: `true` if the file was successfully copied and queued for upload. + @MainActor + func upload(fileUrl: URL, contentType: String, encrypted: Bool, metadata: UploadMetadata?, s3UploadType: S3UploadType) async -> Bool { // Both study data and participant data should first be copied to a temp file. - guard let uploadDirURL = self.uploadDirURL, + guard let uploadDirURL = self.uploadDir(for: s3UploadType), let (_, tempUrl) = sandboxFileManager.tempFileFor(inFileURL: fileUrl, uploadDirURL: uploadDirURL), let contentLength = sandboxFileManager.fileContentLength(tempUrl) else { @@ -94,27 +119,323 @@ class UploadManager : NSObject, BridgeUploader { md5Hash: contentMD5String, encrypted: encrypted, metadata: metadata, - s3UploadType: .studyData) + s3UploadType: s3UploadType) // Finally, queue the upload using the native upload manager. - nativeUploadManager.queueAndRequestUploadSession(uploadFile: uploadFile, callBack: startS3Upload) + if let uploadSession = await nativeUploadManager.queueAndRequestUploadSession(uploadFile: uploadFile) { + // If there is an upload session then start the upload. + startS3Upload(uploadSession) + } + else { + // If the callback is nil then services are offline. Try again later. + await enqueueForRetry(filePath: invariantFilePath, uploaded: false) + } + return true } - private func startS3Upload(_ uploadSession: S3UploadSession?) { - // TODO: Implement syoung 07/26/2023 + fileprivate func uploadDir(for s3UploadType: S3UploadType) -> URL? { + guard let baseURL = uploadDirURL else { return nil } + do { + return try sandboxFileManager.createSubdirectory(baseURL: baseURL, subdir: s3UploadType.name) + } catch { + Logger.log(tag: .upload, error: error, message: "Error trying to create the uploads directory for \(s3UploadType.name).") + return nil + } } + // MARK: BackgroundProcessSyncDelegate + + var shouldScheduleBackgroundProcess: Bool { + isArchiving || nativeUploadManager.hasPendingUploads() + } + + func retryBackgroundProcess(isBackground: Bool) async throws { + try await retryUploadToS3() + try await processFinishedUploads() + try await cleanupLostFiles() + } + + // MARK: BridgeURLSessionHandler + + func canHandle(task: BridgeURLSessionTask) -> Bool { + return (task.taskType == .upload) && (task.taskDescription?.contains(subdir) ?? false) + } + + func bridgeUrlSession(_ session: any BridgeURLSession, task: BridgeURLSessionTask, didCompleteWithError error: Error?) { + Task { + do { + try await urlSession(task: task, didCompleteWithError: error) + } catch { + Logger.log(tag: .upload, error: error, message: "Failed to handle completed task \(task)") + } + } + } + + func urlSession(task: BridgeURLSessionTask, didCompleteWithError error: Error?) async throws { + // Ignore tasks that are not uploads + guard task.taskType == .upload else { + throw BridgeUploadFailedError(category: .wrongHandler, message: "Failed to handle upload task \(task) using the \(self)") + } + + // get the sandbox-relative path to the temp copy of the participant file + let (uploadSessionId, invariantFilePath) = try task.parseTaskDescription() + + // any error that makes it all the way through to here would be the result of something like a + // malformed request, so clean up the temp file and throw the error + if let error = error { + handleUnrecoverableFailure(filePath: invariantFilePath) + throw error + } + + // get the http response + guard let httpResponse = task.response as? HTTPURLResponse else { + throw BridgeUnexpectedNullError(category: .wrongType, + message: "Upload task response is not an HTTPURLResponse: \(type(of: task.response))") + } + + // check the upload status + switch httpResponse.httpUploadStatus() { + case .unhandled: + // The status code is unrecoverable + handleUnrecoverableFailure(filePath: invariantFilePath) + throw BridgeHttpError(errorCode: httpResponse.statusCode, message: "Participant file upload to S3 of file \(invariantFilePath) failed with HTTP response status code \(httpResponse.statusCode)--unhandled, will not retry") + + case .retry: + // this call failed but we may be able to recover with a retry + await enqueueForRetry(filePath: invariantFilePath, uploaded: false) + + case .success: + // If we get here, then we are done uploading to S3. Mark the file and clean up the temp file. + async let success = nativeUploadManager.markUploadFileFinished(filePath: invariantFilePath, uploadSessionId: uploadSessionId) + sandboxFileManager.removeTempFile(filePath: invariantFilePath) + if await !success { + // If we aren't successful in sending the completion then enqueue for retry + await enqueueForRetry(filePath: invariantFilePath, uploaded: true) + } + } + } + + func bridgeUrlSession(_ session: any BridgeURLSession, didBecomeInvalidWithError error: Error?) { + // If a url session becomes invalid, just queue up a delayed retry + Task { + await syncManager.enqueueForRetry() + } + } + + // MARK: Private methods + + func urlUploadTaskIdentifier(filePath: String, uploadSessionId: String) -> String { + "\(uploadSessionId)|\(filePath)" + } + + private func startS3Upload(_ uploadSession: S3UploadSession) { + let fileURL = sandboxFileManager.fileURL(of: uploadSession.filePath) + let taskIdentifier = urlUploadTaskIdentifier(filePath: uploadSession.filePath, uploadSessionId: uploadSession.uploadSessionId) + backgroundNetworkManager.uploadFile(fileURL, + httpHeaders: uploadSession.requestHeaders, + to: uploadSession.url, + taskDescription: taskIdentifier) + } + + private func handleUnrecoverableFailure(filePath: String) { + nativeUploadManager.markUploadUnrecoverableFailure(filePath: filePath) + // TODO: syoung 08/24/2023 If something "unrecoverable" happens, do we want to remove the file or just leave it? + sandboxFileManager.removeTempFile(filePath: filePath) + } + + func enqueueForRetry(filePath: String, uploaded: Bool) async { + // TODO: syoung 08/29/2023 Consider if we need to *only* retry the one file? + await syncManager.enqueueForRetry() + } + + private func processFinishedUploads() async throws { + // process the finished uploads - this is the last call and can take a while + // TODO: syoung 08/23/2023 We need to revisit this for the case where there are *a lot* + // of completion messages to send (since they are all set serially) so that this process + // doesn't timeout and get killed by the OS. + guard !Task.isCancelled else { + throw CancellationError() + } + let _ = try await nativeUploadManager.processFinishedUploads() + } + + private func retryUploadToS3() async throws { + + // get all the currently running upload tasks that can be handled by this manager + async let allUrlSessionTasks = backgroundNetworkManager.getAllTasks() + // get all the files that are currently pending + async let allPendingUploads = nativeUploadManager.getPendingUploadFiles() + + // look through all the currect url session tasks and figure out which should be restarted + var uploadsToRetry = Set(await allPendingUploads) + for urlSessionTask in await allUrlSessionTasks { + // check that the task is not cancelled + guard !Task.isCancelled else { + throw CancellationError() + } + // check that this is an upload task handled by this manager + guard self.canHandle(task: urlSessionTask), + let uploadId = urlSessionTask.parsePendingUpload() + else { + Logger.log(severity: .info, tag: .upload, message: "Skipping URLSessionTask \(urlSessionTask) - does not match pattern of tasks handled by the UploadManager.") + continue + } + let requiresRestart = uploadsToRetry.contains(.init(filePath: uploadId.filePath, uploadSessionId: nil)) + switch urlSessionTask.state { + case .running, .suspended: + if requiresRestart { + // upload session id has expired - need to restart + Logger.log(severity: .info, tag: .upload, message: "Found expired URLSessionTask for \(uploadId) - cancelling task") + urlSessionTask.cancel() + } + else { + // If the session is still valid, then check if it needs to be resumed. + if urlSessionTask.state == .suspended { + Logger.log(severity: .warn, tag: .upload, message: "Found valid suspended URLSessionTask for \(uploadId) - resuming") + urlSessionTask.resume() + } + if let _ = uploadsToRetry.remove(uploadId) { + Logger.log(severity: .info, tag: .upload, message: "Found valid running URLSessionTask for \(uploadId) - not restarting") + } + } + + default: + // TODO: syoung 08/28/2023 Do we need to do more handling for cancelled or completed tasks? + Logger.log(severity: .info, tag: .upload, message: "Found URLSessionTask for \(uploadId) - state=\(urlSessionTask.state)") + break + } + } + + // fetch the uploads to retry + for upload in uploadsToRetry { + // check that the task is not cancelled + guard !Task.isCancelled else { + throw CancellationError() + } + // request an upload session and if non-nil, then start it + if let uploadSession = await nativeUploadManager.requestUploadSession(filePath: upload.filePath) { + startS3Upload(uploadSession) + } + } + } + + private func cleanupLostFiles() async throws { + // TODO: syoung 08/25/2023 Implement + } +} + +enum HTTPS3UploadStatus { + case success, retry, unhandled +} + +extension HTTPURLResponse { + func httpUploadStatus() -> HTTPS3UploadStatus { + if statusCode < 300 { + return .success + } + else { + switch statusCode { + case 403, 409, 500, 503: + // 403: for our purposes means the pre-signed url timed out before starting the actual upload to S3. + // 409: in our case it could only mean a temporary conflict (resource locked by another process, etc.) that should be retried. + // 500: means internal server error ("We encountered an internal error. Please try again.") + // 503: means service not available or the requests are coming too fast, so try again later. + // In any case, we'll retry after a minimum delay to avoid spamming retries. + return .retry + + default: + // iOS handles redirects automatically so only e.g. 304 resource not changed etc. from the 300 range should end up here + // (along with all unhandled 4xx and 5xx of course). + return .unhandled + } + } + } +} + +extension BridgeURLSessionTask { + + fileprivate func parseTaskDescription() throws -> (sessionId: String, invariantFilePath: String) { + guard let identifier = taskDescription else { + throw BridgeUnexpectedNullError(category: .empty, message: "The `taskDescription` should not be NULL for an S3 upload.") + } + let split = identifier.components(separatedBy: "|") + guard split.count == 2 else { + throw BridgeUnexpectedNullError(category: .missingIdentifier, message: "The `taskDescription` should include the '|' character to separate the components of the identifier: \(identifier)") + } + return (split[0], split[1]) + } + + fileprivate func parsePendingUpload() -> PendingUploadFile? { + guard let (sessionId, filePath) = try? parseTaskDescription() else { + return nil + } + return .init(filePath: filePath, uploadSessionId: sessionId) + } +} + +// Use a protocol to wrap the background network manager - this is to allow using a mock for testing. +protocol SharedBackgroundUploadManager : AnyObject { + var sessionDelegateQueue: OperationQueue { get } + func registerBackgroundTransferHandler(_ handler: BridgeURLSessionHandler) + @discardableResult func uploadFile(_ fileURL: URL, httpHeaders: [String : String]?, to urlString: String, taskDescription: String) -> Bool + func getAllTasks() async -> [BridgeURLSessionTask] +} + +extension BackgroundNetworkManager : SharedBackgroundUploadManager { } // Use a protocol to wrap the native upload manager - this is to allow using a mock for testing. protocol BridgeClientUploadManager : AnyObject { - func getUploadFiles() -> [String] - func queueAndRequestUploadSession(uploadFile: UploadFile, callBack: @escaping (S3UploadSession?) -> Void) - func requestUploadSession(filePath: String, callBack: @escaping (S3UploadSession?) -> Void) - func markUploadFileFinished(filePath: String, callBack: @escaping (KotlinBoolean) -> Void) - func processFinishedUploads(callBack: @escaping (KotlinBoolean) -> Void) + func hasPendingUploads() -> Bool + func getPendingUploadFiles() async -> [PendingUploadFile] + func queueAndRequestUploadSession(uploadFile: UploadFile) async -> S3UploadSession? + func requestUploadSession(filePath: String) async -> S3UploadSession? + func markUploadFileFinished(filePath: String, uploadSessionId: String) async -> Bool + func markUploadUnrecoverableFailure(filePath: String) + func processFinishedUploads() async throws -> Bool } extension NativeUploadManager : BridgeClientUploadManager { + + func getPendingUploadFiles() async -> [PendingUploadFile] { + return await withCheckedContinuation { continuation in + self.getPendingUploadFiles() { ret in + continuation.resume(returning: ret) + } + } + } + + func queueAndRequestUploadSession(uploadFile: UploadFile) async -> S3UploadSession? { + return await withCheckedContinuation { continuation in + self.queueAndRequestUploadSession(uploadFile: uploadFile) { session in + continuation.resume(returning: session) + } + } + } + + func requestUploadSession(filePath: String) async -> S3UploadSession? { + return await withCheckedContinuation { continuation in + self.requestUploadSession(filePath: filePath) { session in + continuation.resume(returning: session) + } + } + } + + func markUploadFileFinished(filePath: String, uploadSessionId: String) async -> Bool { + return await withCheckedContinuation { continuation in + self.markUploadFileFinished(filePath: filePath, uploadSessionId: uploadSessionId) { result in + continuation.resume(returning: result.boolValue) + } + } + } + + func processFinishedUploads() async throws -> Bool { + return await withCheckedContinuation { continuation in + self.processFinishedUploads() { result in + continuation.resume(returning: result.boolValue) + } + } + } } + diff --git a/SwiftPackage/Sources/BridgeClientExtension/Model/NetworkStatus.swift b/SwiftPackage/Sources/BridgeClientExtension/Model/NetworkStatus.swift index 821fb7f80..d06e8c520 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/Model/NetworkStatus.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/Model/NetworkStatus.swift @@ -2,6 +2,7 @@ // swift-tools-version:5.0 import Foundation +import Network /// Option set for the current network status. public struct NetworkStatus : OptionSet, CaseIterable, Hashable { @@ -38,3 +39,88 @@ public struct NetworkStatus : OptionSet, CaseIterable, Hashable { } } } + +/// A mockable wrapper for network monitoring. +public class NetworkMonitor : ObservableObject { + + /// Current network status. + @Published public fileprivate(set) var networkStatus: NetworkStatus = .unknown { + didSet { + self.didChangeHandler?(networkStatus) + } + } + + /// Set a block to be called when the network status changes. + public var didChangeHandler: ((NetworkStatus) -> Void)? + + public init(didChangeHandler: ((NetworkStatus) -> Void)? = nil) { + self.didChangeHandler = didChangeHandler + } + + fileprivate let networkMonitoringQueue = DispatchQueue(label: "org.sagebase.NetworkConnectivityMonitor.\(UUID())") + fileprivate var networkMonitor: NWPathMonitor? + + deinit { + networkMonitor?.cancel() + } + + /// Cancel network monitoring, after which point no more path updates will be delivered. + public func cancel() { + networkMonitor?.cancel() + networkMonitor = nil + networkStatus = .unknown + } + + /// Start monitoring network status. If status is already being monitored then multiple + /// calls to start will be ignored. + public func startIfNeeded() { + guard networkMonitor == nil else { return } + let networkMonitor = NWPathMonitor() + self.networkMonitor = networkMonitor + networkMonitor.pathUpdateHandler = { [weak self] path in + guard let strongSelf = self else { return } + DispatchQueue.main.async { + switch path.status { + case .unsatisfied: + if #available(iOS 14.2, *), path.unsatisfiedReason == .cellularDenied { + strongSelf.networkStatus = .cellularDenied + } else { + strongSelf.networkStatus = .notConnected + } + case .satisfied: + strongSelf.networkStatus = .connected + default: + strongSelf.networkStatus = .unknown + } + } + } + networkMonitor.start(queue: networkMonitoringQueue) + } +} + +/// A mock network monitor that can be used to set the network status. This can be used in SwiftUI Preview +/// or in unit tests. +public class MockNetworkMonitor : NetworkMonitor { + + override public func startIfNeeded() { + // Do not start network monitoring - this is a mock. + } + + public func setNetworkStatus(_ newStatus: NetworkStatus, withDelay delay: DispatchTimeInterval? = nil) { + guard let delay = delay else { + if Thread.isMainThread { + self.networkStatus = newStatus + } else { + DispatchQueue.main.sync { + self.networkStatus = newStatus + } + } + return + } + self.networkMonitoringQueue.asyncAfter(deadline: .now().advanced(by: delay)) { + DispatchQueue.main.async { + self.networkStatus = newStatus + } + } + } +} diff --git a/SwiftPackage/Sources/BridgeClientExtension/UploadAppManager.swift b/SwiftPackage/Sources/BridgeClientExtension/UploadAppManager.swift index aa2eda5c1..9ed2bc500 100644 --- a/SwiftPackage/Sources/BridgeClientExtension/UploadAppManager.swift +++ b/SwiftPackage/Sources/BridgeClientExtension/UploadAppManager.swift @@ -6,7 +6,6 @@ import Foundation import BridgeClient import JsonModel -import Network public let kPreviewStudyId = "xcode_preview" public let kStudyIdKey = "studyId" @@ -45,8 +44,7 @@ open class UploadAppManager : ObservableObject { /// /// - Note: Data should be encrypted so that it is not stored insecurely on the phone (while waiting /// for an upload connection). - public var pemPath: String? { uploadProcessor.pemPath } - private let uploadProcessor: ArchiveUploadProcessor + public var pemPath: String? { self.uploadProcessor.pemPath } /// The title of the app to display. By default, this is the localized display name of the app that is shown /// to the participant in their phone home screen. @@ -193,13 +191,16 @@ open class UploadAppManager : ObservableObject { } } + private let uploadProcessor: ArchiveUploadProcessor + private let backgroundProcessId: String? private var appConfigManager: NativeAppConfigManager! public private(set) var authManager: NativeAuthenticationManager! private var pendingUploadObserver: PendingUploadObserver! - lazy var backgroundNetworkManager: BackgroundNetworkManager = .init() - lazy var uploadManagerV1: BridgeFileUploadManager = .init(netManager: backgroundNetworkManager, appManager: self) + let backgroundNetworkManager: BackgroundNetworkManager = .init() + lazy private(set) var uploadManagerV1: BridgeFileUploadManager = .init(netManager: backgroundNetworkManager, appManager: self) + lazy private(set) var uploadManagerV2: UploadManager = .init(backgroundNetworkManager: backgroundNetworkManager) - /// Convenience initializer for intializing a bridge manager with just an app id and pem file. + /// Convenience initializer for intializing a bridge manager with just required properties. /// /// Both the APP ID and the CMS Public Key (ie. pem file) are accessible through the [Bridge Study Manager](https://research.sagebridge.org). /// Once logged into an "app" (which can host multiple "studies"), select "Server Config -> Settings" from the menu. @@ -209,17 +210,43 @@ open class UploadAppManager : ObservableObject { /// - pemPath: The path to the pem file (as an embedded resource) to use for encrypting uploads. The pem file can be downloaded /// from the [Bridge Study Manager](https://research.sagebridge.org) by going "Server Settings -> Settings" /// and tapping on the button labeled "Download CMS Public Key..." and saving the file to a secure location. + /// - backgroundProcessId: The background process ID registered in the "info.plist" used to allow processing pending uploads + /// in the background. + /// - appGroupId: The app group ID to use when setting up files that are shared with an app extension. + /// - defaultConsentGuid: The default consent guid for this app (where the app includes in-app consent). + public convenience init(appId: String, pemPath: String, backgroundProcessId: String, appGroupId: String? = nil, defaultConsentGuid: String? = nil) { + self.init(platformConfig: PlatformConfigImpl(appId: appId, appGroupIdentifier: appGroupId, defaultConsentGuid: defaultConsentGuid), pemPath: pemPath, backgroundProcessId: backgroundProcessId) + } + + public enum MockType { + case unitTest, preview + } + + /// A special manager that is used in unit testing and SwiftUI preview. + /// - Parameter mockType: The type of mock manager to initialize. + public convenience init(mockType: MockType) { + let appId = mockType == .preview ? kPreviewStudyId : "not-a-real-appid" + self.init(appId: appId, pemPath: "", backgroundProcessId: "") + } + + @available(*, deprecated, message: "Use initializer with non-nil pem path and background process Id") public convenience init(appId: String, appGroupId: String? = nil, pemPath: String? = nil, defaultConsentGuid: String? = nil) { self.init(platformConfig: PlatformConfigImpl(appId: appId, appGroupIdentifier: appGroupId, defaultConsentGuid: defaultConsentGuid), pemPath: pemPath) } + @available(*, deprecated, message: "Use initializer with non-nil pem path and background process Id") + public convenience init(platformConfig: IOSPlatformConfig, pemPath: String? = nil) { + let pemPath = pemPath ?? Bundle.main.path(forResource: platformConfig.appId, ofType: "pem") ?? "" + self.init(platformConfig: platformConfig, pemPath: pemPath, backgroundProcessId: "") + } + /// Initialize the bridge manager with a custom platform config. /// /// - Parameters: /// - platformConfig: The platform config to use to set up the connection to Bridge. /// - pemPath: The path to the location of the pem file to use when encrypting uploads. - public init(platformConfig: IOSPlatformConfig, pemPath: String? = nil) { - let pemPath = pemPath ?? Bundle.main.path(forResource: platformConfig.appId, ofType: "pem") + /// - backgroundProcessId: The background process ID registered in the "info.plist" used to allow processing pending uploads in the background. + public init(platformConfig: IOSPlatformConfig, pemPath: String, backgroundProcessId: String) { self.title = platformConfig.localizedAppName self.platformConfig = platformConfig @@ -234,6 +261,7 @@ open class UploadAppManager : ObservableObject { // Create the archive upload processor self.uploadProcessor = .init(pemPath: pemPath, sharedUserDefaults: self.sharedUserDefaults) + self.backgroundProcessId = backgroundProcessId.isEmpty ? nil : backgroundProcessId // Is this a new login (in which case we need to get the adherence records) self.isNewLogin = (self.userSessionId == nil) @@ -257,7 +285,7 @@ open class UploadAppManager : ObservableObject { // Setup upload processor Logger.log(severity: .info, message: "Hook up upload processor") - self.uploadProcessor.uploadManager = uploadManagerV1 + self.uploadProcessor.uploadManager = uploadManagerV2 // Hook up app config Logger.log(severity: .info, message: "Hook up app config") @@ -289,10 +317,16 @@ open class UploadAppManager : ObservableObject { self.authManager.observeUserSessionInfo() // Hook up upload observer + Logger.log(severity: .info, message: "Hook up upload observer") self.pendingUploadObserver = PendingUploadObserver() { count in self.isUploading = (count.intValue > 0) + Logger.log(severity: .info, tag: .upload, message: "Pending upload count changed: \(count.intValue)") } self.pendingUploadObserver.observePendingUploadCount() + + // Setup the upload manager + Logger.log(severity: .info, message: "Setup the upload manager") + self.uploadManagerV2.onLaunch(backgroundProcessId: backgroundProcessId) } /// **Required:** This method should be called by the app delegate in the implementation of @@ -377,46 +411,30 @@ open class UploadAppManager : ObservableObject { // Do nothing. Allows subclass override setup and app state changes. } - private let networkMonitoringQueue = DispatchQueue(label: "org.sagebase.NetworkConnectivityMonitor.\(UUID())") - private var networkMonitor: NWPathMonitor? + lazy private var networkMonitor: NetworkMonitor = .init() { [weak self] newStatus in + guard let strongSelf = self else { return } + Task { + await MainActor.run { + let oldStatus = strongSelf.networkStatus + if newStatus == .connected, oldStatus != .unknown { + // only check for uploads if the new status is connected and the previous + // status was *not* connected. + strongSelf.uploadManagerV2.checkAndRetryUploads() + } + Logger.log(severity: .info, message: "Network status changed: \(newStatus.stringValue)") + strongSelf.networkStatus = newStatus + } + } + } private func updateNetworkMonitoring() { // This is not thread protected so it should always be called from the main thread if isUploading || !userSessionInfo.isAuthenticated { - startMonitoringNetwork() + networkMonitor.startIfNeeded() } else { - stopMonitoringNetwork() - } - } - - private func stopMonitoringNetwork() { - networkMonitor?.cancel() - networkMonitor = nil - } - - private func startMonitoringNetwork() { - guard networkMonitor == nil else { return } - let networkMonitor = NWPathMonitor() - self.networkMonitor = networkMonitor - networkMonitor.pathUpdateHandler = { [weak self] path in - guard let strongSelf = self else { return } - DispatchQueue.main.async { - switch path.status { - case .unsatisfied: - if #available(iOS 14.2, *), path.unsatisfiedReason == .cellularDenied { - strongSelf.networkStatus = .cellularDenied - } else { - strongSelf.networkStatus = .notConnected - } - case .satisfied: - strongSelf.networkStatus = .connected - default: - strongSelf.networkStatus = .unknown - } - } + networkMonitor.cancel() } - networkMonitor.start(queue: networkMonitoringQueue) } /// Encrypt and upload an archive created with the given builder. diff --git a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Login/SingleStudyLoginView.swift b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Login/SingleStudyLoginView.swift index 181090633..5788fab30 100644 --- a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Login/SingleStudyLoginView.swift +++ b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Login/SingleStudyLoginView.swift @@ -183,7 +183,7 @@ struct LoginView_Previews: PreviewProvider { static var previews: some View { Group { SingleStudyLoginView() - .environmentObject(SingleStudyAppManager(appId: kPreviewStudyId)) + .environmentObject(SingleStudyAppManager(mockType: .preview)) } } } diff --git a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/SingleStudyAppManager.swift b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/SingleStudyAppManager.swift index dfb709130..829f7cb2d 100644 --- a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/SingleStudyAppManager.swift +++ b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/SingleStudyAppManager.swift @@ -39,8 +39,8 @@ open class AbstractSingleStudyAppManager : BridgeClientAppManager { private var studyManager: NativeStudyManager? private var notificationManager: NativeScheduledNotificationManager? - public override init(platformConfig: IOSPlatformConfig, pemPath: String? = nil) { - super.init(platformConfig: platformConfig, pemPath: pemPath) + public override init(platformConfig: IOSPlatformConfig, pemPath: String, backgroundProcessId: String) { + super.init(platformConfig: platformConfig, pemPath: pemPath, backgroundProcessId: backgroundProcessId) let studyId = self.isPreview ? previewStudy.identifier : sharedUserDefaults.string(forKey: kStudyIdKey) self.study = studyId.map { .init(identifier: $0) } if self.isPreview { diff --git a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayFinishedView.swift b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayFinishedView.swift index 86101fe83..1bd6fbb05 100644 --- a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayFinishedView.swift +++ b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayFinishedView.swift @@ -125,6 +125,6 @@ fileprivate struct PreviewAllTodayFinished : View { struct TodayFinishedView_Previews: PreviewProvider { static var previews: some View { PreviewAllTodayFinished() - .environmentObject(SingleStudyAppManager(appId: kPreviewStudyId)) + .environmentObject(SingleStudyAppManager(mockType: .preview)) } } diff --git a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayView.swift b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayView.swift index 3fb8064a3..185487fbb 100644 --- a/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayView.swift +++ b/SwiftPackage/Sources/BridgeClientUI/SingleStudy/Views/TodayView.swift @@ -166,7 +166,7 @@ extension AnyTransition { // Used to allow previewing the `TodayView` fileprivate struct PreviewTodayView : View { - @StateObject var bridgeManager = SingleStudyAppManager(appId: kPreviewStudyId) + @StateObject var bridgeManager = SingleStudyAppManager(mockType: .preview) @StateObject var viewModel = TodayTimelineViewModel() var body: some View { diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/MockURLSession.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/MockURLSession.swift similarity index 98% rename from SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/MockURLSession.swift rename to SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/MockURLSession.swift index 7a68db14c..16c0805df 100644 --- a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/MockURLSession.swift +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/MockURLSession.swift @@ -11,7 +11,7 @@ class MockHTTPURLResponse : HTTPURLResponse { } class MockTask : NSObject, BridgeURLSessionTask { - + var state: URLSessionTask.State = .running var session: MockURLSession var request: URLRequest var mockResponse: MockHTTPURLResponse? @@ -52,8 +52,10 @@ class MockUploadTask : MockTask, BridgeURLSessionUploadTask { } override func resume() { + self.state = .running session.delegateQueue.addOperation { [self] in (_, mockResponse) = session.dataAndResponse(for: request) + self.state = .completed session.bridgeDelegate?.bridgeUrlSession(session, task: self, didCompleteWithError: nil) session.remove(mockTask: self) } diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/BridgeFileUploadManagerTestCase.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/BridgeFileUploadManagerTestCase.swift similarity index 100% rename from SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/BridgeFileUploadManagerTestCase.swift rename to SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/BridgeFileUploadManagerTestCase.swift diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/MockAppManager.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/MockAppManager.swift similarity index 100% rename from SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/MockAppManager.swift rename to SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/MockAppManager.swift diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/ParticipantFileUploadAPITests.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/ParticipantFileUploadAPITests.swift similarity index 98% rename from SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/ParticipantFileUploadAPITests.swift rename to SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/ParticipantFileUploadAPITests.swift index 4ca0e377f..730b1516a 100644 --- a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/ParticipantFileUploadAPITests.swift +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/ParticipantFileUploadAPITests.swift @@ -13,7 +13,7 @@ class ParticipantFileUploadAPITests : XCTestCase, BridgeFileUploadManagerTestCas var uploadRequestSuccessResponseFile: String = "pf-upload-request-success" var uploadRequestExpiredResponseFile: String = "pf-upload-request-expired" var mockURLSession: MockURLSession = MockURLSession() - var mockAppManager: MockBridgeClientAppManager = MockBridgeClientAppManager(appId: "not-a-real-appid") + var mockAppManager: MockBridgeClientAppManager = MockBridgeClientAppManager(mockType: .unitTest) var testFileId: String = "TestFileId" var uploadApi: BridgeFileUploadAPI { mockAppManager.uploadManagerV1.participantFileUploadAPI diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/StudyDataUploadAPITests.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/StudyDataUploadAPITests.swift similarity index 98% rename from SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/StudyDataUploadAPITests.swift rename to SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/StudyDataUploadAPITests.swift index 28d02ad2d..b029ef593 100644 --- a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUploadV1/StudyDataUploadAPITests.swift +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V1/StudyDataUploadAPITests.swift @@ -13,7 +13,7 @@ class StudyDataUploadAPITests : XCTestCase, BridgeFileUploadManagerTestCaseTyped var uploadRequestSuccessResponseFile: String = "sd-upload-request-success" var uploadRequestExpiredResponseFile: String = "sd-upload-request-expired" var mockURLSession: MockURLSession = MockURLSession() - var mockAppManager: MockBridgeClientAppManager = MockBridgeClientAppManager(appId: "not-a-real-appid") + var mockAppManager: MockBridgeClientAppManager = MockBridgeClientAppManager(mockType: .unitTest) var testFileId: String = "TestFileId" var uploadApi: BridgeFileUploadAPI { mockAppManager.uploadManagerV1.studyDataUploadAPI diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/ArchiveUploadProcessorTests.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/ArchiveUploadProcessorTests.swift new file mode 100644 index 000000000..cc5e32a34 --- /dev/null +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/ArchiveUploadProcessorTests.swift @@ -0,0 +1,97 @@ +// Created 9/5/23 +// swift-tools-version:5.0 + +import XCTest +@testable import BridgeClientExtension +import ResultModel + +final class ArchiveUploadProcessorTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + + // Remove created files in app support directory + let baseURL = try FileManager.default.sharedAppSupportDirectory() + let url: URL = baseURL.appendingPathComponent("BridgeUploadsV2") + try FileManager.default.removeItem(at: url) + + // Remove any unprocessed uploads + UserDefaults.standard.removeObject(forKey: "unprocessedUploads") + } + + fileprivate func createArchiveUploadProcessor() -> (ArchiveUploadProcessor, Mock) { + let mock = Mock() + let pemPath = Bundle.module.path(forResource: "sample-study", ofType: "pem") + let processor = ArchiveUploadProcessor(pemPath: pemPath, sharedUserDefaults: .init()) + processor.uploadManager = mock.uploadManager + processor.isTestUser = false + return (processor, mock) + } + + fileprivate func createAssessmentScheduleInfo() -> (Date, AssessmentScheduleInfo) { + let now = Date() + let schedule = AssessmentScheduleInfo( + instanceGuid: "not-a-real-instanceGuid", + session: .init(instanceGuid: UUID().uuidString, + eventTimestamp: "not-a-real-eventTimestamp", + scheduledOn: now.addingTimeInterval(-60*60), + assessments: ["not-a-real-guid"]), + assessmentInfo: .init(identifier: "not-a-real-assessmentIdentifier", + key: nil, + guid: "not-a-real-guid", + label: nil)) + return (now, schedule) + } + + func testEncryptAndUpload_AssessmentArchiveBuilder() async throws { + let (uploadProcessor, mock) = createArchiveUploadProcessor() + let (now, schedule) = createAssessmentScheduleInfo() + let step = ResultObject(identifier: "step1", startDate: now.addingTimeInterval(-5*60), endDate: now) + let result = AssessmentResultObject(identifier: schedule.assessmentIdentifier, + startDate: step.startDate, + endDate: step.endDate, + taskRunUUID: UUID(), + stepHistory: [step], + path: [.init(identifier: "step1", direction: .forward)]) + let builder = AssessmentArchiveBuilder(result, schedule: schedule)! + + // -- Method under test + await uploadProcessor.asyncEncryptAndUpload(using: builder) + + // This silences warnings about retaining the mock (which can zombie if you don't) + XCTAssertEqual(mock.nativeManager.queuedUploadFiles.count, 1) + XCTAssertEqual(mock.uploadManager.retryQueue.count, 1) + + // Should not be any markers for unprocessed uploads + XCTAssertNil(uploadProcessor.sharedUserDefaults.object(forKey: "unprocessedUploads")) + + // Look for the original encrypted file to have been moved and deleted + if let encryptedUrl = builder.archive.encryptedURL { + let exists = FileManager.default.fileExists(atPath: encryptedUrl.absoluteString) + XCTAssertFalse(exists, "Expecting file \(encryptedUrl) to have been deleted.") + } else { + XCTFail("The builder encrypted file is missing. Failed to encrypt.") + } + + // Look for the file to upload to be staged + if let uploadFile = mock.nativeManager.queuedUploadFiles.first { + let fullPath = mock.uploadManager.sandboxFileManager.fullyQualifiedPath(of: uploadFile.filePath) + let exists = FileManager.default.fileExists(atPath: fullPath) + XCTAssertTrue(exists, "Expecting file \(fullPath) to exist.") + } else { + XCTFail("Failed to queue the encrypted file for upload.") + } + } + +} + +fileprivate class Mock { + let networkManager: MockBackgroundNetworkManager = .init() + let nativeManager: MockNativeUploadManager = .init() + lazy var uploadManager: TestUploadManager = .init(backgroundNetworkManager: networkManager, + nativeUploadManager: nativeManager) +} diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/SandboxFileManagerTests.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/SandboxFileManagerTests.swift new file mode 100644 index 000000000..56f8c1154 --- /dev/null +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/SandboxFileManagerTests.swift @@ -0,0 +1,58 @@ +// Created 8/1/23 +// swift-tools-version:5.0 + +import XCTest +@testable import BridgeClientExtension + +fileprivate let simulatorBaseDirectory = "/Users/foo/Library/Developer/CoreSimulator/Devices/ABE156AA-B189-4719-AB02-794DC169A014/data" +fileprivate let phoneBaseDirectory = "/var/mobile/Containers/Data/Application/258CADCB-F730-4DD4-AB13-4D8B23CDF93D" + +final class SandboxFileManagerTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func createSandboxFileManager(_ simulator: Bool = true) -> SandboxFileManager { + let manager = SandboxFileManager() + manager.baseDirectory = simulator ? simulatorBaseDirectory : phoneBaseDirectory + return manager + } + + func testSandBoxRelativePath_Simulator() throws { + let manager = createSandboxFileManager() + let input = URL(string: "file://\(manager.baseDirectory)/Library/Application%20Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA")! + let relativePath = manager.sandboxRelativePath(of: input) + let expectedPath = "/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + XCTAssertEqual(expectedPath, relativePath) + } + + func testSandBoxRelativePath_Phone() throws { + let manager = createSandboxFileManager(false) + let input = URL(string: "file://\(manager.baseDirectory)/Library/Application%20Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA")! + let relativePath = manager.sandboxRelativePath(of: input) + let expectedPath = "/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + XCTAssertEqual(expectedPath, relativePath) + } + + func testFullyQualifiedPath_FromRelative_Simulator() throws { + let manager = createSandboxFileManager() + let input = "/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + let fullPath = manager.fullyQualifiedPath(of: input) + let expectedPath = "\(manager.baseDirectory)/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + XCTAssertEqual(expectedPath, fullPath) + } + + func testFullyQualifiedPath_FromFullPath_Simulator() throws { + let manager = createSandboxFileManager() + let input = "\(manager.baseDirectory)/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + let fullPath = manager.fullyQualifiedPath(of: input) + let expectedPath = "\(manager.baseDirectory)/Library/Application Support/StudyDataUploads/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + XCTAssertEqual(expectedPath, fullPath) + } + +} diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/UploadManagerTests.swift b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/UploadManagerTests.swift new file mode 100644 index 000000000..2c4a2baee --- /dev/null +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/FileUpload/V2/UploadManagerTests.swift @@ -0,0 +1,413 @@ +// Created 8/1/23 +// swift-tools-version:5.0 + +import XCTest +import BridgeClient +import JsonModel +@testable import BridgeClientExtension + +final class UploadManagerTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + let expectedTempFileDir = "/App Support/BridgeUploadsV2/STUDY_DATA/" + + fileprivate func createUploadManager() -> (manager: TestUploadManager, mock: Mock) { + let mock = Mock() + let manager = TestUploadManager( + backgroundNetworkManager: mock.networkManager, + sandboxFileManager: mock.sandboxFileManager, + nativeUploadManager: mock.nativeManager) + let fakePath = "\(fakeBaseDirectory)/App Support/\(manager.subdir)" + manager.uploadDirURL = URL(fileURLWithPath: fakePath, isDirectory: true) + + XCTAssertEqual(1, mock.networkManager.registeredHandlers.count) + XCTAssertEqual(manager, mock.networkManager.registeredHandlers.first as? UploadManager) + + return (manager, mock) + } + + // MARK: Upload study data archive + + func testUploadArchive_Offline() async { + let (manager, mock) = createUploadManager() + let (fileURL, schedule, startedOn) = mock.sandboxFileManager.setupFakeArchive() + + // -- Method under test + let success = await manager.uploadEncryptedArchive(fileUrl: fileURL, schedule: schedule, startedOn: startedOn) + XCTAssertTrue(success) + + // independent of network connectivity, the file should be copied and queued for upload + XCTAssertEqual(mock.nativeManager.queuedUploadFiles.count, 1) + guard let uploadFile = mock.nativeManager.queuedUploadFiles.first else { + XCTFail("Unexpected NULL") + return + } + XCTAssertEqual(fileURL, mock.sandboxFileManager.copyFileCalls[uploadFile.filePath]) + checkUploadFileExpectations(uploadFile) + + // and b/c the native upload manager failed to return an S3UploadSession, it should be + // queued to retry upload + XCTAssertEqual([uploadFile.filePath : false], manager.retryQueue) + } + + func testUploadArchive_Online() async throws { + let (manager, mock) = createUploadManager() + let (fileURL, schedule, startedOn) = mock.sandboxFileManager.setupFakeArchive() + let requestHeaders = ["foo" : "magoo"] + mock.nativeManager.queuedS3UploadSession = .init( + filePath: "ignored", + contentType: "application/zip", + uploadSessionId: "not-a-real-uploadSessionId-0", + url: "https://not-a-real-org.org/not-a-real-url", + requestHeaders: requestHeaders) + + // -- Method under test + let success = await manager.uploadEncryptedArchive(fileUrl: fileURL, schedule: schedule, startedOn: startedOn) + XCTAssertTrue(success) + + // Independent of network connectivity, the file should be copied and queued for upload + XCTAssertEqual(mock.nativeManager.queuedUploadFiles.count, 1) + guard let uploadFile = mock.nativeManager.queuedUploadFiles.first else { + XCTFail("Unexpected NULL") + return + } + XCTAssertEqual(fileURL, mock.sandboxFileManager.copyFileCalls[uploadFile.filePath]) + checkUploadFileExpectations(uploadFile) + + // If successful in requesting an S3UploadSession then the upload manager should + // call the network manager with that request. + XCTAssertEqual(mock.networkManager.uploadRequests.count, 1) + XCTAssertEqual(1, mock.sandboxFileManager.copyTempFiles.count) + guard let uploadTask = mock.networkManager.uploadRequests.first, + let tempFile = mock.sandboxFileManager.copyTempFiles.first + else { + XCTFail("Unexpected NULL") + return + } + + let expectedIdentifier = "not-a-real-uploadSessionId-0|\(uploadFile.filePath)" + XCTAssertEqual(expectedIdentifier, uploadTask.taskDescription) + XCTAssertEqual(requestHeaders, uploadTask.httpHeaders) + XCTAssertEqual(tempFile, uploadTask.fileURL) + XCTAssertEqual("https://not-a-real-org.org/not-a-real-url", uploadTask.urlString) + + // Check if the manager can handle the upload response + XCTAssertTrue(manager.canHandle(task: uploadTask)) + } + + func checkUploadFileExpectations(_ uploadFile: UploadFile ) { + XCTAssertEqual("application/zip", uploadFile.contentType) + XCTAssertEqual(1024, uploadFile.fileLength) + XCTAssertEqual("not-a-real-md5Hash", uploadFile.md5Hash) + XCTAssertTrue(uploadFile.encrypted) + XCTAssertEqual(.studyData, uploadFile.s3UploadType) + XCTAssertNotNil(uploadFile.metadata) + XCTAssertEqual("not-a-real-instanceGuid", uploadFile.metadata?.instanceGuid) + XCTAssertEqual("not-a-real-eventTimestamp", uploadFile.metadata?.eventTimestamp) + XCTAssertNotNil(uploadFile.metadata?.startedOn) + XCTAssertTrue(uploadFile.filePath.hasPrefix(expectedTempFileDir), "\(uploadFile.filePath) does not have expected prefix") + } + + // MARK: Upload S3 responses + + fileprivate func createS3UploadTask(_ manager: TestUploadManager, _ mock: Mock) -> (filePath: String, uploadId: String, uploadTask: MockBackgroundNetworkManager.UploadRequest) { + let filePath = "\(expectedTempFileDir)not-a-real-file" + let uploadId = "not-a-real-uploadSessionId-0" + let uploadTask = MockBackgroundNetworkManager.UploadRequest( + fileURL: mock.sandboxFileManager.fileURL(of: filePath), + httpHeaders: ["foo" : "magoo"], + urlString: "https://not-a-real-org.org/\(uploadId)/not-a-real-url", + taskDescription: manager.urlUploadTaskIdentifier(filePath: filePath, uploadSessionId: uploadId)) + + // check test setup assumptions + XCTAssertTrue(manager.canHandle(task: uploadTask)) + + return (filePath, uploadId, uploadTask) + } + + func testS3UploadResponse_HappyPath() async throws { + let (manager, mock) = createUploadManager() + let (filePath, _, uploadTask) = createS3UploadTask(manager, mock) + + // for the happy path, the S3 response code is 200 + uploadTask.setResponse(statusCode: 200) + // and the native upload was successful + mock.nativeManager.uploadFinishedResponse[filePath] = true + + // -- Method under test + + // Once the S3 upload is complete, the shared network manager will call back to the upload manager. + // This should fire a call to the native upload manager (Kotlin) that will then attempt to mark the + // file as done. + try await manager.urlSession(task: uploadTask, didCompleteWithError: nil) + + // Check that the file was marked as uploaded + XCTAssertEqual([filePath], mock.nativeManager.uploadFinished) + // Check for file deleted + XCTAssertEqual([filePath], mock.sandboxFileManager.removeTempFiles) + // Check that the file was *not* queued for retry + XCTAssertEqual([:], manager.retryQueue) + } + + func checkRetryS3Failure(statusCode: Int) async throws { + let (manager, mock) = createUploadManager() + let (filePath, _, uploadTask) = createS3UploadTask(manager, mock) + + uploadTask.setResponse(statusCode: statusCode) + + // -- Methods under test + + // Once the S3 upload is complete, the shared network manager will call back to the upload manager. + // This should fire a call to the native upload manager (Kotlin) that will then attempt to mark the + // file as done. + try await manager.urlSession(task: uploadTask, didCompleteWithError: nil) + + // check that the file was not marked for upload + XCTAssertEqual([], mock.nativeManager.uploadFinished) + // and that the file was not removed + XCTAssertEqual([], mock.sandboxFileManager.removeTempFiles) + // but it is marked for retry + XCTAssertEqual([filePath:false], manager.retryQueue) + } + + func testS3UploadResponse_403() async throws { + try await checkRetryS3Failure(statusCode: 403) + } + + func testS3UploadResponse_409() async throws { + try await checkRetryS3Failure(statusCode: 409) + } + + func testS3UploadResponse_500() async throws { + try await checkRetryS3Failure(statusCode: 500) + } + + func testS3UploadResponse_503() async throws { + try await checkRetryS3Failure(statusCode: 503) + } + + // MARK: Sanity checks of Kotlin framework + + func testKotlinDataClassEquality() { + // Check to make sure that data classes are properly handling equality and hash value. + let obj1 = PendingUploadFile(filePath: "/foo/goo/laroo", uploadSessionId: "1234567890") + let obj2 = PendingUploadFile(filePath: "/foo/goo/laroo", uploadSessionId: "1234567890") + XCTAssertEqual(obj1, obj2) + XCTAssertEqual(obj1.hashValue, obj2.hashValue) + } + +} + +fileprivate let fakeBaseDirectory = "/Foo/48B0BAEB-94C4-4158-953C-74AB0935E5FA" + +class TestUploadManager : UploadManager { + var retryQueue: [String : Bool] = [:] + + override func enqueueForRetry(filePath: String, uploaded: Bool) async { + // Do not call through to the sync manager + retryQueue[filePath] = uploaded + } +} + +/// Used to hold onto the mocks so they aren't prematurely released +fileprivate class Mock { + let networkManager: MockBackgroundNetworkManager = .init() + let sandboxFileManager: MockSandboxFileManager = .init() + let nativeManager: MockNativeUploadManager = .init() +} + +class MockSandboxFileManager : SandboxFileManager { + + var removeTempFiles: [String] = [] + var copyTempFiles: [URL] = [] + var copyFileCalls: [String : URL] = [:] + var touchCalls: [URL : Date] = [:] + var contentLengthMap: [URL : Int] = [:] + var md5Map: [URL : String] = [:] + + override init() { + super.init() + self.baseDirectory = fakeBaseDirectory + } + + override func copyFile(from fileURL: URL, to tempFileURL: URL) -> Bool { + let invariantPath = self.sandboxRelativePath(of: tempFileURL) + copyTempFiles.append(tempFileURL) + copyFileCalls[invariantPath] = fileURL + return true + } + + override func removeTempFile(filePath: String) { + removeTempFiles.append(filePath) + } + + override func touch(fileUrl: URL) { + touchCalls[fileUrl] = Date() + } + + override func fileContentLength(_ fileUrl: URL) -> Int? { + contentLengthMap[fileUrl] ?? copyFileCalls[sandboxRelativePath(of: fileUrl)].flatMap { contentLengthMap[$0] } + } + + override func fileMD5String(_ fileUrl: URL) -> String? { + md5Map[fileUrl] ?? copyFileCalls[sandboxRelativePath(of: fileUrl)].flatMap { md5Map[$0] } + } + + override func createSubdirectory(baseURL: URL, subdir: String) throws -> URL { + baseURL.appendingPathComponent(subdir, isDirectory: true) + } + + func setupFakeArchive() -> (URL, AssessmentScheduleInfo, Date) { + let now = Date() + let schedule = AssessmentScheduleInfo( + instanceGuid: "not-a-real-instanceGuid", + session: .init(instanceGuid: UUID().uuidString, + eventTimestamp: "not-a-real-eventTimestamp", + scheduledOn: now.addingTimeInterval(-60*60), + assessments: ["not-a-real-guid"]), + assessmentInfo: .init(identifier: "not-a-real-assessmentIdentifier", + key: nil, + guid: "not-a-real-guid", + label: nil)) + let filename = UUID().uuidString + let fileURL = URL(fileURLWithPath: "\(fakeBaseDirectory)/tmp/\(filename).zip.encypted") + let startedOn = now.addingTimeInterval(-5*60) + contentLengthMap[fileURL] = 1024 + md5Map[fileURL] = "not-a-real-md5Hash" + return (fileURL, schedule, startedOn) + } + +} + +class MockBackgroundNetworkManager : NSObject, SharedBackgroundUploadManager { + + let mockURLSession: MockURLSession = MockURLSession() + + let sessionDelegateQueue: OperationQueue = { + let delegateQueue = OperationQueue() + delegateQueue.maxConcurrentOperationCount = 1 + return delegateQueue + }() + + var isAppBackground: Bool = false + + var registeredHandlers: [BridgeURLSessionHandler] = [] + var uploadRequests: [UploadRequest] = [] + var tasks: [BridgeURLSessionTask] = [] + + func registerBackgroundTransferHandler(_ handler: BridgeURLSessionHandler) { + registeredHandlers.append(handler) + } + + @discardableResult + func uploadFile(_ fileURL: URL, httpHeaders: [String : String]?, to urlString: String, taskDescription: String) -> Bool { + let request: UploadRequest = .init(fileURL: fileURL, httpHeaders: httpHeaders, urlString: urlString, taskDescription: taskDescription) + uploadRequests.append(request) + return true + } + + class UploadRequest : NSObject, BridgeURLSessionUploadTask { + let fileURL: URL + let httpHeaders: [String : String]? + let urlString: String + var taskDescription: String? + var state: URLSessionTask.State = .running + + init(fileURL: URL, httpHeaders: [String : String]?, urlString: String, taskDescription: String) { + self.fileURL = fileURL + self.httpHeaders = httpHeaders + self.urlString = urlString + self.taskDescription = taskDescription + self.originalRequest = URL(string: urlString).map { + var request = URLRequest(url: $0) + request.allHTTPHeaderFields = httpHeaders + request.httpMethod = HTTPMethod.PUT.rawValue + return request + } + } + + var taskType: BridgeClientExtension.BridgeURLSessionTaskType { + .upload + } + + var originalRequest: URLRequest? + var response: URLResponse? + + var didCallResume: Bool = false + var didCallCancel: Bool = false + + func setResponse(statusCode: Int) { + self.response = HTTPURLResponse(url: URL(string: urlString)!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + } + + func resume() { + didCallResume = true + } + + func cancel() { + didCallCancel = true + } + } + + func getAllTasks() async -> [BridgeURLSessionTask] { + tasks + } +} + +class MockNativeUploadManager : NSObject, BridgeClientUploadManager { + + var queuedUploadFiles: [UploadFile] = [] + var queuedS3UploadSession: S3UploadSession? + + var uploadFiles: [String] = [] + var s3Uploads: [String : S3UploadSession] = [:] + var uploadFinished: [String] = [] + var uploadFailedPermanently: [String] = [] + var uploadFinishedResponse: [String : Bool] = [:] + var processFinishedUploadsResponse: Bool = false + + func getPendingUploadFiles() async -> [PendingUploadFile] { + uploadFiles.map { + .init(filePath: $0, uploadSessionId: s3Uploads[$0]?.uploadSessionId) + } + } + + func hasPendingUploads() -> Bool { + uploadFiles.count > 0 || uploadFinishedResponse.count > 0 + } + + func queueAndRequestUploadSession(uploadFile: UploadFile) async -> S3UploadSession? { + queuedUploadFiles.append(uploadFile) + return queuedS3UploadSession.map { + .init(filePath: uploadFile.filePath, + contentType: $0.contentType, + uploadSessionId: $0.uploadSessionId, + url: $0.url, + requestHeaders: $0.requestHeaders) + } + } + + func requestUploadSession(filePath: String) async -> S3UploadSession? { + return s3Uploads[filePath] + } + + func markUploadFileFinished(filePath: String, uploadSessionId: String) async -> Bool { + uploadFinished.append(filePath) + return uploadFinishedResponse[filePath] ?? false + } + + func markUploadUnrecoverableFailure(filePath: String) { + uploadFailedPermanently.append(filePath) + } + + func processFinishedUploads() async -> Bool { + return processFinishedUploadsResponse + } +} diff --git a/SwiftPackage/Tests/BridgeClientExtensionTests/Resources/sample-study.pem b/SwiftPackage/Tests/BridgeClientExtensionTests/Resources/sample-study.pem new file mode 100644 index 000000000..c0762358e --- /dev/null +++ b/SwiftPackage/Tests/BridgeClientExtensionTests/Resources/sample-study.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/TCCAuWgAwIBAgIUDOpoLAbiQpTp226smOV0Lsahq0kwDQYJKoZIhvcNAQEL +BQAwgY0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH +DAdTZWF0dGxlMRUwEwYDVQQKDAxGb28gVGVzdCBPcmcxDDAKBgNVBAsMA0dvbzEV +MBMGA1UEAwwMRm9vIFRlc3QgT3JnMRswGQYJKoZIhvcNAQkBFgxmb29Ac3BhbS5u +ZXQwHhcNMjMwOTA3MDIyNjQxWhcNMzMwOTA0MDIyNjQxWjCBjTELMAkGA1UEBhMC +VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFTATBgNV +BAoMDEZvbyBUZXN0IE9yZzEMMAoGA1UECwwDR29vMRUwEwYDVQQDDAxGb28gVGVz +dCBPcmcxGzAZBgkqhkiG9w0BCQEWDGZvb0BzcGFtLm5ldDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMYrZcghjv9HABNoLw0zTUSa5wfcfwYPVwCUUjTr +j5C9TugTdyfVvSdy0DRWmGaQMNcEn1BkjeOT9gu+/Edf1ySl6CLjwRUODy4HduvJ +PXoUGkNrOfRJ1zWQ6zeC1VukZG0E5/PdGDd4o12+XT1UQttXjlUgksUl3wH0gcYX +0yU0rK8Eg46pr/Vfl31LhOWCotRHVVGDUDBmyS0gbhl2MeCi5cid75UHpyA+d2oc +Z1xNHGg67RsaDfdHlIPXWVcjYfpj507l3he0tLGR2d2oluMKF87YISxPYkmViOAA +SUspip/eGEKwjdachik6mFw5C/Iqzww/GnUHkkNujKhwxq8CAwEAAaNTMFEwHQYD +VR0OBBYEFLq7UrPB4LAuNBi7fUAFVoyrOH/+MB8GA1UdIwQYMBaAFLq7UrPB4LAu +NBi7fUAFVoyrOH/+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AJUAU4xY9Sx+1cIQ38jAi75RyWB9GQMzaESW+XwJZl99eEO0qtRpqbqQbOVcRkZY +nNYurVVj88TTaxbjf3MmXFnioSQ1RwJA6EqzZ0EmnrkkeQ+2sz8FLe5lNvr6L5aI +HQIBSA3OHasj43/gOvxHTnEriMh9PUIrRF5d44uaCsiJlhUxKlPVnEjKSW8lTzRb +0bEsIyniQtU2vRyAQszLmLn6Y/Jovligd7kBt9QKVE4Ead8OrQxD9ql3yOBws25W +MJexrQq9BRIRgYIJl5F6cX3mLKV4hs4bNZ51oQwt1UMrtNBTS/qNqcKwxUN2K2+K +gqQRZuvx+xDkMuvD/K/PE1c= +-----END CERTIFICATE----- diff --git a/SwiftPackage/Tests/BridgeClientUITests/TodayTimelineViewModelTests.swift b/SwiftPackage/Tests/BridgeClientUITests/TodayTimelineViewModelTests.swift index b3f04fd04..8b5e9e35f 100644 --- a/SwiftPackage/Tests/BridgeClientUITests/TodayTimelineViewModelTests.swift +++ b/SwiftPackage/Tests/BridgeClientUITests/TodayTimelineViewModelTests.swift @@ -15,7 +15,7 @@ final class TodayTimelineViewModelTests: XCTestCase { return } - let bridgeManager = SingleStudyAppManager(appId: kPreviewStudyId) + let bridgeManager = SingleStudyAppManager(mockType: .preview) let todayManager = TodayTimelineViewModel() todayManager.onAppear(bridgeManager: bridgeManager, previewSchedules: previewSchedulesA) @@ -42,7 +42,7 @@ final class TodayTimelineViewModelTests: XCTestCase { return } - let bridgeManager = SingleStudyAppManager(appId: kPreviewStudyId) + let bridgeManager = SingleStudyAppManager(mockType: .preview) let todayManager = TodayTimelineViewModel() todayManager.onAppear(bridgeManager: bridgeManager, previewSchedules: previewSchedulesA) @@ -139,7 +139,7 @@ final class TodayTimelineViewModelTests: XCTestCase { return } - let bridgeManager = SingleStudyAppManager(appId: kPreviewStudyId) + let bridgeManager = SingleStudyAppManager(mockType: .preview) let todayManager = TodayTimelineViewModel() todayManager.onAppear(bridgeManager: bridgeManager, previewSchedules: previewSchedulesA) diff --git a/bridge-client/src/androidMain/kotlin/org/sagebionetworks/bridge/kmm/shared/upload/UploadManager.kt b/bridge-client/src/androidMain/kotlin/org/sagebionetworks/bridge/kmm/shared/upload/UploadManager.kt index 325c3d3f6..2f01bd85b 100644 --- a/bridge-client/src/androidMain/kotlin/org/sagebionetworks/bridge/kmm/shared/upload/UploadManager.kt +++ b/bridge-client/src/androidMain/kotlin/org/sagebionetworks/bridge/kmm/shared/upload/UploadManager.kt @@ -69,7 +69,7 @@ internal class UploadManager( Logger.d("uploadingToS3 $uploadFile") s3UploadApi.uploadFile(uploadSession.url, uploadFile) //TODO: Handle network exceptions -nbrown 4/26/21 FileSystem.SYSTEM.delete(uploadFile.filePath.toPath()) //TODO: Handle delete failure -nbrown 12/16/20 - markUploadFileFinished(uploadFile) + markUploadFileFinished(uploadFile, uploadSession.id) } catch (error: Throwable) { Logger.e("Error uploadingToS3 $uploadFile", error) diff --git a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/cache/ResourceType.kt b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/cache/ResourceType.kt index 076921e97..094eae8e8 100644 --- a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/cache/ResourceType.kt +++ b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/cache/ResourceType.kt @@ -16,7 +16,8 @@ enum class ResourceType { ADHERENCE_RECORD, STUDY, STUDY_INFO, - PARTICIPANT_REPORT + PARTICIPANT_REPORT, + UPLOADED_FILE_RECORD, } enum class ResourceStatus { diff --git a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadFile.kt b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadFile.kt index 2b0581d20..dd106863e 100644 --- a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadFile.kt +++ b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadFile.kt @@ -62,6 +62,10 @@ internal fun UploadFileIdentifiable.getUploadSessionResourceId(): String { return "uploadSession--$filePath" } +internal fun UploadFileIdentifiable.getUploadedFileRecordResourceId(): String { + return "uploadedFileRecord--$filePath" +} + internal class UploadFileId(override val filePath: String) : UploadFileIdentifiable enum class S3UploadType { diff --git a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadMetadata.kt b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadMetadata.kt index f18fe7f51..b2151da75 100644 --- a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadMetadata.kt +++ b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadMetadata.kt @@ -11,11 +11,6 @@ data class UploadMetadata( val eventTimestamp: String? = null, val startedOn: String? = null, ) { - - fun getId(): String? { - return if (instanceGuid == null) null else "$instanceGuid|$eventTimestamp|$startedOn" - } - internal fun toJsonMap(): Map { val jsonCoder = Json { ignoreUnknownKeys = true @@ -24,12 +19,4 @@ data class UploadMetadata( } return jsonCoder.encodeToJsonElement(UploadMetadata.serializer(),this).jsonObject.toMap() } - - /** - * Used on iOS to encode metadata. - * (`JsonElement` does not implement the Swift Codable protocol - syoung 07/10/2023) - */ - fun toStringMap(): Map { - return toJsonMap().mapValues { it.toString() } - } } \ No newline at end of file diff --git a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadedFileRecord.kt b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadedFileRecord.kt new file mode 100644 index 000000000..e5144a83a --- /dev/null +++ b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/models/UploadedFileRecord.kt @@ -0,0 +1,13 @@ +package org.sagebionetworks.bridge.kmm.shared.models + +import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable + +@Serializable +data class UploadedFileRecord( + override val filePath: String, + val uploadTimestamp: Instant, + val uploadSessionId: String? = null, + val metadata: UploadMetadata? = null, +) : UploadFileIdentifiable + diff --git a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepo.kt b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepo.kt index 371ec61a0..e8e188526 100644 --- a/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepo.kt +++ b/bridge-client/src/commonMain/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepo.kt @@ -3,7 +3,6 @@ package org.sagebionetworks.bridge.kmm.shared.repo import co.touchlab.kermit.Logger import co.touchlab.stately.ensureNeverFrozen import io.ktor.client.HttpClient -import io.ktor.http.headers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.datetime.Clock @@ -14,7 +13,9 @@ import kotlinx.datetime.plus import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.sagebionetworks.bridge.kmm.shared.apis.UploadsApi +import org.sagebionetworks.bridge.kmm.shared.cache.Resource import org.sagebionetworks.bridge.kmm.shared.cache.ResourceDatabaseHelper +import org.sagebionetworks.bridge.kmm.shared.cache.ResourceStatus import org.sagebionetworks.bridge.kmm.shared.cache.ResourceType import org.sagebionetworks.bridge.kmm.shared.cache.loadResource import org.sagebionetworks.bridge.kmm.shared.models.S3UploadSession @@ -23,8 +24,10 @@ import org.sagebionetworks.bridge.kmm.shared.models.UploadFileId import org.sagebionetworks.bridge.kmm.shared.models.UploadFileIdentifiable import org.sagebionetworks.bridge.kmm.shared.models.UploadRequest import org.sagebionetworks.bridge.kmm.shared.models.UploadSession +import org.sagebionetworks.bridge.kmm.shared.models.UploadedFileRecord import org.sagebionetworks.bridge.kmm.shared.models.getUploadFileResourceId import org.sagebionetworks.bridge.kmm.shared.models.getUploadSessionResourceId +import org.sagebionetworks.bridge.kmm.shared.models.getUploadedFileRecordResourceId internal open class UploadRepo( httpClient: HttpClient, @@ -71,6 +74,18 @@ internal open class UploadRepo( return resources.mapNotNull { it.loadResource() } } + /** + * Get a tuple of the filepath and upload session for all pending uploads. + */ + fun getPendingUploadFiles(): List { + return getUploadFiles().map { + PendingUploadFile( + it.filePath, + getCachedUploadSession(it)?.id + ) + } + } + /** * Remove the [UploadFile] from the local cache and mark the upload session as "completed" * by setting `needSave = true`. This is called after successfully uploading to S3. @@ -80,15 +95,60 @@ internal open class UploadRepo( * complicated - the file location can change with app or OS updates and synchronization is * a bit brittle. It's less confusing to handle this in Swift. syoung 07/14/2023 */ - fun markUploadFileFinished(uploadFile: UploadFileIdentifiable) { + fun markUploadFileFinished(uploadFile: UploadFileIdentifiable, uploadSessionId: String?) { val resourceId = uploadFile.getUploadSessionResourceId() - val resource = database.getResource(resourceId, ResourceType.UPLOAD_SESSION, + val uploadSessionResource = database.getResource(resourceId, ResourceType.UPLOAD_SESSION, ResourceDatabaseHelper.APP_WIDE_STUDY_ID )?.copy(needSave = true) + val uploadedFileRecord = UploadedFileRecord( + filePath = uploadFile.filePath, + uploadTimestamp = Clock.System.now(), + uploadSessionId = uploadSessionId, + metadata = getUploadFile(uploadFile.filePath)?.metadata + ) database.database.transaction { - resource?.let { + // Mark the upload session as "completed" by updating with `needSave = true` + uploadSessionResource?.let { database.insertUpdateResource(it) } + // Remove the upload file that is queued for upload to S3 + database.removeResource( + uploadFile.getUploadFileResourceId(), + ResourceType.FILE_UPLOAD, + ResourceDatabaseHelper.APP_WIDE_STUDY_ID + ) + // Insert a record with the information for the uploaded file record + database.insertUpdateResource( + Resource( + identifier = uploadedFileRecord.filePath, + type = ResourceType.UPLOADED_FILE_RECORD, + secondaryId = uploadedFileRecord.metadata?.instanceGuid ?: ResourceDatabaseHelper.DEFAULT_SECONDARY_ID, + studyId = ResourceDatabaseHelper.APP_WIDE_STUDY_ID, + json = Json.encodeToString(uploadedFileRecord), + lastUpdate = uploadedFileRecord.uploadTimestamp.toEpochMilliseconds(), + status = ResourceStatus.SUCCESS, + needSave = false + ) + ) + } + } + + /** + * Remove the [UploadFile] from the local cache. This is called after an unrecoverable failure + * to upload to S3. + * + * Note: Deleting the file must be handled natively as a part of S3 upload rather than using + * expect/actually. This is done b/c on iOS coordinating file read/write is much more + * complicated - the file location can change with app or OS updates and synchronization is + * a bit brittle. It's less confusing to handle this in Swift. syoung 07/14/2023 + */ + fun removeUploadFile(uploadFile: UploadFileIdentifiable) { + database.database.transaction { + database.removeResource( + uploadFile.getUploadSessionResourceId(), + ResourceType.UPLOAD_SESSION, + ResourceDatabaseHelper.APP_WIDE_STUDY_ID + ) database.removeResource( uploadFile.getUploadFileResourceId(), ResourceType.FILE_UPLOAD, @@ -109,6 +169,18 @@ internal open class UploadRepo( ).firstOrNull()?.loadResource() } + /** + * If the file has been uploaded, then this will get the record associated with + * marking that file as "done". + */ + fun getUploadedFileRecord(filePath: String): UploadedFileRecord? { + return database.getResourcesById( + filePath, + ResourceType.UPLOADED_FILE_RECORD, + ResourceDatabaseHelper.APP_WIDE_STUDY_ID + ).firstOrNull()?.loadResource() + } + /** * Add the given upload file to the queue and request the S3 upload session. */ @@ -126,6 +198,9 @@ internal open class UploadRepo( return getUploadFile(filePath)?.let { getS3UploadSession(it) } } + /** + * Get the information needed to spawn an S3 upload. + */ suspend fun getS3UploadSession(uploadFile: UploadFile): S3UploadSession? { val uploadSession = getUploadSession(uploadFile) return if (uploadSession?.id == null) null @@ -138,40 +213,48 @@ internal open class UploadRepo( ) } + /** + * Retrieves cached UploadSession (if one exists). This method is responsible for ensuring + * that upload session has not expired and is valid. + */ + fun getCachedUploadSession(uploadFile: UploadFileIdentifiable): UploadSession? { + val uploadSession = database.getResource( + uploadFile.getUploadSessionResourceId(), + ResourceType.UPLOAD_SESSION, + ResourceDatabaseHelper.APP_WIDE_STUDY_ID + )?.loadResource() + return if (isValid(uploadSession)) uploadSession else null + } + + /** + * Is the upload session still valid (or has it expired)? + */ + private fun isValid(uploadSession: UploadSession?): Boolean { + if (uploadSession?.expires == null) return true + val desiredMinimumExpiration = Clock.System.now().plus( + UPLOAD_EXPIRY_WINDOW_MINUTES, + DateTimeUnit.MINUTE, + TimeZone.currentSystemDefault() + ) + val expires = Instant.parse(uploadSession.expires) + return (desiredMinimumExpiration < expires) + } + /** * Retrieves cached UploadSession (if one exists), else requests one from Bridge and caches session * before returning it. This method is responsible for ensuring that upload session has not expired and is valid. */ suspend fun getUploadSession(uploadFile: UploadFile): UploadSession? { - val identifier = uploadFile.getUploadSessionResourceId() - val resource = database.getResource(identifier, ResourceType.UPLOAD_SESSION, - ResourceDatabaseHelper.APP_WIDE_STUDY_ID - ) - resource?.let { - val uploadSession = resource.loadResource() - uploadSession?.expires?.let { - val desiredMinimumExpiration = Clock.System.now().plus( - UPLOAD_EXPIRY_WINDOW_MINUTES, - DateTimeUnit.MINUTE, - TimeZone.currentSystemDefault() - ) - val expires = Instant.parse(uploadSession.expires) - if (desiredMinimumExpiration < expires) { - //return cached upload session - return uploadSession - } - } - } - val updatedResource = remoteLoadResource( + return getCachedUploadSession(uploadFile) ?: + remoteLoadResource( database = database, - identifier = identifier, + identifier = uploadFile.getUploadSessionResourceId(), resourceType = ResourceType.UPLOAD_SESSION, studyId = ResourceDatabaseHelper.APP_WIDE_STUDY_ID, curResource = null, remoteLoad = {loadRemoteUploadSession(uploadFile.getUploadRequest())} - ) + ).loadResource() //TODO: Need to think through error handling if we are unable to get an UploadSession -nbrown 12/16/20 - return updatedResource.loadResource() } private suspend fun loadRemoteUploadSession(uploadRequest: UploadRequest): String { @@ -210,3 +293,9 @@ internal open class UploadRepo( } } } + +data class PendingUploadFile( + val filePath: String, + val uploadSessionId: String? +) + diff --git a/bridge-client/src/commonTest/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepoTest.kt b/bridge-client/src/commonTest/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepoTest.kt index a7a0933a9..99b99e073 100644 --- a/bridge-client/src/commonTest/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepoTest.kt +++ b/bridge-client/src/commonTest/kotlin/org/sagebionetworks/bridge/kmm/shared/repo/UploadRepoTest.kt @@ -14,6 +14,8 @@ import kotlinx.datetime.minus import kotlinx.datetime.plus import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive import org.sagebionetworks.bridge.kmm.shared.BaseTest import org.sagebionetworks.bridge.kmm.shared.cache.Resource import org.sagebionetworks.bridge.kmm.shared.cache.ResourceDatabaseHelper @@ -25,6 +27,7 @@ import org.sagebionetworks.bridge.kmm.shared.models.S3UploadSession import org.sagebionetworks.bridge.kmm.shared.models.UploadFile import org.sagebionetworks.bridge.kmm.shared.models.UploadFileId import org.sagebionetworks.bridge.kmm.shared.models.UploadFileIdentifiable +import org.sagebionetworks.bridge.kmm.shared.models.UploadMetadata import org.sagebionetworks.bridge.kmm.shared.models.UploadSession import org.sagebionetworks.bridge.kmm.shared.models.UploadStatus import org.sagebionetworks.bridge.kmm.shared.models.UploadValidationStatus @@ -53,10 +56,10 @@ class UploadRepoTest : BaseTest() { fun mockUploadFile(): UploadFile { return UploadFile( - randomUUID(), - "application/zip", - 1024, - randomUUID(), + filePath = randomUUID(), + contentType = "application/zip", + fileLength = 1024, + md5Hash = randomUUID(), ) } @@ -139,7 +142,7 @@ class UploadRepoTest : BaseTest() { assertEquals(1L, initialCount) // Pretend to upload the S3 file by marking it as finished - repo.markUploadFileFinished(UploadFileId(uploadFile.filePath)) + repo.markUploadFileFinished(UploadFileId(uploadFile.filePath), sessionId) val afterUploadCount = database.getPendingUploadCount() assertEquals(1L, afterUploadCount) @@ -376,4 +379,15 @@ class UploadRepoTest : BaseTest() { } } + @Test + fun testUploadMetadata_toJsonMap() { + val metadata = UploadMetadata("foo", "goo", "maloo") + val json = metadata.toJsonMap() + val expectedJson: Map = mapOf( + "instanceGuid" to JsonPrimitive("foo"), + "eventTimestamp" to JsonPrimitive("goo"), + "startedOn" to JsonPrimitive("maloo"), + ) + assertEquals(expectedJson, json) + } } \ No newline at end of file diff --git a/bridge-client/src/iosMain/kotlin/org/sagebionetworks/bridge/kmm/shared/managers/NativeUploadManager.kt b/bridge-client/src/iosMain/kotlin/org/sagebionetworks/bridge/kmm/shared/managers/NativeUploadManager.kt index bff3f0c54..e84dcd953 100644 --- a/bridge-client/src/iosMain/kotlin/org/sagebionetworks/bridge/kmm/shared/managers/NativeUploadManager.kt +++ b/bridge-client/src/iosMain/kotlin/org/sagebionetworks/bridge/kmm/shared/managers/NativeUploadManager.kt @@ -1,5 +1,10 @@ package org.sagebionetworks.bridge.kmm.shared.managers +import co.touchlab.kermit.Logger +import co.touchlab.kermit.Severity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch @@ -8,6 +13,7 @@ import org.koin.core.component.inject import org.sagebionetworks.bridge.kmm.shared.models.S3UploadSession import org.sagebionetworks.bridge.kmm.shared.models.UploadFile import org.sagebionetworks.bridge.kmm.shared.models.UploadFileId +import org.sagebionetworks.bridge.kmm.shared.models.getUploadFileResourceId import org.sagebionetworks.bridge.kmm.shared.repo.* class NativeUploadManager : KoinComponent { @@ -15,7 +21,7 @@ class NativeUploadManager : KoinComponent { private val repo : UploadRepo by inject(mode = LazyThreadSafetyMode.NONE) private val adherenceRecordRepo : AdherenceRecordRepo by inject(mode = LazyThreadSafetyMode.NONE) private val authManager : AuthenticationRepository by inject(mode = LazyThreadSafetyMode.NONE) - private val scope = MainScope() + private val scope = CoroutineScope(Dispatchers.IO) // TODO: syoung 08/24/2023 Decide if this should be main scope? fun queueAndRequestUploadSession(uploadFile: UploadFile, callBack: (S3UploadSession?) -> Unit) { scope.launch { @@ -28,8 +34,15 @@ class NativeUploadManager : KoinComponent { } } - fun getUploadFiles(): List { - return repo.getUploadFiles().map { it.filePath } + fun hasPendingUploads(): Boolean { + return repo.database.getPendingUploadCount() > 0 + } + + fun getPendingUploadFiles(callBack: (List) -> Unit) { + scope.launch { + val ret = repo.getPendingUploadFiles() + callBack(ret) + } } fun requestUploadSession(filePath: String, callBack: (S3UploadSession?) -> Unit) { @@ -43,19 +56,26 @@ class NativeUploadManager : KoinComponent { } } - fun markUploadFileFinished(filePath: String, callBack: (Boolean) -> Unit) { - markAndProcessFinishedUploads(filePath, callBack) + fun markUploadUnrecoverableFailure(filePath: String) { + repo.removeUploadFile(UploadFileId(filePath)) } - fun processFinishedUploads(callBack: (Boolean) -> Unit) { - markAndProcessFinishedUploads(null, callBack) + fun markUploadFileFinished(filePath: String, uploadSessionId: String, callBack: (Boolean) -> Unit) { + val uploadFile = UploadFileId(filePath) + repo.markUploadFileFinished(uploadFile, uploadSessionId) + scope.launch { + try { + repo.completeUploadSession(uploadSessionId, uploadFile.getUploadFileResourceId()) + callBack(true) + } catch (throwable: Throwable) { + Logger.i("Failed to send upload complete to server: $uploadSessionId", throwable) + callBack(false) + } + } } - private fun markAndProcessFinishedUploads(filePath: String? = null, callBack: (Boolean) -> Unit) { + fun processFinishedUploads(callBack: (Boolean) -> Unit) { scope.launch { - if (filePath != null) { - repo.markUploadFileFinished(UploadFileId(filePath)) - } try { repo.processFinishedUploads() authManager.currentStudyId()?.let { @@ -68,6 +88,9 @@ class NativeUploadManager : KoinComponent { } } + fun hasMarkedFileAsUploaded(filePath: String): Boolean { + return repo.getUploadedFileRecord(filePath) != null + } } class PendingUploadObserver( diff --git a/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.pbxproj b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.pbxproj new file mode 100644 index 000000000..0183d2ee6 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.pbxproj @@ -0,0 +1,360 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + FF9FB31B2A9D61340044E76A /* BackgroundProcessTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9FB31A2A9D61340044E76A /* BackgroundProcessTestApp.swift */; }; + FF9FB31D2A9D61340044E76A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9FB31C2A9D61340044E76A /* ContentView.swift */; }; + FF9FB31F2A9D61350044E76A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF9FB31E2A9D61350044E76A /* Assets.xcassets */; }; + FF9FB3222A9D61350044E76A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF9FB3212A9D61350044E76A /* Preview Assets.xcassets */; }; + FF9FB32A2A9DCB460044E76A /* BackgroundProcessSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9FB3292A9DCB460044E76A /* BackgroundProcessSyncManager.swift */; }; + FFF9174F2A9EDF57004CE632 /* DebugLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9174E2A9EDF57004CE632 /* DebugLogger.swift */; }; + FFF917512A9EDFD2004CE632 /* TestUploadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF917502A9EDFD2004CE632 /* TestUploadManager.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + FF9FB3172A9D61340044E76A /* BackgroundProcessTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BackgroundProcessTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + FF9FB31A2A9D61340044E76A /* BackgroundProcessTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundProcessTestApp.swift; sourceTree = ""; }; + FF9FB31C2A9D61340044E76A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + FF9FB31E2A9D61350044E76A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + FF9FB3212A9D61350044E76A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + FF9FB3282A9D61490044E76A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + FF9FB3292A9DCB460044E76A /* BackgroundProcessSyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BackgroundProcessSyncManager.swift; path = "../../SwiftPackage/Sources/BridgeClientExtension/File Uploading/BackgroundProcessSyncManager.swift"; sourceTree = ""; }; + FFF9174E2A9EDF57004CE632 /* DebugLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLogger.swift; sourceTree = ""; }; + FFF917502A9EDFD2004CE632 /* TestUploadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUploadManager.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FF9FB3142A9D61340044E76A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FF9FB30E2A9D61340044E76A = { + isa = PBXGroup; + children = ( + FF9FB3192A9D61340044E76A /* BackgroundProcessTest */, + FF9FB3182A9D61340044E76A /* Products */, + ); + sourceTree = ""; + }; + FF9FB3182A9D61340044E76A /* Products */ = { + isa = PBXGroup; + children = ( + FF9FB3172A9D61340044E76A /* BackgroundProcessTest.app */, + ); + name = Products; + sourceTree = ""; + }; + FF9FB3192A9D61340044E76A /* BackgroundProcessTest */ = { + isa = PBXGroup; + children = ( + FF9FB3282A9D61490044E76A /* Info.plist */, + FF9FB31A2A9D61340044E76A /* BackgroundProcessTestApp.swift */, + FF9FB31C2A9D61340044E76A /* ContentView.swift */, + FFF9174E2A9EDF57004CE632 /* DebugLogger.swift */, + FFF917502A9EDFD2004CE632 /* TestUploadManager.swift */, + FF9FB3292A9DCB460044E76A /* BackgroundProcessSyncManager.swift */, + FF9FB31E2A9D61350044E76A /* Assets.xcassets */, + FF9FB3202A9D61350044E76A /* Preview Content */, + ); + path = BackgroundProcessTest; + sourceTree = ""; + }; + FF9FB3202A9D61350044E76A /* Preview Content */ = { + isa = PBXGroup; + children = ( + FF9FB3212A9D61350044E76A /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + FF9FB3162A9D61340044E76A /* BackgroundProcessTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = FF9FB3252A9D61350044E76A /* Build configuration list for PBXNativeTarget "BackgroundProcessTest" */; + buildPhases = ( + FF9FB3132A9D61340044E76A /* Sources */, + FF9FB3142A9D61340044E76A /* Frameworks */, + FF9FB3152A9D61340044E76A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BackgroundProcessTest; + productName = BackgroundProcessTest; + productReference = FF9FB3172A9D61340044E76A /* BackgroundProcessTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FF9FB30F2A9D61340044E76A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + FF9FB3162A9D61340044E76A = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = FF9FB3122A9D61340044E76A /* Build configuration list for PBXProject "BackgroundProcessTest" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = FF9FB30E2A9D61340044E76A; + productRefGroup = FF9FB3182A9D61340044E76A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FF9FB3162A9D61340044E76A /* BackgroundProcessTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + FF9FB3152A9D61340044E76A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF9FB3222A9D61350044E76A /* Preview Assets.xcassets in Resources */, + FF9FB31F2A9D61350044E76A /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + FF9FB3132A9D61340044E76A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF9FB31D2A9D61340044E76A /* ContentView.swift in Sources */, + FF9FB32A2A9DCB460044E76A /* BackgroundProcessSyncManager.swift in Sources */, + FFF917512A9EDFD2004CE632 /* TestUploadManager.swift in Sources */, + FF9FB31B2A9D61340044E76A /* BackgroundProcessTestApp.swift in Sources */, + FFF9174F2A9EDF57004CE632 /* DebugLogger.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + FF9FB3232A9D61350044E76A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + FF9FB3242A9D61350044E76A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + FF9FB3262A9D61350044E76A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"BackgroundProcessTest/Preview Content\""; + DEVELOPMENT_TEAM = KA9Z8R6M6K; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BackgroundProcessTest/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.sagebase.BackgroundProcessTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + FF9FB3272A9D61350044E76A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"BackgroundProcessTest/Preview Content\""; + DEVELOPMENT_TEAM = KA9Z8R6M6K; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BackgroundProcessTest/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.sagebase.BackgroundProcessTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FF9FB3122A9D61340044E76A /* Build configuration list for PBXProject "BackgroundProcessTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF9FB3232A9D61350044E76A /* Debug */, + FF9FB3242A9D61350044E76A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FF9FB3252A9D61350044E76A /* Build configuration list for PBXNativeTarget "BackgroundProcessTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF9FB3262A9D61350044E76A /* Debug */, + FF9FB3272A9D61350044E76A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FF9FB30F2A9D61340044E76A /* Project object */; +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AccentColor.colorset/Contents.json b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..13613e3ee --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/Contents.json b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/BackgroundProcessTestApp.swift b/iosBackgroundProcessTest/BackgroundProcessTest/BackgroundProcessTestApp.swift new file mode 100644 index 000000000..35841f683 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/BackgroundProcessTestApp.swift @@ -0,0 +1,30 @@ +// Created 8/28/23 +// swift-tools-version:5.0 + +import SwiftUI + + +let bgTaskId = "org.sagebase.BackgroundProcessTest" + +class AppDelegate : UIResponder, UIApplicationDelegate { + + let manager = TestUploadManager() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + + manager.onLaunch(backgroundProcessId: bgTaskId) + + return true + } +} + +@main +struct BackgroundProcessTestApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/ContentView.swift b/iosBackgroundProcessTest/BackgroundProcessTest/ContentView.swift new file mode 100644 index 000000000..7571c5699 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/ContentView.swift @@ -0,0 +1,24 @@ +// Created 8/28/23 +// swift-tools-version:5.0 + +import SwiftUI +import BackgroundTasks + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundColor(.accentColor) + Text("Hello, world!") + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} + diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/DebugLogger.swift b/iosBackgroundProcessTest/BackgroundProcessTest/DebugLogger.swift new file mode 100644 index 000000000..f97722331 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/DebugLogger.swift @@ -0,0 +1,57 @@ +// Created 8/29/23 +// swift-tools-version:5.0 + +import Foundation + +/// Fake logger that is used to test background processing +final class Logger { + + static func log(severity: LogSeverity, tag: LoggingTag = .bridgeClientUI, message: String, metadata: [String : Any]? = nil) { + logToConsole(severity: severity, tag: tag, message: message, error: nil) + } + + static func log(tag: LoggingTag = .bridgeClientUI, error: Error, message: String? = nil, metadata: [String : Any]? = nil) { + logToConsole(severity: .error, tag: tag, message: message, error: error) + } + + private static func logToConsole(severity: LogSeverity, tag: LoggingTag, message: String?, error: Error?) { + print("[\(tag.rawValue):\(Date())] \(severity.name.uppercased()): \(message ?? "")") + if let error = error { + debugPrint(error) + } + } +} + +struct LoggingTag : RawRepresentable, Hashable, ExpressibleByStringLiteral { + + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public init(stringLiteral value: StringLiteralType) { + self.rawValue = value + } + + public static let upload: LoggingTag = "Upload" + public static let bridgeClientUI: LoggingTag = "BridgeClientUI" +} + +enum LogSeverity : String, CaseIterable { + case debug, info, warn, error + + var name: String { + self.rawValue.uppercased() + } + + var ordinal : Int { + Self.allCases.firstIndex(of: self)! + } +} + +extension LogSeverity : Comparable { + public static func < (lhs: LogSeverity, rhs: LogSeverity) -> Bool { + lhs.ordinal < rhs.ordinal + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/Info.plist b/iosBackgroundProcessTest/BackgroundProcessTest/Info.plist new file mode 100644 index 000000000..fd439cde6 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/Info.plist @@ -0,0 +1,15 @@ + + + + + BGTaskSchedulerPermittedIdentifiers + + org.sagebase.BackgroundProcessTest + + UIBackgroundModes + + fetch + processing + + + diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/Preview Content/Preview Assets.xcassets/Contents.json b/iosBackgroundProcessTest/BackgroundProcessTest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosBackgroundProcessTest/BackgroundProcessTest/TestUploadManager.swift b/iosBackgroundProcessTest/BackgroundProcessTest/TestUploadManager.swift new file mode 100644 index 000000000..e4027fab7 --- /dev/null +++ b/iosBackgroundProcessTest/BackgroundProcessTest/TestUploadManager.swift @@ -0,0 +1,61 @@ +// Created 8/29/23 +// swift-tools-version:5.0 + +import Foundation + +/// Fake upload manager that is used to test background processing +class TestUploadManager : NSObject, BackgroundProcessSyncDelegate { + + lazy var syncManager: BackgroundProcessSyncManager = .init(delegate: self) + + let subdir = "UrbanDictionary" + lazy var uploadDirURL: URL? = { + do { + let baseURL = try FileManager.default.sharedAppSupportDirectory() + let url: URL + if #available(iOS 16.0, *) { + url = baseURL.appending(component: subdir, directoryHint: .isDirectory) + } else { + url = baseURL.appendingPathComponent(subdir) + } + try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) + return url + } catch { + Logger.log(tag: .upload, error: error, message: "Error trying to create the uploads directory.") + return nil + } + }() + + @MainActor + func onLaunch(backgroundProcessId: String?) { + syncManager.onLaunch(backgroundProcessId: backgroundProcessId) + } + + var shouldScheduleBackgroundProcess: Bool { + true + } + + func retryBackgroundProcess(isBackground: Bool) async throws { + let dir = uploadDirURL! + for ii in 0..<10 { + print("retry: START \(ii)") + let url = testURL() + let (data, _) = try await URLSession.shared.data(from: url) + let fileURL = dir.appendingPathComponent("\(UUID()).json") + try data.write(to: fileURL) + try await Task.sleep(nanoseconds: 5 * 1_000_000_000) + print("retry: END \(ii)") + } + } + + func testURL() -> URL { + URL(string: "https://www.urbandictionary.com/random.php")! + } +} + +extension FileManager { + func sharedAppSupportDirectory() throws -> URL { + try self.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + } +} +