--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 185C1B0B2B21547C00695075 /* MqttFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185C1B0A2B21547C00695075 /* MqttFileManager.swift */; };
+ 185C1B0D2B21549600695075 /* Data_category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185C1B0C2B21549600695075 /* Data_category.swift */; };
+ 18BF1E482B83095500B8D90F /* CentralManagerRestoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E252B83095500B8D90F /* CentralManagerRestoredState.swift */; };
+ 18BF1E492B83095500B8D90F /* BluetoothState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E262B83095500B8D90F /* BluetoothState.swift */; };
+ 18BF1E4A2B83095500B8D90F /* Peripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E272B83095500B8D90F /* Peripheral.swift */; };
+ 18BF1E4B2B83095500B8D90F /* PeripheralManager+RestoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E282B83095500B8D90F /* PeripheralManager+RestoredState.swift */; };
+ 18BF1E4C2B83095500B8D90F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E292B83095500B8D90F /* Logging.swift */; };
+ 18BF1E4D2B83095500B8D90F /* CBPeripheralDelegateWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2A2B83095500B8D90F /* CBPeripheralDelegateWrapper.swift */; };
+ 18BF1E4E2B83095500B8D90F /* CBPeripheralManagerDelegateWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2B2B83095500B8D90F /* CBPeripheralManagerDelegateWrapper.swift */; };
+ 18BF1E4F2B83095500B8D90F /* CBPeripheral+Uuid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2C2B83095500B8D90F /* CBPeripheral+Uuid.swift */; };
+ 18BF1E502B83095500B8D90F /* DeviceIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2D2B83095500B8D90F /* DeviceIdentifiers.swift */; };
+ 18BF1E512B83095500B8D90F /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2E2B83095500B8D90F /* Service.swift */; };
+ 18BF1E522B83095500B8D90F /* CBCentral+Uuid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E2F2B83095500B8D90F /* CBCentral+Uuid.swift */; };
+ 18BF1E532B83095500B8D90F /* BluetoothError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E302B83095500B8D90F /* BluetoothError.swift */; };
+ 18BF1E542B83095500B8D90F /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E312B83095500B8D90F /* Logger.swift */; };
+ 18BF1E552B83095500B8D90F /* Peripheral+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E322B83095500B8D90F /* Peripheral+Convenience.swift */; };
+ 18BF1E562B83095500B8D90F /* AdvertisementData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E332B83095500B8D90F /* AdvertisementData.swift */; };
+ 18BF1E572B83095500B8D90F /* PeripheralManagerRestoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E342B83095500B8D90F /* PeripheralManagerRestoredState.swift */; };
+ 18BF1E582B83095500B8D90F /* RxBluetoothKitLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E352B83095500B8D90F /* RxBluetoothKitLogger.swift */; };
+ 18BF1E592B83095500B8D90F /* PeripheralProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E362B83095500B8D90F /* PeripheralProvider.swift */; };
+ 18BF1E5A2B83095500B8D90F /* CBCentralManagerDelegateWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E372B83095500B8D90F /* CBCentralManagerDelegateWrapper.swift */; };
+ 18BF1E5B2B83095500B8D90F /* RestoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E382B83095500B8D90F /* RestoredState.swift */; };
+ 18BF1E5C2B83095500B8D90F /* Descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E392B83095500B8D90F /* Descriptor.swift */; };
+ 18BF1E5D2B83095500B8D90F /* CharacteristicNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3A2B83095500B8D90F /* CharacteristicNotificationManager.swift */; };
+ 18BF1E5E2B83095500B8D90F /* CentralManager+RestoredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3B2B83095500B8D90F /* CentralManager+RestoredState.swift */; };
+ 18BF1E5F2B83095500B8D90F /* Boxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3C2B83095500B8D90F /* Boxes.swift */; };
+ 18BF1E602B83095500B8D90F /* CentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3D2B83095500B8D90F /* CentralManager.swift */; };
+ 18BF1E612B83095500B8D90F /* Observable+Absorb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3E2B83095500B8D90F /* Observable+Absorb.swift */; };
+ 18BF1E622B83095500B8D90F /* Array+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E3F2B83095500B8D90F /* Array+Utils.swift */; };
+ 18BF1E632B83095500B8D90F /* UUIDIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E402B83095500B8D90F /* UUIDIdentifiable.swift */; };
+ 18BF1E642B83095500B8D90F /* Connector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E412B83095500B8D90F /* Connector.swift */; };
+ 18BF1E652B83095500B8D90F /* ScannedPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E422B83095500B8D90F /* ScannedPeripheral.swift */; };
+ 18BF1E662B83095500B8D90F /* ManagerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E432B83095500B8D90F /* ManagerType.swift */; };
+ 18BF1E672B83095500B8D90F /* PeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E442B83095500B8D90F /* PeripheralManager.swift */; };
+ 18BF1E682B83095500B8D90F /* StartAdvertisingResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E452B83095500B8D90F /* StartAdvertisingResult.swift */; };
+ 18BF1E692B83095500B8D90F /* Characteristic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E462B83095500B8D90F /* Characteristic.swift */; };
+ 18BF1E6A2B83095500B8D90F /* Unimplemented.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E472B83095500B8D90F /* Unimplemented.swift */; };
+ 18DE22752B21A5490096A835 /* MqttHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 18DE22732B21A5490096A835 /* MqttHelper.h */; };
+ 18DE22762B21A5490096A835 /* MqttHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18DE22742B21A5490096A835 /* MqttHelper.mm */; };
+ F21215192A85EC2800BD4BEF /* AliIotConnectKit.h in Headers */ = {isa = PBXBuildFile; fileRef = F21215182A85EC2800BD4BEF /* AliIotConnectKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F2F8D5F02A85EF58009C4D21 /* srca.cer in Resources */ = {isa = PBXBuildFile; fileRef = F2F8D4642A85EF56009C4D21 /* srca.cer */; };
+ F2F8D5F12A85EF58009C4D21 /* FGSCheckService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4662A85EF56009C4D21 /* FGSCheckService_C.swift */; };
+ F2F8D5F22A85EF58009C4D21 /* BleService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4672A85EF56009C4D21 /* BleService_C.swift */; };
+ F2F8D5F32A85EF58009C4D21 /* AliCloudIotAutomaticService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4682A85EF56009C4D21 /* AliCloudIotAutomaticService_C.swift */; };
+ F2F8D5F42A85EF58009C4D21 /* BleBaseSendService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4692A85EF56009C4D21 /* BleBaseSendService_C.swift */; };
+ F2F8D5F52A85EF58009C4D21 /* IotService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D46A2A85EF56009C4D21 /* IotService_C.swift */; };
+ F2F8D5F62A85EF58009C4D21 /* RideYardService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D46B2A85EF56009C4D21 /* RideYardService_C.swift */; };
+ F2F8D5F72A85EF58009C4D21 /* ID2Service_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D46C2A85EF56009C4D21 /* ID2Service_C.swift */; };
+ F2F8D5F82A85EF58009C4D21 /* LPConnectService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D46D2A85EF56009C4D21 /* LPConnectService_C.swift */; };
+ F2F8D5FB2A85EF58009C4D21 /* AliCloudIotWatchManager_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4712A85EF56009C4D21 /* AliCloudIotWatchManager_C.swift */; };
+ F2F8D5FC2A85EF58009C4D21 /* MqttManager_C.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4732A85EF56009C4D21 /* MqttManager_C.m */; };
+ F2F8D5FD2A85EF58009C4D21 /* AFHttpHelper_C.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D4742A85EF56009C4D21 /* AFHttpHelper_C.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F2F8D5FE2A85EF58009C4D21 /* AFHttpHelper_C.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4752A85EF56009C4D21 /* AFHttpHelper_C.m */; };
+ F2F8D5FF2A85EF58009C4D21 /* MqttManager_C.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D4762A85EF56009C4D21 /* MqttManager_C.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F2F8D6002A85EF58009C4D21 /* AESEncode_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4782A85EF56009C4D21 /* AESEncode_C.swift */; };
+ F2F8D6012A85EF58009C4D21 /* HMACTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4792A85EF56009C4D21 /* HMACTool.swift */; };
+ F2F8D6022A85EF58009C4D21 /* CommonConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D47A2A85EF56009C4D21 /* CommonConfig.swift */; };
+ F2F8D6032A85EF58009C4D21 /* IDGenerater_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D47B2A85EF56009C4D21 /* IDGenerater_C.swift */; };
+ F2F8D6042A85EF58009C4D21 /* MyExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D47C2A85EF56009C4D21 /* MyExtension.swift */; };
+ F2F8D6052A85EF58009C4D21 /* IotMqttManager_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D47F2A85EF56009C4D21 /* IotMqttManager_C.swift */; };
+ F2F8D6062A85EF58009C4D21 /* IotMqttLegacyConfig_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4802A85EF56009C4D21 /* IotMqttLegacyConfig_C.swift */; };
+ F2F8D6072A85EF58009C4D21 /* IotStateDelatege_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4812A85EF56009C4D21 /* IotStateDelatege_C.swift */; };
+ F2F8D6082A85EF58009C4D21 /* SdkMqttDelegate_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4832A85EF56009C4D21 /* SdkMqttDelegate_C.swift */; };
+ F2F8D6092A85EF58009C4D21 /* SdkMqttService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4842A85EF56009C4D21 /* SdkMqttService_C.swift */; };
+ F2F8D60A2A85EF58009C4D21 /* HttpTool_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4862A85EF56009C4D21 /* HttpTool_C.swift */; };
+ F2F8D60B2A85EF58009C4D21 /* ThingCommunicateService_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4882A85EF56009C4D21 /* ThingCommunicateService_C.swift */; };
+ F2F8D60C2A85EF58009C4D21 /* WearCommonEvent_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48A2A85EF56009C4D21 /* WearCommonEvent_C.swift */; };
+ F2F8D60D2A85EF58009C4D21 /* EventIdentifier_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48B2A85EF56009C4D21 /* EventIdentifier_C.swift */; };
+ F2F8D60E2A85EF58009C4D21 /* ReplyPack_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48C2A85EF56009C4D21 /* ReplyPack_C.swift */; };
+ F2F8D60F2A85EF58009C4D21 /* ThingPack_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48D2A85EF56009C4D21 /* ThingPack_C.swift */; };
+ F2F8D6102A85EF58009C4D21 /* CommandIdentifier_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48E2A85EF56009C4D21 /* CommandIdentifier_C.swift */; };
+ F2F8D6112A85EF58009C4D21 /* WearCommonCommand_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D48F2A85EF56009C4D21 /* WearCommonCommand_C.swift */; };
+ F2F8D6122A85EF58009C4D21 /* FGSMsgType_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4902A85EF56009C4D21 /* FGSMsgType_C.swift */; };
+ F2F8D6132A85EF58009C4D21 /* ThingType_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4912A85EF56009C4D21 /* ThingType_C.swift */; };
+ F2F8D6142A85EF58009C4D21 /* BleNeedSendDataDelegate_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4932A85EF56009C4D21 /* BleNeedSendDataDelegate_C.swift */; };
+ F2F8D6152A85EF58009C4D21 /* BleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4942A85EF56009C4D21 /* BleExtension.swift */; };
+ F2F8D6162A85EF58009C4D21 /* BleManager_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4952A85EF56009C4D21 /* BleManager_C.swift */; };
+ F2F8D6172A85EF58009C4D21 /* DeviceServiceCharacteristic.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4962A85EF56009C4D21 /* DeviceServiceCharacteristic.swift */; };
+ F2F8D6182A85EF58009C4D21 /* BleDeviceModel_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4972A85EF56009C4D21 /* BleDeviceModel_C.swift */; };
+ F2F8D6192A85EF58009C4D21 /* BleSIdentifier_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4982A85EF56009C4D21 /* BleSIdentifier_C.swift */; };
+ F2F8D61A2A85EF58009C4D21 /* BleAuthModel_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4992A85EF56009C4D21 /* BleAuthModel_C.swift */; };
+ F2F8D61B2A85EF58009C4D21 /* BleTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D49A2A85EF56009C4D21 /* BleTypes.swift */; };
+ F2F8D61C2A85EF58009C4D21 /* COOKSwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D49D2A85EF57009C4D21 /* COOKSwiftyJSON.swift */; };
+ F2F8D61F2A85EF58009C4D21 /* AsMaybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A22A85EF57009C4D21 /* AsMaybe.swift */; };
+ F2F8D6202A85EF58009C4D21 /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A32A85EF57009C4D21 /* SubjectType.swift */; };
+ F2F8D6212A85EF58009C4D21 /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A42A85EF57009C4D21 /* DisposeBase.swift */; };
+ F2F8D6222A85EF58009C4D21 /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A52A85EF57009C4D21 /* Cancelable.swift */; };
+ F2F8D6232A85EF58009C4D21 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A62A85EF57009C4D21 /* ScheduledItem.swift */; };
+ F2F8D6242A85EF58009C4D21 /* GroupedObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A72A85EF57009C4D21 /* GroupedObservable.swift */; };
+ F2F8D6252A85EF58009C4D21 /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A82A85EF57009C4D21 /* Timeout.swift */; };
+ F2F8D6262A85EF58009C4D21 /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4A92A85EF57009C4D21 /* RecursiveScheduler.swift */; };
+ F2F8D6272A85EF58009C4D21 /* Never.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AA2A85EF57009C4D21 /* Never.swift */; };
+ F2F8D6282A85EF58009C4D21 /* Completable+AndThen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AB2A85EF57009C4D21 /* Completable+AndThen.swift */; };
+ F2F8D6292A85EF58009C4D21 /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AC2A85EF57009C4D21 /* BehaviorSubject.swift */; };
+ F2F8D62A2A85EF58009C4D21 /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AD2A85EF57009C4D21 /* RetryWhen.swift */; };
+ F2F8D62B2A85EF58009C4D21 /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AE2A85EF57009C4D21 /* Do.swift */; };
+ F2F8D62C2A85EF58009C4D21 /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4AF2A85EF57009C4D21 /* OperationQueueScheduler.swift */; };
+ F2F8D62D2A85EF58009C4D21 /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B02A85EF57009C4D21 /* Deferred.swift */; };
+ F2F8D62E2A85EF58009C4D21 /* Bag+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B12A85EF57009C4D21 /* Bag+Rx.swift */; };
+ F2F8D62F2A85EF58009C4D21 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B22A85EF57009C4D21 /* LockOwnerType.swift */; };
+ F2F8D6302A85EF58009C4D21 /* Platform.Darwin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B32A85EF57009C4D21 /* Platform.Darwin.swift */; };
+ F2F8D6312A85EF58009C4D21 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B42A85EF57009C4D21 /* SynchronizedDisposeType.swift */; };
+ F2F8D6322A85EF58009C4D21 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B52A85EF57009C4D21 /* Buffer.swift */; };
+ F2F8D6332A85EF58009C4D21 /* SubscribeOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B62A85EF57009C4D21 /* SubscribeOn.swift */; };
+ F2F8D6342A85EF58009C4D21 /* VirtualTimeScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B72A85EF57009C4D21 /* VirtualTimeScheduler.swift */; };
+ F2F8D6352A85EF58009C4D21 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B82A85EF57009C4D21 /* SynchronizedOnType.swift */; };
+ F2F8D6362A85EF58009C4D21 /* DefaultIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4B92A85EF57009C4D21 /* DefaultIfEmpty.swift */; };
+ F2F8D6372A85EF58009C4D21 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BA2A85EF57009C4D21 /* Using.swift */; };
+ F2F8D6382A85EF58009C4D21 /* AsyncSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BB2A85EF57009C4D21 /* AsyncSubject.swift */; };
+ F2F8D6392A85EF58009C4D21 /* DispatchQueue+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BC2A85EF57009C4D21 /* DispatchQueue+Extensions.swift */; };
+ F2F8D63A2A85EF58009C4D21 /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BD2A85EF57009C4D21 /* DisposeBag.swift */; };
+ F2F8D63B2A85EF58009C4D21 /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BE2A85EF57009C4D21 /* SchedulerServices+Emulation.swift */; };
+ F2F8D63C2A85EF58009C4D21 /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4BF2A85EF57009C4D21 /* CurrentThreadScheduler.swift */; };
+ F2F8D63D2A85EF58009C4D21 /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C02A85EF57009C4D21 /* Delay.swift */; };
+ F2F8D63E2A85EF58009C4D21 /* AtomicInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C12A85EF57009C4D21 /* AtomicInt.swift */; };
+ F2F8D63F2A85EF58009C4D21 /* Producer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C22A85EF57009C4D21 /* Producer.swift */; };
+ F2F8D6402A85EF58009C4D21 /* GroupBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C32A85EF57009C4D21 /* GroupBy.swift */; };
+ F2F8D6412A85EF58009C4D21 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C42A85EF57009C4D21 /* InvocableType.swift */; };
+ F2F8D6422A85EF58009C4D21 /* RxMutableBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C52A85EF57009C4D21 /* RxMutableBox.swift */; };
+ F2F8D6432A85EF58009C4D21 /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C62A85EF57009C4D21 /* Concat.swift */; };
+ F2F8D6442A85EF58009C4D21 /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C72A85EF57009C4D21 /* ReplaySubject.swift */; };
+ F2F8D6452A85EF58009C4D21 /* ScheduledDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C82A85EF57009C4D21 /* ScheduledDisposable.swift */; };
+ F2F8D6462A85EF58009C4D21 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4C92A85EF57009C4D21 /* Event.swift */; };
+ F2F8D6472A85EF58009C4D21 /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CA2A85EF57009C4D21 /* MainScheduler.swift */; };
+ F2F8D6482A85EF58009C4D21 /* Take.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CB2A85EF57009C4D21 /* Take.swift */; };
+ F2F8D6492A85EF58009C4D21 /* TakeWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CC2A85EF57009C4D21 /* TakeWhile.swift */; };
+ F2F8D64A2A85EF58009C4D21 /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CD2A85EF57009C4D21 /* TakeLast.swift */; };
+ F2F8D64B2A85EF58009C4D21 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CE2A85EF57009C4D21 /* Error.swift */; };
+ F2F8D64C2A85EF58009C4D21 /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4CF2A85EF57009C4D21 /* PublishSubject.swift */; };
+ F2F8D64D2A85EF58009C4D21 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D02A85EF57009C4D21 /* InvocableScheduledItem.swift */; };
+ F2F8D64E2A85EF58009C4D21 /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D12A85EF57009C4D21 /* CombineLatest.swift */; };
+ F2F8D64F2A85EF58009C4D21 /* NopDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D22A85EF57009C4D21 /* NopDisposable.swift */; };
+ F2F8D6502A85EF58009C4D21 /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D32A85EF57009C4D21 /* Sample.swift */; };
+ F2F8D6512A85EF58009C4D21 /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D42A85EF57009C4D21 /* AnyObserver.swift */; };
+ F2F8D6522A85EF58009C4D21 /* AsSingle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D52A85EF57009C4D21 /* AsSingle.swift */; };
+ F2F8D6532A85EF58009C4D21 /* Completable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D62A85EF57009C4D21 /* Completable.swift */; };
+ F2F8D6542A85EF58009C4D21 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D72A85EF57009C4D21 /* Empty.swift */; };
+ F2F8D6552A85EF58009C4D21 /* Enumerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D82A85EF57009C4D21 /* Enumerated.swift */; };
+ F2F8D6562A85EF58009C4D21 /* DistinctUntilChanged.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4D92A85EF57009C4D21 /* DistinctUntilChanged.swift */; };
+ F2F8D6572A85EF58009C4D21 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DA2A85EF57009C4D21 /* SubscriptionDisposable.swift */; };
+ F2F8D6582A85EF58009C4D21 /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DB2A85EF57009C4D21 /* ConcurrentDispatchQueueScheduler.swift */; };
+ F2F8D6592A85EF58009C4D21 /* First.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DC2A85EF57009C4D21 /* First.swift */; };
+ F2F8D65A2A85EF58009C4D21 /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DD2A85EF57009C4D21 /* Lock.swift */; };
+ F2F8D65B2A85EF58009C4D21 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DE2A85EF57009C4D21 /* Timer.swift */; };
+ F2F8D65C2A85EF58009C4D21 /* Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4DF2A85EF57009C4D21 /* Single.swift */; };
+ F2F8D65D2A85EF58009C4D21 /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E02A85EF57009C4D21 /* AddRef.swift */; };
+ F2F8D65E2A85EF58009C4D21 /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E12A85EF57009C4D21 /* Sequence.swift */; };
+ F2F8D65F2A85EF58009C4D21 /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E22A85EF57009C4D21 /* ObserverBase.swift */; };
+ F2F8D6602A85EF58009C4D21 /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E32A85EF57009C4D21 /* Sink.swift */; };
+ F2F8D6612A85EF58009C4D21 /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E42A85EF57009C4D21 /* AsyncLock.swift */; };
+ F2F8D6622A85EF58009C4D21 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E52A85EF57009C4D21 /* Throttle.swift */; };
+ F2F8D6632A85EF58009C4D21 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E62A85EF57009C4D21 /* Range.swift */; };
+ F2F8D6642A85EF58009C4D21 /* CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E72A85EF57009C4D21 /* CompactMap.swift */; };
+ F2F8D6652A85EF58009C4D21 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E82A85EF57009C4D21 /* Disposable.swift */; };
+ F2F8D6662A85EF58009C4D21 /* PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4E92A85EF57009C4D21 /* PrimitiveSequence.swift */; };
+ F2F8D6672A85EF58009C4D21 /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4EA2A85EF57009C4D21 /* RefCountDisposable.swift */; };
+ F2F8D6682A85EF58009C4D21 /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4EB2A85EF57009C4D21 /* WithLatestFrom.swift */; };
+ F2F8D6692A85EF58009C4D21 /* ObserveOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4EC2A85EF57009C4D21 /* ObserveOn.swift */; };
+ F2F8D66A2A85EF58009C4D21 /* CombineLatest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4ED2A85EF57009C4D21 /* CombineLatest+Collection.swift */; };
+ F2F8D66B2A85EF58009C4D21 /* ObservableType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4EE2A85EF57009C4D21 /* ObservableType+Extensions.swift */; };
+ F2F8D66C2A85EF58009C4D21 /* CompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4EF2A85EF57009C4D21 /* CompositeDisposable.swift */; };
+ F2F8D66D2A85EF58009C4D21 /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F02A85EF57009C4D21 /* ConcurrentMainScheduler.swift */; };
+ F2F8D66E2A85EF58009C4D21 /* Multicast.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F12A85EF57009C4D21 /* Multicast.swift */; };
+ F2F8D66F2A85EF58009C4D21 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F22A85EF57009C4D21 /* SkipWhile.swift */; };
+ F2F8D6702A85EF58009C4D21 /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F32A85EF57009C4D21 /* ObserverType.swift */; };
+ F2F8D6712A85EF58009C4D21 /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F42A85EF57009C4D21 /* TailRecursiveSink.swift */; };
+ F2F8D6722A85EF58009C4D21 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F52A85EF57009C4D21 /* SerialDispatchQueueScheduler.swift */; };
+ F2F8D6732A85EF58009C4D21 /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F62A85EF57009C4D21 /* DelaySubscription.swift */; };
+ F2F8D6742A85EF58009C4D21 /* HistoricalScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F72A85EF57009C4D21 /* HistoricalScheduler.swift */; };
+ F2F8D6752A85EF58009C4D21 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F82A85EF57009C4D21 /* Bag.swift */; };
+ F2F8D6762A85EF58009C4D21 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4F92A85EF57009C4D21 /* Repeat.swift */; };
+ F2F8D6772A85EF58009C4D21 /* Platform.Linux.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FA2A85EF57009C4D21 /* Platform.Linux.swift */; };
+ F2F8D6782A85EF58009C4D21 /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FB2A85EF57009C4D21 /* Reactive.swift */; };
+ F2F8D6792A85EF58009C4D21 /* Maybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FC2A85EF57009C4D21 /* Maybe.swift */; };
+ F2F8D67A2A85EF58009C4D21 /* Date+Dispatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FD2A85EF57009C4D21 /* Date+Dispatch.swift */; };
+ F2F8D67B2A85EF58009C4D21 /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FE2A85EF57009C4D21 /* ToArray.swift */; };
+ F2F8D67C2A85EF58009C4D21 /* TakeUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4FF2A85EF57009C4D21 /* TakeUntil.swift */; };
+ F2F8D67D2A85EF58009C4D21 /* DispatchQueueConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5002A85EF57009C4D21 /* DispatchQueueConfiguration.swift */; };
+ F2F8D67E2A85EF58009C4D21 /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5012A85EF57009C4D21 /* Switch.swift */; };
+ F2F8D67F2A85EF58009C4D21 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5022A85EF57009C4D21 /* Window.swift */; };
+ F2F8D6802A85EF58009C4D21 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5032A85EF57009C4D21 /* Debounce.swift */; };
+ F2F8D6812A85EF58009C4D21 /* Dematerialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5042A85EF57009C4D21 /* Dematerialize.swift */; };
+ F2F8D6822A85EF58009C4D21 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5052A85EF57009C4D21 /* SynchronizedUnsubscribeType.swift */; };
+ F2F8D6832A85EF58009C4D21 /* Zip+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5062A85EF57009C4D21 /* Zip+Collection.swift */; };
+ F2F8D6842A85EF58009C4D21 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5072A85EF57009C4D21 /* Deprecated.swift */; };
+ F2F8D6852A85EF58009C4D21 /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5082A85EF57009C4D21 /* Catch.swift */; };
+ F2F8D6862A85EF58009C4D21 /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5092A85EF57009C4D21 /* AnonymousObserver.swift */; };
+ F2F8D6872A85EF58009C4D21 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50A2A85EF57009C4D21 /* SwiftSupport.swift */; };
+ F2F8D6882A85EF58009C4D21 /* PriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50B2A85EF57009C4D21 /* PriorityQueue.swift */; };
+ F2F8D6892A85EF58009C4D21 /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50C2A85EF57009C4D21 /* SingleAssignmentDisposable.swift */; };
+ F2F8D68A2A85EF58009C4D21 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50D2A85EF57009C4D21 /* Queue.swift */; };
+ F2F8D68B2A85EF58009C4D21 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50E2A85EF57009C4D21 /* ObservableConvertibleType.swift */; };
+ F2F8D68C2A85EF58009C4D21 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D50F2A85EF57009C4D21 /* Filter.swift */; };
+ F2F8D68D2A85EF58009C4D21 /* ObservableType+PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5102A85EF57009C4D21 /* ObservableType+PrimitiveSequence.swift */; };
+ F2F8D68E2A85EF58009C4D21 /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5112A85EF57009C4D21 /* Amb.swift */; };
+ F2F8D68F2A85EF58009C4D21 /* Reduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5122A85EF57009C4D21 /* Reduce.swift */; };
+ F2F8D6902A85EF58009C4D21 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5132A85EF57009C4D21 /* VirtualTimeConverterType.swift */; };
+ F2F8D6912A85EF58009C4D21 /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5142A85EF57009C4D21 /* CombineLatest+arity.swift */; };
+ F2F8D6922A85EF58009C4D21 /* HistoricalSchedulerTimeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5152A85EF57009C4D21 /* HistoricalSchedulerTimeConverter.swift */; };
+ F2F8D6932A85EF58009C4D21 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5162A85EF57009C4D21 /* Observable.swift */; };
+ F2F8D6942A85EF58009C4D21 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5172A85EF57009C4D21 /* Zip.swift */; };
+ F2F8D6952A85EF58009C4D21 /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5182A85EF57009C4D21 /* Debug.swift */; };
+ F2F8D6962A85EF58009C4D21 /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5192A85EF57009C4D21 /* SchedulerType.swift */; };
+ F2F8D6972A85EF58009C4D21 /* SerialDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51A2A85EF57009C4D21 /* SerialDisposable.swift */; };
+ F2F8D6982A85EF58009C4D21 /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51B2A85EF57009C4D21 /* ConnectableObservableType.swift */; };
+ F2F8D6992A85EF58009C4D21 /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51C2A85EF57009C4D21 /* Merge.swift */; };
+ F2F8D69A2A85EF58009C4D21 /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51D2A85EF57009C4D21 /* ObservableType.swift */; };
+ F2F8D69B2A85EF58009C4D21 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51E2A85EF57009C4D21 /* Errors.swift */; };
+ F2F8D69C2A85EF58009C4D21 /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D51F2A85EF57009C4D21 /* Rx.swift */; };
+ F2F8D69D2A85EF58009C4D21 /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5202A85EF57009C4D21 /* Skip.swift */; };
+ F2F8D69E2A85EF58009C4D21 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5212A85EF57009C4D21 /* Map.swift */; };
+ F2F8D69F2A85EF58009C4D21 /* BinaryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5222A85EF57009C4D21 /* BinaryDisposable.swift */; };
+ F2F8D6A02A85EF58009C4D21 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5232A85EF57009C4D21 /* SkipUntil.swift */; };
+ F2F8D6A12A85EF58009C4D21 /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5242A85EF57009C4D21 /* InfiniteSequence.swift */; };
+ F2F8D6A22A85EF58009C4D21 /* PrimitiveSequence+Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5252A85EF57009C4D21 /* PrimitiveSequence+Zip+arity.swift */; };
+ F2F8D6A32A85EF58009C4D21 /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5262A85EF57009C4D21 /* AnonymousDisposable.swift */; };
+ F2F8D6A42A85EF58009C4D21 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5272A85EF57009C4D21 /* StartWith.swift */; };
+ F2F8D6A52A85EF58009C4D21 /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5282A85EF57009C4D21 /* Just.swift */; };
+ F2F8D6A62A85EF58009C4D21 /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5292A85EF57009C4D21 /* BooleanDisposable.swift */; };
+ F2F8D6A72A85EF58009C4D21 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52A2A85EF57009C4D21 /* Optional.swift */; };
+ F2F8D6A82A85EF58009C4D21 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52B2A85EF57009C4D21 /* Generate.swift */; };
+ F2F8D6A92A85EF58009C4D21 /* ShareReplayScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52C2A85EF57009C4D21 /* ShareReplayScope.swift */; };
+ F2F8D6AA2A85EF58009C4D21 /* Disposables.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52D2A85EF57009C4D21 /* Disposables.swift */; };
+ F2F8D6AB2A85EF58009C4D21 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52E2A85EF57009C4D21 /* RecursiveLock.swift */; };
+ F2F8D6AC2A85EF58009C4D21 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D52F2A85EF57009C4D21 /* ScheduledItemType.swift */; };
+ F2F8D6AD2A85EF58009C4D21 /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5302A85EF57009C4D21 /* Scan.swift */; };
+ F2F8D6AE2A85EF58009C4D21 /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5312A85EF57009C4D21 /* Zip+arity.swift */; };
+ F2F8D6AF2A85EF58009C4D21 /* SwitchIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5322A85EF57009C4D21 /* SwitchIfEmpty.swift */; };
+ F2F8D6B02A85EF58009C4D21 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5332A85EF57009C4D21 /* ElementAt.swift */; };
+ F2F8D6B12A85EF58009C4D21 /* Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5342A85EF57009C4D21 /* Create.swift */; };
+ F2F8D6B22A85EF58009C4D21 /* Materialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5352A85EF57009C4D21 /* Materialize.swift */; };
+ F2F8D6B32A85EF58009C4D21 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5362A85EF57009C4D21 /* SingleAsync.swift */; };
+ F2F8D6B42A85EF58009C4D21 /* ImmediateSchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5372A85EF57009C4D21 /* ImmediateSchedulerType.swift */; };
+ F2F8D6B52A85EF58009C4D21 /* RSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5392A85EF57009C4D21 /* RSA.swift */; };
+ F2F8D6B62A85EF58009C4D21 /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53A2A85EF57009C4D21 /* AES.swift */; };
+ F2F8D6B72A85EF58009C4D21 /* String+FoundationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53B2A85EF57009C4D21 /* String+FoundationExtension.swift */; };
+ F2F8D6B82A85EF58009C4D21 /* BlockModeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53C2A85EF57009C4D21 /* BlockModeOptions.swift */; };
+ F2F8D6B92A85EF58009C4D21 /* StreamDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53D2A85EF57009C4D21 /* StreamDecryptor.swift */; };
+ F2F8D6BA2A85EF58009C4D21 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53E2A85EF57009C4D21 /* Random.swift */; };
+ F2F8D6BB2A85EF58009C4D21 /* GCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D53F2A85EF57009C4D21 /* GCD.swift */; };
+ F2F8D6BC2A85EF58009C4D21 /* BlockEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5402A85EF57009C4D21 /* BlockEncryptor.swift */; };
+ F2F8D6BD2A85EF58009C4D21 /* OCB.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5412A85EF57009C4D21 /* OCB.swift */; };
+ F2F8D6BE2A85EF58009C4D21 /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5422A85EF57009C4D21 /* Hashable.swift */; };
+ F2F8D6BF2A85EF58009C4D21 /* UInt64+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5432A85EF57009C4D21 /* UInt64+Extension.swift */; };
+ F2F8D6C02A85EF58009C4D21 /* Cryptors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5442A85EF57009C4D21 /* Cryptors.swift */; };
+ F2F8D6C12A85EF58009C4D21 /* HKDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5452A85EF57009C4D21 /* HKDF.swift */; };
+ F2F8D6C22A85EF58009C4D21 /* PKCS7Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5462A85EF57009C4D21 /* PKCS7Padding.swift */; };
+ F2F8D6C32A85EF58009C4D21 /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5472A85EF57009C4D21 /* CFB.swift */; };
+ F2F8D6C42A85EF58009C4D21 /* ISO10126Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5482A85EF57009C4D21 /* ISO10126Padding.swift */; };
+ F2F8D6C52A85EF58009C4D21 /* Strideable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5492A85EF57009C4D21 /* Strideable.swift */; };
+ F2F8D6C62A85EF58009C4D21 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54A2A85EF57009C4D21 /* Cipher.swift */; };
+ F2F8D6C72A85EF58009C4D21 /* Addition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54B2A85EF57009C4D21 /* Addition.swift */; };
+ F2F8D6C82A85EF58009C4D21 /* Data Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54C2A85EF57009C4D21 /* Data Conversion.swift */; };
+ F2F8D6C92A85EF58009C4D21 /* CBCMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54D2A85EF57009C4D21 /* CBCMAC.swift */; };
+ F2F8D6CA2A85EF58009C4D21 /* Int+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54E2A85EF57009C4D21 /* Int+Extension.swift */; };
+ F2F8D6CB2A85EF58009C4D21 /* AES.Cryptors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D54F2A85EF57009C4D21 /* AES.Cryptors.swift */; };
+ F2F8D6CC2A85EF58009C4D21 /* Bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5502A85EF57009C4D21 /* Bit.swift */; };
+ F2F8D6CD2A85EF58009C4D21 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5512A85EF57009C4D21 /* HMAC.swift */; };
+ F2F8D6CE2A85EF58009C4D21 /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5522A85EF57009C4D21 /* CBC.swift */; };
+ F2F8D6CF2A85EF58009C4D21 /* Bitwise Ops.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5532A85EF57009C4D21 /* Bitwise Ops.swift */; };
+ F2F8D6D02A85EF58009C4D21 /* CMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5542A85EF57009C4D21 /* CMAC.swift */; };
+ F2F8D6D12A85EF58009C4D21 /* UInt16+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5552A85EF57009C4D21 /* UInt16+Extension.swift */; };
+ F2F8D6D22A85EF58009C4D21 /* Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5562A85EF57009C4D21 /* Comparable.swift */; };
+ F2F8D6D32A85EF58009C4D21 /* Collection+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5572A85EF57009C4D21 /* Collection+Extension.swift */; };
+ F2F8D6D42A85EF58009C4D21 /* UInt32+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5582A85EF57009C4D21 /* UInt32+Extension.swift */; };
+ F2F8D6D52A85EF58009C4D21 /* Digest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5592A85EF57009C4D21 /* Digest.swift */; };
+ F2F8D6D62A85EF58009C4D21 /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55A2A85EF57009C4D21 /* UInt128.swift */; };
+ F2F8D6D72A85EF58009C4D21 /* RSA+Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55B2A85EF57009C4D21 /* RSA+Signature.swift */; };
+ F2F8D6D82A85EF58009C4D21 /* ECB.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55C2A85EF57009C4D21 /* ECB.swift */; };
+ F2F8D6D92A85EF58009C4D21 /* Words and Bits.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55D2A85EF57009C4D21 /* Words and Bits.swift */; };
+ F2F8D6DA2A85EF58009C4D21 /* CCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55E2A85EF57009C4D21 /* CCM.swift */; };
+ F2F8D6DB2A85EF58009C4D21 /* Scrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D55F2A85EF57009C4D21 /* Scrypt.swift */; };
+ F2F8D6DC2A85EF58009C4D21 /* PCBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5602A85EF57009C4D21 /* PCBC.swift */; };
+ F2F8D6DD2A85EF58009C4D21 /* NoPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5612A85EF57009C4D21 /* NoPadding.swift */; };
+ F2F8D6DE2A85EF58009C4D21 /* UInt8+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5622A85EF57009C4D21 /* UInt8+Extension.swift */; };
+ F2F8D6DF2A85EF58009C4D21 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5632A85EF57009C4D21 /* MD5.swift */; };
+ F2F8D6E02A85EF58009C4D21 /* ASN1Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5642A85EF57009C4D21 /* ASN1Scanner.swift */; };
+ F2F8D6E12A85EF58009C4D21 /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5652A85EF57009C4D21 /* Cryptor.swift */; };
+ F2F8D6E22A85EF58009C4D21 /* Square Root.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5662A85EF57009C4D21 /* Square Root.swift */; };
+ F2F8D6E32A85EF58009C4D21 /* BatchedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5672A85EF57009C4D21 /* BatchedCollection.swift */; };
+ F2F8D6E42A85EF58009C4D21 /* Blowfish+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5682A85EF57009C4D21 /* Blowfish+Foundation.swift */; };
+ F2F8D6E52A85EF58009C4D21 /* CSCompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5692A85EF57009C4D21 /* CSCompactMap.swift */; };
+ F2F8D6E62A85EF58009C4D21 /* ISO78164Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56A2A85EF57009C4D21 /* ISO78164Padding.swift */; };
+ F2F8D6E72A85EF58009C4D21 /* SecureBytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56B2A85EF57009C4D21 /* SecureBytes.swift */; };
+ F2F8D6E82A85EF58009C4D21 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56C2A85EF57009C4D21 /* Signature.swift */; };
+ F2F8D6E92A85EF58009C4D21 /* ZeroPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56D2A85EF57009C4D21 /* ZeroPadding.swift */; };
+ F2F8D6EA2A85EF58009C4D21 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56E2A85EF57009C4D21 /* String+Extension.swift */; };
+ F2F8D6EB2A85EF58009C4D21 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D56F2A85EF57009C4D21 /* Authenticator.swift */; };
+ F2F8D6EC2A85EF58009C4D21 /* SHA3.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5702A85EF57009C4D21 /* SHA3.swift */; };
+ F2F8D6ED2A85EF58009C4D21 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5712A85EF57009C4D21 /* Operators.swift */; };
+ F2F8D6EE2A85EF58009C4D21 /* Division.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5722A85EF57009C4D21 /* Division.swift */; };
+ F2F8D6EF2A85EF58009C4D21 /* Utils+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5732A85EF57009C4D21 /* Utils+Foundation.swift */; };
+ F2F8D6F02A85EF58009C4D21 /* PKCS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5742A85EF57009C4D21 /* PKCS5.swift */; };
+ F2F8D6F12A85EF58009C4D21 /* Prime Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5752A85EF57009C4D21 /* Prime Test.swift */; };
+ F2F8D6F22A85EF58009C4D21 /* Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5762A85EF57009C4D21 /* Codable.swift */; };
+ F2F8D6F32A85EF58009C4D21 /* BlockMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5772A85EF57009C4D21 /* BlockMode.swift */; };
+ F2F8D6F42A85EF58009C4D21 /* BigUInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5782A85EF57009C4D21 /* BigUInt.swift */; };
+ F2F8D6F52A85EF58009C4D21 /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5792A85EF57009C4D21 /* Rabbit.swift */; };
+ F2F8D6F62A85EF58009C4D21 /* PKCS7.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57A2A85EF57009C4D21 /* PKCS7.swift */; };
+ F2F8D6F72A85EF58009C4D21 /* SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57B2A85EF57009C4D21 /* SHA1.swift */; };
+ F2F8D6F82A85EF58009C4D21 /* Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57C2A85EF57009C4D21 /* Poly1305.swift */; };
+ F2F8D6F92A85EF58009C4D21 /* BlockCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57D2A85EF57009C4D21 /* BlockCipher.swift */; };
+ F2F8D6FA2A85EF58009C4D21 /* GCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57E2A85EF57009C4D21 /* GCM.swift */; };
+ F2F8D6FB2A85EF58009C4D21 /* PKCS1v15.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D57F2A85EF57009C4D21 /* PKCS1v15.swift */; };
+ F2F8D6FC2A85EF58009C4D21 /* Updatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5802A85EF57009C4D21 /* Updatable.swift */; };
+ F2F8D6FD2A85EF58009C4D21 /* DER.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5812A85EF57009C4D21 /* DER.swift */; };
+ F2F8D6FE2A85EF58009C4D21 /* ASN1Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5822A85EF57009C4D21 /* ASN1Encoder.swift */; };
+ F2F8D6FF2A85EF58009C4D21 /* PBKDF2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5832A85EF57009C4D21 /* PBKDF2.swift */; };
+ F2F8D7002A85EF58009C4D21 /* Blowfish.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5842A85EF57009C4D21 /* Blowfish.swift */; };
+ F2F8D7012A85EF58009C4D21 /* BlockDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5852A85EF57009C4D21 /* BlockDecryptor.swift */; };
+ F2F8D7022A85EF58009C4D21 /* HMAC+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5862A85EF57009C4D21 /* HMAC+Foundation.swift */; };
+ F2F8D7032A85EF58009C4D21 /* StreamEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5872A85EF57009C4D21 /* StreamEncryptor.swift */; };
+ F2F8D7042A85EF58009C4D21 /* Multiplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5882A85EF57009C4D21 /* Multiplication.swift */; };
+ F2F8D7052A85EF58009C4D21 /* CS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5892A85EF57009C4D21 /* CS.swift */; };
+ F2F8D7062A85EF58009C4D21 /* CipherModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58A2A85EF57009C4D21 /* CipherModeWorker.swift */; };
+ F2F8D7072A85EF58009C4D21 /* Data+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58B2A85EF57009C4D21 /* Data+Extension.swift */; };
+ F2F8D7082A85EF58009C4D21 /* DigestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58C2A85EF57009C4D21 /* DigestType.swift */; };
+ F2F8D7092A85EF58009C4D21 /* ChaCha20+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58D2A85EF57009C4D21 /* ChaCha20+Foundation.swift */; };
+ F2F8D70A2A85EF58009C4D21 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58E2A85EF57009C4D21 /* Utils.swift */; };
+ F2F8D70B2A85EF58009C4D21 /* Exponentiation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D58F2A85EF57009C4D21 /* Exponentiation.swift */; };
+ F2F8D70C2A85EF58009C4D21 /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5902A85EF57009C4D21 /* CTR.swift */; };
+ F2F8D70D2A85EF58009C4D21 /* AEADChaCha20Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5912A85EF57009C4D21 /* AEADChaCha20Poly1305.swift */; };
+ F2F8D70E2A85EF58009C4D21 /* Generics.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5922A85EF57009C4D21 /* Generics.swift */; };
+ F2F8D70F2A85EF58009C4D21 /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5932A85EF57009C4D21 /* Checksum.swift */; };
+ F2F8D7102A85EF58009C4D21 /* Floating Point Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5942A85EF57009C4D21 /* Floating Point Conversion.swift */; };
+ F2F8D7112A85EF58009C4D21 /* Shifts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5952A85EF57009C4D21 /* Shifts.swift */; };
+ F2F8D7122A85EF58009C4D21 /* SHA2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5962A85EF57009C4D21 /* SHA2.swift */; };
+ F2F8D7132A85EF58009C4D21 /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5972A85EF57009C4D21 /* Padding.swift */; };
+ F2F8D7142A85EF58009C4D21 /* AEAD.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5982A85EF57009C4D21 /* AEAD.swift */; };
+ F2F8D7152A85EF58009C4D21 /* ASN1Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5992A85EF57009C4D21 /* ASN1Decoder.swift */; };
+ F2F8D7162A85EF58009C4D21 /* Array+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59A2A85EF57009C4D21 /* Array+Foundation.swift */; };
+ F2F8D7172A85EF58009C4D21 /* PBKDF1.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59B2A85EF57009C4D21 /* PBKDF1.swift */; };
+ F2F8D7182A85EF58009C4D21 /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59C2A85EF57009C4D21 /* ChaCha20.swift */; };
+ F2F8D7192A85EF58009C4D21 /* Subtraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59D2A85EF57009C4D21 /* Subtraction.swift */; };
+ F2F8D71A2A85EF58009C4D21 /* Integer Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59E2A85EF57009C4D21 /* Integer Conversion.swift */; };
+ F2F8D71B2A85EF58009C4D21 /* BigInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D59F2A85EF57009C4D21 /* BigInt.swift */; };
+ F2F8D71C2A85EF58009C4D21 /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A02A85EF57009C4D21 /* ASN1.swift */; };
+ F2F8D71D2A85EF58009C4D21 /* AES+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A12A85EF57009C4D21 /* AES+Foundation.swift */; };
+ F2F8D71E2A85EF58009C4D21 /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A22A85EF57009C4D21 /* Rabbit+Foundation.swift */; };
+ F2F8D71F2A85EF58009C4D21 /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A32A85EF57009C4D21 /* OFB.swift */; };
+ F2F8D7202A85EF58009C4D21 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A42A85EF57009C4D21 /* Array+Extension.swift */; };
+ F2F8D7212A85EF58009C4D21 /* String Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A52A85EF57009C4D21 /* String Conversion.swift */; };
+ F2F8D7222A85EF58009C4D21 /* RSA+Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A62A85EF57009C4D21 /* RSA+Cipher.swift */; };
+ F2F8D7232A85EF58009C4D21 /* SBFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A82A85EF57009C4D21 /* SBFilter.swift */; };
+ F2F8D7242A85EF58009C4D21 /* FilterValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5A92A85EF57009C4D21 /* FilterValidator.swift */; };
+ F2F8D7252A85EF58009C4D21 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AA2A85EF57009C4D21 /* Extensions.swift */; };
+ F2F8D7262A85EF58009C4D21 /* FileDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AB2A85EF57009C4D21 /* FileDestination.swift */; };
+ F2F8D7272A85EF58009C4D21 /* GoogleCloudDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AC2A85EF57009C4D21 /* GoogleCloudDestination.swift */; };
+ F2F8D7282A85EF58009C4D21 /* Base64.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AD2A85EF57009C4D21 /* Base64.swift */; };
+ F2F8D7292A85EF58009C4D21 /* SBPlatformDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AE2A85EF57009C4D21 /* SBPlatformDestination.swift */; };
+ F2F8D72A2A85EF58009C4D21 /* SwiftyBeaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5AF2A85EF57009C4D21 /* SwiftyBeaver.swift */; };
+ F2F8D72B2A85EF58009C4D21 /* BaseDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5B02A85EF57009C4D21 /* BaseDestination.swift */; };
+ F2F8D72C2A85EF58009C4D21 /* AES256CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5B12A85EF57009C4D21 /* AES256CBC.swift */; };
+ F2F8D72D2A85EF58009C4D21 /* ConsoleDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5B22A85EF57009C4D21 /* ConsoleDestination.swift */; };
+ F2F8D72E2A85EF58009C4D21 /* AFSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5B42A85EF57009C4D21 /* AFSecurityPolicy.h */; };
+ F2F8D72F2A85EF58009C4D21 /* AFNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5B52A85EF57009C4D21 /* AFNetworkReachabilityManager.h */; };
+ F2F8D7302A85EF58009C4D21 /* AFURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5B62A85EF57009C4D21 /* AFURLSessionManager.h */; };
+ F2F8D7312A85EF58009C4D21 /* AFURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5B72A85EF57009C4D21 /* AFURLRequestSerialization.h */; };
+ F2F8D7322A85EF58009C4D21 /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5B82A85EF57009C4D21 /* AFURLResponseSerialization.m */; };
+ F2F8D7332A85EF58009C4D21 /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5B92A85EF57009C4D21 /* AFHTTPSessionManager.m */; };
+ F2F8D7342A85EF58009C4D21 /* AFURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5BA2A85EF57009C4D21 /* AFURLResponseSerialization.h */; };
+ F2F8D7352A85EF58009C4D21 /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5BB2A85EF57009C4D21 /* AFURLSessionManager.m */; };
+ F2F8D7362A85EF58009C4D21 /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5BC2A85EF57009C4D21 /* AFURLRequestSerialization.m */; };
+ F2F8D7372A85EF58009C4D21 /* AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5BD2A85EF57009C4D21 /* AFNetworking.h */; };
+ F2F8D7382A85EF58009C4D21 /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5BE2A85EF57009C4D21 /* AFNetworkReachabilityManager.m */; };
+ F2F8D7392A85EF58009C4D21 /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5BF2A85EF57009C4D21 /* AFSecurityPolicy.m */; };
+ F2F8D73A2A85EF58009C4D21 /* AFCompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5C02A85EF57009C4D21 /* AFCompatibilityMacros.h */; };
+ F2F8D73B2A85EF58009C4D21 /* AFHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F2F8D5C12A85EF57009C4D21 /* AFHTTPSessionManager.h */; };
+ F2F8D75F2A85EF58009C4D21 /* AliCloudIotBleDelegate_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5E72A85EF58009C4D21 /* AliCloudIotBleDelegate_C.swift */; };
+ F2F8D7602A85EF58009C4D21 /* AliCloudIotError_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5E82A85EF58009C4D21 /* AliCloudIotError_C.swift */; };
+ F2F8D7612A85EF58009C4D21 /* AliCloudIotSdkInitDelegate_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5E92A85EF58009C4D21 /* AliCloudIotSdkInitDelegate_C.swift */; };
+ F2F8D7622A85EF58009C4D21 /* AliCloudIotQulianConfig_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5EA2A85EF58009C4D21 /* AliCloudIotQulianConfig_C.swift */; };
+ F2F8D7632A85EF58009C4D21 /* AliCloudIotConnectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5EB2A85EF58009C4D21 /* AliCloudIotConnectionState.swift */; };
+ F2F8D7642A85EF58009C4D21 /* AliCloudIotSdkConfigBase_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5EC2A85EF58009C4D21 /* AliCloudIotSdkConfigBase_C.swift */; };
+ F2F8D7652A85EF58009C4D21 /* MqttState_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5ED2A85EF58009C4D21 /* MqttState_C.swift */; };
+ F2F8D7662A85EF58009C4D21 /* AliCloudIotDelegate_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5EE2A85EF58009C4D21 /* AliCloudIotDelegate_C.swift */; };
+ F2F8D7672A85EF58009C4D21 /* AliCloudIotDirectConfig_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D5EF2A85EF58009C4D21 /* AliCloudIotDirectConfig_C.swift */; };
+ F2F8D7682A85F329009C4D21 /* AliConnectMananger_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D4702A85EF56009C4D21 /* AliConnectMananger_C.swift */; };
+ F2F8D7692A85F32C009C4D21 /* AliCloudIotLogManager_C.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8D46F2A85EF56009C4D21 /* AliCloudIotLogManager_C.swift */; };
+ F2FB5A152AA0CE9F001EEB99 /* iotMqtt.h in Headers */ = {isa = PBXBuildFile; fileRef = F2FB5A132AA0CE9F001EEB99 /* iotMqtt.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F2FB5A1D2AA20062001EEB99 /* libiotRtc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F2FB5A1C2AA20062001EEB99 /* libiotRtc.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 185C1B0A2B21547C00695075 /* MqttFileManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MqttFileManager.swift; sourceTree = "<group>"; };
+ 185C1B0C2B21549600695075 /* Data_category.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data_category.swift; sourceTree = "<group>"; };
+ 18BF1E252B83095500B8D90F /* CentralManagerRestoredState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CentralManagerRestoredState.swift; sourceTree = "<group>"; };
+ 18BF1E262B83095500B8D90F /* BluetoothState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothState.swift; sourceTree = "<group>"; };
+ 18BF1E272B83095500B8D90F /* Peripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Peripheral.swift; sourceTree = "<group>"; };
+ 18BF1E282B83095500B8D90F /* PeripheralManager+RestoredState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PeripheralManager+RestoredState.swift"; sourceTree = "<group>"; };
+ 18BF1E292B83095500B8D90F /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
+ 18BF1E2A2B83095500B8D90F /* CBPeripheralDelegateWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBPeripheralDelegateWrapper.swift; sourceTree = "<group>"; };
+ 18BF1E2B2B83095500B8D90F /* CBPeripheralManagerDelegateWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBPeripheralManagerDelegateWrapper.swift; sourceTree = "<group>"; };
+ 18BF1E2C2B83095500B8D90F /* CBPeripheral+Uuid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CBPeripheral+Uuid.swift"; sourceTree = "<group>"; };
+ 18BF1E2D2B83095500B8D90F /* DeviceIdentifiers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceIdentifiers.swift; sourceTree = "<group>"; };
+ 18BF1E2E2B83095500B8D90F /* Service.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = "<group>"; };
+ 18BF1E2F2B83095500B8D90F /* CBCentral+Uuid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CBCentral+Uuid.swift"; sourceTree = "<group>"; };
+ 18BF1E302B83095500B8D90F /* BluetoothError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothError.swift; sourceTree = "<group>"; };
+ 18BF1E312B83095500B8D90F /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
+ 18BF1E322B83095500B8D90F /* Peripheral+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Peripheral+Convenience.swift"; sourceTree = "<group>"; };
+ 18BF1E332B83095500B8D90F /* AdvertisementData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvertisementData.swift; sourceTree = "<group>"; };
+ 18BF1E342B83095500B8D90F /* PeripheralManagerRestoredState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeripheralManagerRestoredState.swift; sourceTree = "<group>"; };
+ 18BF1E352B83095500B8D90F /* RxBluetoothKitLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxBluetoothKitLogger.swift; sourceTree = "<group>"; };
+ 18BF1E362B83095500B8D90F /* PeripheralProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeripheralProvider.swift; sourceTree = "<group>"; };
+ 18BF1E372B83095500B8D90F /* CBCentralManagerDelegateWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBCentralManagerDelegateWrapper.swift; sourceTree = "<group>"; };
+ 18BF1E382B83095500B8D90F /* RestoredState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoredState.swift; sourceTree = "<group>"; };
+ 18BF1E392B83095500B8D90F /* Descriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Descriptor.swift; sourceTree = "<group>"; };
+ 18BF1E3A2B83095500B8D90F /* CharacteristicNotificationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacteristicNotificationManager.swift; sourceTree = "<group>"; };
+ 18BF1E3B2B83095500B8D90F /* CentralManager+RestoredState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CentralManager+RestoredState.swift"; sourceTree = "<group>"; };
+ 18BF1E3C2B83095500B8D90F /* Boxes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Boxes.swift; sourceTree = "<group>"; };
+ 18BF1E3D2B83095500B8D90F /* CentralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CentralManager.swift; sourceTree = "<group>"; };
+ 18BF1E3E2B83095500B8D90F /* Observable+Absorb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Absorb.swift"; sourceTree = "<group>"; };
+ 18BF1E3F2B83095500B8D90F /* Array+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Utils.swift"; sourceTree = "<group>"; };
+ 18BF1E402B83095500B8D90F /* UUIDIdentifiable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UUIDIdentifiable.swift; sourceTree = "<group>"; };
+ 18BF1E412B83095500B8D90F /* Connector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Connector.swift; sourceTree = "<group>"; };
+ 18BF1E422B83095500B8D90F /* ScannedPeripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScannedPeripheral.swift; sourceTree = "<group>"; };
+ 18BF1E432B83095500B8D90F /* ManagerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagerType.swift; sourceTree = "<group>"; };
+ 18BF1E442B83095500B8D90F /* PeripheralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeripheralManager.swift; sourceTree = "<group>"; };
+ 18BF1E452B83095500B8D90F /* StartAdvertisingResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartAdvertisingResult.swift; sourceTree = "<group>"; };
+ 18BF1E462B83095500B8D90F /* Characteristic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Characteristic.swift; sourceTree = "<group>"; };
+ 18BF1E472B83095500B8D90F /* Unimplemented.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Unimplemented.swift; sourceTree = "<group>"; };
+ 18DE22732B21A5490096A835 /* MqttHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MqttHelper.h; sourceTree = "<group>"; };
+ 18DE22742B21A5490096A835 /* MqttHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MqttHelper.mm; sourceTree = "<group>"; };
+ F21215152A85EC2800BD4BEF /* AliIotConnectKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AliIotConnectKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F21215182A85EC2800BD4BEF /* AliIotConnectKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AliIotConnectKit.h; sourceTree = "<group>"; };
+ F212151F2A85EC5100BD4BEF /* AliIotConnect.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = AliIotConnect.modulemap; sourceTree = "<group>"; };
+ F2F8D4642A85EF56009C4D21 /* srca.cer */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = srca.cer; sourceTree = "<group>"; };
+ F2F8D4662A85EF56009C4D21 /* FGSCheckService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FGSCheckService_C.swift; sourceTree = "<group>"; };
+ F2F8D4672A85EF56009C4D21 /* BleService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleService_C.swift; sourceTree = "<group>"; };
+ F2F8D4682A85EF56009C4D21 /* AliCloudIotAutomaticService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotAutomaticService_C.swift; sourceTree = "<group>"; };
+ F2F8D4692A85EF56009C4D21 /* BleBaseSendService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleBaseSendService_C.swift; sourceTree = "<group>"; };
+ F2F8D46A2A85EF56009C4D21 /* IotService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IotService_C.swift; sourceTree = "<group>"; };
+ F2F8D46B2A85EF56009C4D21 /* RideYardService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RideYardService_C.swift; sourceTree = "<group>"; };
+ F2F8D46C2A85EF56009C4D21 /* ID2Service_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ID2Service_C.swift; sourceTree = "<group>"; };
+ F2F8D46D2A85EF56009C4D21 /* LPConnectService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LPConnectService_C.swift; sourceTree = "<group>"; };
+ F2F8D46F2A85EF56009C4D21 /* AliCloudIotLogManager_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotLogManager_C.swift; sourceTree = "<group>"; };
+ F2F8D4702A85EF56009C4D21 /* AliConnectMananger_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliConnectMananger_C.swift; sourceTree = "<group>"; };
+ F2F8D4712A85EF56009C4D21 /* AliCloudIotWatchManager_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotWatchManager_C.swift; sourceTree = "<group>"; };
+ F2F8D4732A85EF56009C4D21 /* MqttManager_C.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MqttManager_C.m; sourceTree = "<group>"; };
+ F2F8D4742A85EF56009C4D21 /* AFHttpHelper_C.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHttpHelper_C.h; sourceTree = "<group>"; };
+ F2F8D4752A85EF56009C4D21 /* AFHttpHelper_C.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHttpHelper_C.m; sourceTree = "<group>"; };
+ F2F8D4762A85EF56009C4D21 /* MqttManager_C.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MqttManager_C.h; sourceTree = "<group>"; };
+ F2F8D4782A85EF56009C4D21 /* AESEncode_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AESEncode_C.swift; sourceTree = "<group>"; };
+ F2F8D4792A85EF56009C4D21 /* HMACTool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACTool.swift; sourceTree = "<group>"; };
+ F2F8D47A2A85EF56009C4D21 /* CommonConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonConfig.swift; sourceTree = "<group>"; };
+ F2F8D47B2A85EF56009C4D21 /* IDGenerater_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IDGenerater_C.swift; sourceTree = "<group>"; };
+ F2F8D47C2A85EF56009C4D21 /* MyExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyExtension.swift; sourceTree = "<group>"; };
+ F2F8D47F2A85EF56009C4D21 /* IotMqttManager_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IotMqttManager_C.swift; sourceTree = "<group>"; };
+ F2F8D4802A85EF56009C4D21 /* IotMqttLegacyConfig_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IotMqttLegacyConfig_C.swift; sourceTree = "<group>"; };
+ F2F8D4812A85EF56009C4D21 /* IotStateDelatege_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IotStateDelatege_C.swift; sourceTree = "<group>"; };
+ F2F8D4832A85EF56009C4D21 /* SdkMqttDelegate_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SdkMqttDelegate_C.swift; sourceTree = "<group>"; };
+ F2F8D4842A85EF56009C4D21 /* SdkMqttService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SdkMqttService_C.swift; sourceTree = "<group>"; };
+ F2F8D4862A85EF56009C4D21 /* HttpTool_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpTool_C.swift; sourceTree = "<group>"; };
+ F2F8D4882A85EF56009C4D21 /* ThingCommunicateService_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThingCommunicateService_C.swift; sourceTree = "<group>"; };
+ F2F8D48A2A85EF56009C4D21 /* WearCommonEvent_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WearCommonEvent_C.swift; sourceTree = "<group>"; };
+ F2F8D48B2A85EF56009C4D21 /* EventIdentifier_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventIdentifier_C.swift; sourceTree = "<group>"; };
+ F2F8D48C2A85EF56009C4D21 /* ReplyPack_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyPack_C.swift; sourceTree = "<group>"; };
+ F2F8D48D2A85EF56009C4D21 /* ThingPack_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThingPack_C.swift; sourceTree = "<group>"; };
+ F2F8D48E2A85EF56009C4D21 /* CommandIdentifier_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandIdentifier_C.swift; sourceTree = "<group>"; };
+ F2F8D48F2A85EF56009C4D21 /* WearCommonCommand_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WearCommonCommand_C.swift; sourceTree = "<group>"; };
+ F2F8D4902A85EF56009C4D21 /* FGSMsgType_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FGSMsgType_C.swift; sourceTree = "<group>"; };
+ F2F8D4912A85EF56009C4D21 /* ThingType_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThingType_C.swift; sourceTree = "<group>"; };
+ F2F8D4932A85EF56009C4D21 /* BleNeedSendDataDelegate_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleNeedSendDataDelegate_C.swift; sourceTree = "<group>"; };
+ F2F8D4942A85EF56009C4D21 /* BleExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleExtension.swift; sourceTree = "<group>"; };
+ F2F8D4952A85EF56009C4D21 /* BleManager_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleManager_C.swift; sourceTree = "<group>"; };
+ F2F8D4962A85EF56009C4D21 /* DeviceServiceCharacteristic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceServiceCharacteristic.swift; sourceTree = "<group>"; };
+ F2F8D4972A85EF56009C4D21 /* BleDeviceModel_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleDeviceModel_C.swift; sourceTree = "<group>"; };
+ F2F8D4982A85EF56009C4D21 /* BleSIdentifier_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleSIdentifier_C.swift; sourceTree = "<group>"; };
+ F2F8D4992A85EF56009C4D21 /* BleAuthModel_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleAuthModel_C.swift; sourceTree = "<group>"; };
+ F2F8D49A2A85EF56009C4D21 /* BleTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BleTypes.swift; sourceTree = "<group>"; };
+ F2F8D49D2A85EF57009C4D21 /* COOKSwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = COOKSwiftyJSON.swift; sourceTree = "<group>"; };
+ F2F8D49F2A85EF57009C4D21 /* libiotMqtt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libiotMqtt.a; sourceTree = "<group>"; };
+ F2F8D4A02A85EF57009C4D21 /* iotMqtt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iotMqtt.h; sourceTree = "<group>"; };
+ F2F8D4A22A85EF57009C4D21 /* AsMaybe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsMaybe.swift; sourceTree = "<group>"; };
+ F2F8D4A32A85EF57009C4D21 /* SubjectType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = "<group>"; };
+ F2F8D4A42A85EF57009C4D21 /* DisposeBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBase.swift; sourceTree = "<group>"; };
+ F2F8D4A52A85EF57009C4D21 /* Cancelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cancelable.swift; sourceTree = "<group>"; };
+ F2F8D4A62A85EF57009C4D21 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = "<group>"; };
+ F2F8D4A72A85EF57009C4D21 /* GroupedObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupedObservable.swift; sourceTree = "<group>"; };
+ F2F8D4A82A85EF57009C4D21 /* Timeout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timeout.swift; sourceTree = "<group>"; };
+ F2F8D4A92A85EF57009C4D21 /* RecursiveScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecursiveScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4AA2A85EF57009C4D21 /* Never.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = "<group>"; };
+ F2F8D4AB2A85EF57009C4D21 /* Completable+AndThen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Completable+AndThen.swift"; sourceTree = "<group>"; };
+ F2F8D4AC2A85EF57009C4D21 /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = "<group>"; };
+ F2F8D4AD2A85EF57009C4D21 /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = "<group>"; };
+ F2F8D4AE2A85EF57009C4D21 /* Do.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Do.swift; sourceTree = "<group>"; };
+ F2F8D4AF2A85EF57009C4D21 /* OperationQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueueScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4B02A85EF57009C4D21 /* Deferred.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deferred.swift; sourceTree = "<group>"; };
+ F2F8D4B12A85EF57009C4D21 /* Bag+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bag+Rx.swift"; sourceTree = "<group>"; };
+ F2F8D4B22A85EF57009C4D21 /* LockOwnerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockOwnerType.swift; sourceTree = "<group>"; };
+ F2F8D4B32A85EF57009C4D21 /* Platform.Darwin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Platform.Darwin.swift; sourceTree = "<group>"; };
+ F2F8D4B42A85EF57009C4D21 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedDisposeType.swift; sourceTree = "<group>"; };
+ F2F8D4B52A85EF57009C4D21 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = "<group>"; };
+ F2F8D4B62A85EF57009C4D21 /* SubscribeOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeOn.swift; sourceTree = "<group>"; };
+ F2F8D4B72A85EF57009C4D21 /* VirtualTimeScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4B82A85EF57009C4D21 /* SynchronizedOnType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedOnType.swift; sourceTree = "<group>"; };
+ F2F8D4B92A85EF57009C4D21 /* DefaultIfEmpty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultIfEmpty.swift; sourceTree = "<group>"; };
+ F2F8D4BA2A85EF57009C4D21 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = "<group>"; };
+ F2F8D4BB2A85EF57009C4D21 /* AsyncSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncSubject.swift; sourceTree = "<group>"; };
+ F2F8D4BC2A85EF57009C4D21 /* DispatchQueue+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Extensions.swift"; sourceTree = "<group>"; };
+ F2F8D4BD2A85EF57009C4D21 /* DisposeBag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBag.swift; sourceTree = "<group>"; };
+ F2F8D4BE2A85EF57009C4D21 /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = "<group>"; };
+ F2F8D4BF2A85EF57009C4D21 /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4C02A85EF57009C4D21 /* Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = "<group>"; };
+ F2F8D4C12A85EF57009C4D21 /* AtomicInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomicInt.swift; sourceTree = "<group>"; };
+ F2F8D4C22A85EF57009C4D21 /* Producer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Producer.swift; sourceTree = "<group>"; };
+ F2F8D4C32A85EF57009C4D21 /* GroupBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupBy.swift; sourceTree = "<group>"; };
+ F2F8D4C42A85EF57009C4D21 /* InvocableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = "<group>"; };
+ F2F8D4C52A85EF57009C4D21 /* RxMutableBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxMutableBox.swift; sourceTree = "<group>"; };
+ F2F8D4C62A85EF57009C4D21 /* Concat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = "<group>"; };
+ F2F8D4C72A85EF57009C4D21 /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = "<group>"; };
+ F2F8D4C82A85EF57009C4D21 /* ScheduledDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledDisposable.swift; sourceTree = "<group>"; };
+ F2F8D4C92A85EF57009C4D21 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
+ F2F8D4CA2A85EF57009C4D21 /* MainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4CB2A85EF57009C4D21 /* Take.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Take.swift; sourceTree = "<group>"; };
+ F2F8D4CC2A85EF57009C4D21 /* TakeWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeWhile.swift; sourceTree = "<group>"; };
+ F2F8D4CD2A85EF57009C4D21 /* TakeLast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = "<group>"; };
+ F2F8D4CE2A85EF57009C4D21 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
+ F2F8D4CF2A85EF57009C4D21 /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = "<group>"; };
+ F2F8D4D02A85EF57009C4D21 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = "<group>"; };
+ F2F8D4D12A85EF57009C4D21 /* CombineLatest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineLatest.swift; sourceTree = "<group>"; };
+ F2F8D4D22A85EF57009C4D21 /* NopDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NopDisposable.swift; sourceTree = "<group>"; };
+ F2F8D4D32A85EF57009C4D21 /* Sample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = "<group>"; };
+ F2F8D4D42A85EF57009C4D21 /* AnyObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyObserver.swift; sourceTree = "<group>"; };
+ F2F8D4D52A85EF57009C4D21 /* AsSingle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsSingle.swift; sourceTree = "<group>"; };
+ F2F8D4D62A85EF57009C4D21 /* Completable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Completable.swift; sourceTree = "<group>"; };
+ F2F8D4D72A85EF57009C4D21 /* Empty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = "<group>"; };
+ F2F8D4D82A85EF57009C4D21 /* Enumerated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Enumerated.swift; sourceTree = "<group>"; };
+ F2F8D4D92A85EF57009C4D21 /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistinctUntilChanged.swift; sourceTree = "<group>"; };
+ F2F8D4DA2A85EF57009C4D21 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionDisposable.swift; sourceTree = "<group>"; };
+ F2F8D4DB2A85EF57009C4D21 /* ConcurrentDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentDispatchQueueScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4DC2A85EF57009C4D21 /* First.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = First.swift; sourceTree = "<group>"; };
+ F2F8D4DD2A85EF57009C4D21 /* Lock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = "<group>"; };
+ F2F8D4DE2A85EF57009C4D21 /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
+ F2F8D4DF2A85EF57009C4D21 /* Single.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Single.swift; sourceTree = "<group>"; };
+ F2F8D4E02A85EF57009C4D21 /* AddRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddRef.swift; sourceTree = "<group>"; };
+ F2F8D4E12A85EF57009C4D21 /* Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = "<group>"; };
+ F2F8D4E22A85EF57009C4D21 /* ObserverBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = "<group>"; };
+ F2F8D4E32A85EF57009C4D21 /* Sink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sink.swift; sourceTree = "<group>"; };
+ F2F8D4E42A85EF57009C4D21 /* AsyncLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncLock.swift; sourceTree = "<group>"; };
+ F2F8D4E52A85EF57009C4D21 /* Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = "<group>"; };
+ F2F8D4E62A85EF57009C4D21 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = "<group>"; };
+ F2F8D4E72A85EF57009C4D21 /* CompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompactMap.swift; sourceTree = "<group>"; };
+ F2F8D4E82A85EF57009C4D21 /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = "<group>"; };
+ F2F8D4E92A85EF57009C4D21 /* PrimitiveSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveSequence.swift; sourceTree = "<group>"; };
+ F2F8D4EA2A85EF57009C4D21 /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = "<group>"; };
+ F2F8D4EB2A85EF57009C4D21 /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = "<group>"; };
+ F2F8D4EC2A85EF57009C4D21 /* ObserveOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserveOn.swift; sourceTree = "<group>"; };
+ F2F8D4ED2A85EF57009C4D21 /* CombineLatest+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+Collection.swift"; sourceTree = "<group>"; };
+ F2F8D4EE2A85EF57009C4D21 /* ObservableType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableType+Extensions.swift"; sourceTree = "<group>"; };
+ F2F8D4EF2A85EF57009C4D21 /* CompositeDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositeDisposable.swift; sourceTree = "<group>"; };
+ F2F8D4F02A85EF57009C4D21 /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4F12A85EF57009C4D21 /* Multicast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multicast.swift; sourceTree = "<group>"; };
+ F2F8D4F22A85EF57009C4D21 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = "<group>"; };
+ F2F8D4F32A85EF57009C4D21 /* ObserverType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverType.swift; sourceTree = "<group>"; };
+ F2F8D4F42A85EF57009C4D21 /* TailRecursiveSink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = "<group>"; };
+ F2F8D4F52A85EF57009C4D21 /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4F62A85EF57009C4D21 /* DelaySubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelaySubscription.swift; sourceTree = "<group>"; };
+ F2F8D4F72A85EF57009C4D21 /* HistoricalScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoricalScheduler.swift; sourceTree = "<group>"; };
+ F2F8D4F82A85EF57009C4D21 /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = "<group>"; };
+ F2F8D4F92A85EF57009C4D21 /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = "<group>"; };
+ F2F8D4FA2A85EF57009C4D21 /* Platform.Linux.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Platform.Linux.swift; sourceTree = "<group>"; };
+ F2F8D4FB2A85EF57009C4D21 /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = "<group>"; };
+ F2F8D4FC2A85EF57009C4D21 /* Maybe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Maybe.swift; sourceTree = "<group>"; };
+ F2F8D4FD2A85EF57009C4D21 /* Date+Dispatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Dispatch.swift"; sourceTree = "<group>"; };
+ F2F8D4FE2A85EF57009C4D21 /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = "<group>"; };
+ F2F8D4FF2A85EF57009C4D21 /* TakeUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeUntil.swift; sourceTree = "<group>"; };
+ F2F8D5002A85EF57009C4D21 /* DispatchQueueConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueueConfiguration.swift; sourceTree = "<group>"; };
+ F2F8D5012A85EF57009C4D21 /* Switch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = "<group>"; };
+ F2F8D5022A85EF57009C4D21 /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
+ F2F8D5032A85EF57009C4D21 /* Debounce.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
+ F2F8D5042A85EF57009C4D21 /* Dematerialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dematerialize.swift; sourceTree = "<group>"; };
+ F2F8D5052A85EF57009C4D21 /* SynchronizedUnsubscribeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedUnsubscribeType.swift; sourceTree = "<group>"; };
+ F2F8D5062A85EF57009C4D21 /* Zip+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+Collection.swift"; sourceTree = "<group>"; };
+ F2F8D5072A85EF57009C4D21 /* Deprecated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
+ F2F8D5082A85EF57009C4D21 /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = "<group>"; };
+ F2F8D5092A85EF57009C4D21 /* AnonymousObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = "<group>"; };
+ F2F8D50A2A85EF57009C4D21 /* SwiftSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSupport.swift; sourceTree = "<group>"; };
+ F2F8D50B2A85EF57009C4D21 /* PriorityQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PriorityQueue.swift; sourceTree = "<group>"; };
+ F2F8D50C2A85EF57009C4D21 /* SingleAssignmentDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAssignmentDisposable.swift; sourceTree = "<group>"; };
+ F2F8D50D2A85EF57009C4D21 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = "<group>"; };
+ F2F8D50E2A85EF57009C4D21 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = "<group>"; };
+ F2F8D50F2A85EF57009C4D21 /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; };
+ F2F8D5102A85EF57009C4D21 /* ObservableType+PrimitiveSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableType+PrimitiveSequence.swift"; sourceTree = "<group>"; };
+ F2F8D5112A85EF57009C4D21 /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = "<group>"; };
+ F2F8D5122A85EF57009C4D21 /* Reduce.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reduce.swift; sourceTree = "<group>"; };
+ F2F8D5132A85EF57009C4D21 /* VirtualTimeConverterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeConverterType.swift; sourceTree = "<group>"; };
+ F2F8D5142A85EF57009C4D21 /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = "<group>"; };
+ F2F8D5152A85EF57009C4D21 /* HistoricalSchedulerTimeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoricalSchedulerTimeConverter.swift; sourceTree = "<group>"; };
+ F2F8D5162A85EF57009C4D21 /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = "<group>"; };
+ F2F8D5172A85EF57009C4D21 /* Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = "<group>"; };
+ F2F8D5182A85EF57009C4D21 /* Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debug.swift; sourceTree = "<group>"; };
+ F2F8D5192A85EF57009C4D21 /* SchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerType.swift; sourceTree = "<group>"; };
+ F2F8D51A2A85EF57009C4D21 /* SerialDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDisposable.swift; sourceTree = "<group>"; };
+ F2F8D51B2A85EF57009C4D21 /* ConnectableObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservableType.swift; sourceTree = "<group>"; };
+ F2F8D51C2A85EF57009C4D21 /* Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Merge.swift; sourceTree = "<group>"; };
+ F2F8D51D2A85EF57009C4D21 /* ObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = "<group>"; };
+ F2F8D51E2A85EF57009C4D21 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
+ F2F8D51F2A85EF57009C4D21 /* Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rx.swift; sourceTree = "<group>"; };
+ F2F8D5202A85EF57009C4D21 /* Skip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Skip.swift; sourceTree = "<group>"; };
+ F2F8D5212A85EF57009C4D21 /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = "<group>"; };
+ F2F8D5222A85EF57009C4D21 /* BinaryDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryDisposable.swift; sourceTree = "<group>"; };
+ F2F8D5232A85EF57009C4D21 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = "<group>"; };
+ F2F8D5242A85EF57009C4D21 /* InfiniteSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfiniteSequence.swift; sourceTree = "<group>"; };
+ F2F8D5252A85EF57009C4D21 /* PrimitiveSequence+Zip+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PrimitiveSequence+Zip+arity.swift"; sourceTree = "<group>"; };
+ F2F8D5262A85EF57009C4D21 /* AnonymousDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousDisposable.swift; sourceTree = "<group>"; };
+ F2F8D5272A85EF57009C4D21 /* StartWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartWith.swift; sourceTree = "<group>"; };
+ F2F8D5282A85EF57009C4D21 /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = "<group>"; };
+ F2F8D5292A85EF57009C4D21 /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = "<group>"; };
+ F2F8D52A2A85EF57009C4D21 /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = "<group>"; };
+ F2F8D52B2A85EF57009C4D21 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = "<group>"; };
+ F2F8D52C2A85EF57009C4D21 /* ShareReplayScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplayScope.swift; sourceTree = "<group>"; };
+ F2F8D52D2A85EF57009C4D21 /* Disposables.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposables.swift; sourceTree = "<group>"; };
+ F2F8D52E2A85EF57009C4D21 /* RecursiveLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecursiveLock.swift; sourceTree = "<group>"; };
+ F2F8D52F2A85EF57009C4D21 /* ScheduledItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItemType.swift; sourceTree = "<group>"; };
+ F2F8D5302A85EF57009C4D21 /* Scan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scan.swift; sourceTree = "<group>"; };
+ F2F8D5312A85EF57009C4D21 /* Zip+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+arity.swift"; sourceTree = "<group>"; };
+ F2F8D5322A85EF57009C4D21 /* SwitchIfEmpty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchIfEmpty.swift; sourceTree = "<group>"; };
+ F2F8D5332A85EF57009C4D21 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = "<group>"; };
+ F2F8D5342A85EF57009C4D21 /* Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Create.swift; sourceTree = "<group>"; };
+ F2F8D5352A85EF57009C4D21 /* Materialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Materialize.swift; sourceTree = "<group>"; };
+ F2F8D5362A85EF57009C4D21 /* SingleAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = "<group>"; };
+ F2F8D5372A85EF57009C4D21 /* ImmediateSchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateSchedulerType.swift; sourceTree = "<group>"; };
+ F2F8D5392A85EF57009C4D21 /* RSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSA.swift; sourceTree = "<group>"; };
+ F2F8D53A2A85EF57009C4D21 /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = "<group>"; };
+ F2F8D53B2A85EF57009C4D21 /* String+FoundationExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+FoundationExtension.swift"; sourceTree = "<group>"; };
+ F2F8D53C2A85EF57009C4D21 /* BlockModeOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockModeOptions.swift; sourceTree = "<group>"; };
+ F2F8D53D2A85EF57009C4D21 /* StreamDecryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamDecryptor.swift; sourceTree = "<group>"; };
+ F2F8D53E2A85EF57009C4D21 /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = "<group>"; };
+ F2F8D53F2A85EF57009C4D21 /* GCD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GCD.swift; sourceTree = "<group>"; };
+ F2F8D5402A85EF57009C4D21 /* BlockEncryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockEncryptor.swift; sourceTree = "<group>"; };
+ F2F8D5412A85EF57009C4D21 /* OCB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OCB.swift; sourceTree = "<group>"; };
+ F2F8D5422A85EF57009C4D21 /* Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hashable.swift; sourceTree = "<group>"; };
+ F2F8D5432A85EF57009C4D21 /* UInt64+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt64+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5442A85EF57009C4D21 /* Cryptors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cryptors.swift; sourceTree = "<group>"; };
+ F2F8D5452A85EF57009C4D21 /* HKDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKDF.swift; sourceTree = "<group>"; };
+ F2F8D5462A85EF57009C4D21 /* PKCS7Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7Padding.swift; sourceTree = "<group>"; };
+ F2F8D5472A85EF57009C4D21 /* CFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CFB.swift; sourceTree = "<group>"; };
+ F2F8D5482A85EF57009C4D21 /* ISO10126Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISO10126Padding.swift; sourceTree = "<group>"; };
+ F2F8D5492A85EF57009C4D21 /* Strideable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strideable.swift; sourceTree = "<group>"; };
+ F2F8D54A2A85EF57009C4D21 /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
+ F2F8D54B2A85EF57009C4D21 /* Addition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Addition.swift; sourceTree = "<group>"; };
+ F2F8D54C2A85EF57009C4D21 /* Data Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data Conversion.swift"; sourceTree = "<group>"; };
+ F2F8D54D2A85EF57009C4D21 /* CBCMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBCMAC.swift; sourceTree = "<group>"; };
+ F2F8D54E2A85EF57009C4D21 /* Int+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D54F2A85EF57009C4D21 /* AES.Cryptors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.Cryptors.swift; sourceTree = "<group>"; };
+ F2F8D5502A85EF57009C4D21 /* Bit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bit.swift; sourceTree = "<group>"; };
+ F2F8D5512A85EF57009C4D21 /* HMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMAC.swift; sourceTree = "<group>"; };
+ F2F8D5522A85EF57009C4D21 /* CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBC.swift; sourceTree = "<group>"; };
+ F2F8D5532A85EF57009C4D21 /* Bitwise Ops.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bitwise Ops.swift"; sourceTree = "<group>"; };
+ F2F8D5542A85EF57009C4D21 /* CMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CMAC.swift; sourceTree = "<group>"; };
+ F2F8D5552A85EF57009C4D21 /* UInt16+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt16+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5562A85EF57009C4D21 /* Comparable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = "<group>"; };
+ F2F8D5572A85EF57009C4D21 /* Collection+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5582A85EF57009C4D21 /* UInt32+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt32+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5592A85EF57009C4D21 /* Digest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Digest.swift; sourceTree = "<group>"; };
+ F2F8D55A2A85EF57009C4D21 /* UInt128.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UInt128.swift; sourceTree = "<group>"; };
+ F2F8D55B2A85EF57009C4D21 /* RSA+Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RSA+Signature.swift"; sourceTree = "<group>"; };
+ F2F8D55C2A85EF57009C4D21 /* ECB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECB.swift; sourceTree = "<group>"; };
+ F2F8D55D2A85EF57009C4D21 /* Words and Bits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Words and Bits.swift"; sourceTree = "<group>"; };
+ F2F8D55E2A85EF57009C4D21 /* CCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CCM.swift; sourceTree = "<group>"; };
+ F2F8D55F2A85EF57009C4D21 /* Scrypt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scrypt.swift; sourceTree = "<group>"; };
+ F2F8D5602A85EF57009C4D21 /* PCBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PCBC.swift; sourceTree = "<group>"; };
+ F2F8D5612A85EF57009C4D21 /* NoPadding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoPadding.swift; sourceTree = "<group>"; };
+ F2F8D5622A85EF57009C4D21 /* UInt8+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt8+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5632A85EF57009C4D21 /* MD5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = "<group>"; };
+ F2F8D5642A85EF57009C4D21 /* ASN1Scanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Scanner.swift; sourceTree = "<group>"; };
+ F2F8D5652A85EF57009C4D21 /* Cryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = "<group>"; };
+ F2F8D5662A85EF57009C4D21 /* Square Root.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Square Root.swift"; sourceTree = "<group>"; };
+ F2F8D5672A85EF57009C4D21 /* BatchedCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchedCollection.swift; sourceTree = "<group>"; };
+ F2F8D5682A85EF57009C4D21 /* Blowfish+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Blowfish+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D5692A85EF57009C4D21 /* CSCompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSCompactMap.swift; sourceTree = "<group>"; };
+ F2F8D56A2A85EF57009C4D21 /* ISO78164Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISO78164Padding.swift; sourceTree = "<group>"; };
+ F2F8D56B2A85EF57009C4D21 /* SecureBytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureBytes.swift; sourceTree = "<group>"; };
+ F2F8D56C2A85EF57009C4D21 /* Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = "<group>"; };
+ F2F8D56D2A85EF57009C4D21 /* ZeroPadding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZeroPadding.swift; sourceTree = "<group>"; };
+ F2F8D56E2A85EF57009C4D21 /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D56F2A85EF57009C4D21 /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authenticator.swift; sourceTree = "<group>"; };
+ F2F8D5702A85EF57009C4D21 /* SHA3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA3.swift; sourceTree = "<group>"; };
+ F2F8D5712A85EF57009C4D21 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
+ F2F8D5722A85EF57009C4D21 /* Division.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Division.swift; sourceTree = "<group>"; };
+ F2F8D5732A85EF57009C4D21 /* Utils+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Utils+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D5742A85EF57009C4D21 /* PKCS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS5.swift; sourceTree = "<group>"; };
+ F2F8D5752A85EF57009C4D21 /* Prime Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Prime Test.swift"; sourceTree = "<group>"; };
+ F2F8D5762A85EF57009C4D21 /* Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Codable.swift; sourceTree = "<group>"; };
+ F2F8D5772A85EF57009C4D21 /* BlockMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockMode.swift; sourceTree = "<group>"; };
+ F2F8D5782A85EF57009C4D21 /* BigUInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigUInt.swift; sourceTree = "<group>"; };
+ F2F8D5792A85EF57009C4D21 /* Rabbit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rabbit.swift; sourceTree = "<group>"; };
+ F2F8D57A2A85EF57009C4D21 /* PKCS7.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7.swift; sourceTree = "<group>"; };
+ F2F8D57B2A85EF57009C4D21 /* SHA1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA1.swift; sourceTree = "<group>"; };
+ F2F8D57C2A85EF57009C4D21 /* Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poly1305.swift; sourceTree = "<group>"; };
+ F2F8D57D2A85EF57009C4D21 /* BlockCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockCipher.swift; sourceTree = "<group>"; };
+ F2F8D57E2A85EF57009C4D21 /* GCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GCM.swift; sourceTree = "<group>"; };
+ F2F8D57F2A85EF57009C4D21 /* PKCS1v15.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS1v15.swift; sourceTree = "<group>"; };
+ F2F8D5802A85EF57009C4D21 /* Updatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Updatable.swift; sourceTree = "<group>"; };
+ F2F8D5812A85EF57009C4D21 /* DER.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DER.swift; sourceTree = "<group>"; };
+ F2F8D5822A85EF57009C4D21 /* ASN1Encoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Encoder.swift; sourceTree = "<group>"; };
+ F2F8D5832A85EF57009C4D21 /* PBKDF2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBKDF2.swift; sourceTree = "<group>"; };
+ F2F8D5842A85EF57009C4D21 /* Blowfish.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blowfish.swift; sourceTree = "<group>"; };
+ F2F8D5852A85EF57009C4D21 /* BlockDecryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockDecryptor.swift; sourceTree = "<group>"; };
+ F2F8D5862A85EF57009C4D21 /* HMAC+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HMAC+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D5872A85EF57009C4D21 /* StreamEncryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamEncryptor.swift; sourceTree = "<group>"; };
+ F2F8D5882A85EF57009C4D21 /* Multiplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multiplication.swift; sourceTree = "<group>"; };
+ F2F8D5892A85EF57009C4D21 /* CS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CS.swift; sourceTree = "<group>"; };
+ F2F8D58A2A85EF57009C4D21 /* CipherModeWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherModeWorker.swift; sourceTree = "<group>"; };
+ F2F8D58B2A85EF57009C4D21 /* Data+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D58C2A85EF57009C4D21 /* DigestType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestType.swift; sourceTree = "<group>"; };
+ F2F8D58D2A85EF57009C4D21 /* ChaCha20+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ChaCha20+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D58E2A85EF57009C4D21 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
+ F2F8D58F2A85EF57009C4D21 /* Exponentiation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Exponentiation.swift; sourceTree = "<group>"; };
+ F2F8D5902A85EF57009C4D21 /* CTR.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CTR.swift; sourceTree = "<group>"; };
+ F2F8D5912A85EF57009C4D21 /* AEADChaCha20Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEADChaCha20Poly1305.swift; sourceTree = "<group>"; };
+ F2F8D5922A85EF57009C4D21 /* Generics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generics.swift; sourceTree = "<group>"; };
+ F2F8D5932A85EF57009C4D21 /* Checksum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checksum.swift; sourceTree = "<group>"; };
+ F2F8D5942A85EF57009C4D21 /* Floating Point Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Floating Point Conversion.swift"; sourceTree = "<group>"; };
+ F2F8D5952A85EF57009C4D21 /* Shifts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shifts.swift; sourceTree = "<group>"; };
+ F2F8D5962A85EF57009C4D21 /* SHA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA2.swift; sourceTree = "<group>"; };
+ F2F8D5972A85EF57009C4D21 /* Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = "<group>"; };
+ F2F8D5982A85EF57009C4D21 /* AEAD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEAD.swift; sourceTree = "<group>"; };
+ F2F8D5992A85EF57009C4D21 /* ASN1Decoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Decoder.swift; sourceTree = "<group>"; };
+ F2F8D59A2A85EF57009C4D21 /* Array+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D59B2A85EF57009C4D21 /* PBKDF1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBKDF1.swift; sourceTree = "<group>"; };
+ F2F8D59C2A85EF57009C4D21 /* ChaCha20.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha20.swift; sourceTree = "<group>"; };
+ F2F8D59D2A85EF57009C4D21 /* Subtraction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subtraction.swift; sourceTree = "<group>"; };
+ F2F8D59E2A85EF57009C4D21 /* Integer Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Integer Conversion.swift"; sourceTree = "<group>"; };
+ F2F8D59F2A85EF57009C4D21 /* BigInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigInt.swift; sourceTree = "<group>"; };
+ F2F8D5A02A85EF57009C4D21 /* ASN1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = "<group>"; };
+ F2F8D5A12A85EF57009C4D21 /* AES+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AES+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D5A22A85EF57009C4D21 /* Rabbit+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rabbit+Foundation.swift"; sourceTree = "<group>"; };
+ F2F8D5A32A85EF57009C4D21 /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OFB.swift; sourceTree = "<group>"; };
+ F2F8D5A42A85EF57009C4D21 /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = "<group>"; };
+ F2F8D5A52A85EF57009C4D21 /* String Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String Conversion.swift"; sourceTree = "<group>"; };
+ F2F8D5A62A85EF57009C4D21 /* RSA+Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RSA+Cipher.swift"; sourceTree = "<group>"; };
+ F2F8D5A82A85EF57009C4D21 /* SBFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SBFilter.swift; sourceTree = "<group>"; };
+ F2F8D5A92A85EF57009C4D21 /* FilterValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterValidator.swift; sourceTree = "<group>"; };
+ F2F8D5AA2A85EF57009C4D21 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
+ F2F8D5AB2A85EF57009C4D21 /* FileDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileDestination.swift; sourceTree = "<group>"; };
+ F2F8D5AC2A85EF57009C4D21 /* GoogleCloudDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleCloudDestination.swift; sourceTree = "<group>"; };
+ F2F8D5AD2A85EF57009C4D21 /* Base64.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base64.swift; sourceTree = "<group>"; };
+ F2F8D5AE2A85EF57009C4D21 /* SBPlatformDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SBPlatformDestination.swift; sourceTree = "<group>"; };
+ F2F8D5AF2A85EF57009C4D21 /* SwiftyBeaver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyBeaver.swift; sourceTree = "<group>"; };
+ F2F8D5B02A85EF57009C4D21 /* BaseDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDestination.swift; sourceTree = "<group>"; };
+ F2F8D5B12A85EF57009C4D21 /* AES256CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES256CBC.swift; sourceTree = "<group>"; };
+ F2F8D5B22A85EF57009C4D21 /* ConsoleDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleDestination.swift; sourceTree = "<group>"; };
+ F2F8D5B42A85EF57009C4D21 /* AFSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFSecurityPolicy.h; sourceTree = "<group>"; };
+ F2F8D5B52A85EF57009C4D21 /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkReachabilityManager.h; sourceTree = "<group>"; };
+ F2F8D5B62A85EF57009C4D21 /* AFURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLSessionManager.h; sourceTree = "<group>"; };
+ F2F8D5B72A85EF57009C4D21 /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLRequestSerialization.h; sourceTree = "<group>"; };
+ F2F8D5B82A85EF57009C4D21 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLResponseSerialization.m; sourceTree = "<group>"; };
+ F2F8D5B92A85EF57009C4D21 /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPSessionManager.m; sourceTree = "<group>"; };
+ F2F8D5BA2A85EF57009C4D21 /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLResponseSerialization.h; sourceTree = "<group>"; };
+ F2F8D5BB2A85EF57009C4D21 /* AFURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLSessionManager.m; sourceTree = "<group>"; };
+ F2F8D5BC2A85EF57009C4D21 /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLRequestSerialization.m; sourceTree = "<group>"; };
+ F2F8D5BD2A85EF57009C4D21 /* AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworking.h; sourceTree = "<group>"; };
+ F2F8D5BE2A85EF57009C4D21 /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkReachabilityManager.m; sourceTree = "<group>"; };
+ F2F8D5BF2A85EF57009C4D21 /* AFSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFSecurityPolicy.m; sourceTree = "<group>"; };
+ F2F8D5C02A85EF57009C4D21 /* AFCompatibilityMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFCompatibilityMacros.h; sourceTree = "<group>"; };
+ F2F8D5C12A85EF57009C4D21 /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPSessionManager.h; sourceTree = "<group>"; };
+ F2F8D5E72A85EF58009C4D21 /* AliCloudIotBleDelegate_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotBleDelegate_C.swift; sourceTree = "<group>"; };
+ F2F8D5E82A85EF58009C4D21 /* AliCloudIotError_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotError_C.swift; sourceTree = "<group>"; };
+ F2F8D5E92A85EF58009C4D21 /* AliCloudIotSdkInitDelegate_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotSdkInitDelegate_C.swift; sourceTree = "<group>"; };
+ F2F8D5EA2A85EF58009C4D21 /* AliCloudIotQulianConfig_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotQulianConfig_C.swift; sourceTree = "<group>"; };
+ F2F8D5EB2A85EF58009C4D21 /* AliCloudIotConnectionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotConnectionState.swift; sourceTree = "<group>"; };
+ F2F8D5EC2A85EF58009C4D21 /* AliCloudIotSdkConfigBase_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotSdkConfigBase_C.swift; sourceTree = "<group>"; };
+ F2F8D5ED2A85EF58009C4D21 /* MqttState_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MqttState_C.swift; sourceTree = "<group>"; };
+ F2F8D5EE2A85EF58009C4D21 /* AliCloudIotDelegate_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotDelegate_C.swift; sourceTree = "<group>"; };
+ F2F8D5EF2A85EF58009C4D21 /* AliCloudIotDirectConfig_C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliCloudIotDirectConfig_C.swift; sourceTree = "<group>"; };
+ F2FB5A132AA0CE9F001EEB99 /* iotMqtt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iotMqtt.h; sourceTree = "<group>"; };
+ F2FB5A1C2AA20062001EEB99 /* libiotRtc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libiotRtc.a; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ F21215122A85EC2800BD4BEF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F2FB5A1D2AA20062001EEB99 /* libiotRtc.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 18BF1E242B83095500B8D90F /* RXBlue */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1E252B83095500B8D90F /* CentralManagerRestoredState.swift */,
+ 18BF1E262B83095500B8D90F /* BluetoothState.swift */,
+ 18BF1E272B83095500B8D90F /* Peripheral.swift */,
+ 18BF1E282B83095500B8D90F /* PeripheralManager+RestoredState.swift */,
+ 18BF1E292B83095500B8D90F /* Logging.swift */,
+ 18BF1E2A2B83095500B8D90F /* CBPeripheralDelegateWrapper.swift */,
+ 18BF1E2B2B83095500B8D90F /* CBPeripheralManagerDelegateWrapper.swift */,
+ 18BF1E2C2B83095500B8D90F /* CBPeripheral+Uuid.swift */,
+ 18BF1E2D2B83095500B8D90F /* DeviceIdentifiers.swift */,
+ 18BF1E2E2B83095500B8D90F /* Service.swift */,
+ 18BF1E2F2B83095500B8D90F /* CBCentral+Uuid.swift */,
+ 18BF1E302B83095500B8D90F /* BluetoothError.swift */,
+ 18BF1E312B83095500B8D90F /* Logger.swift */,
+ 18BF1E322B83095500B8D90F /* Peripheral+Convenience.swift */,
+ 18BF1E332B83095500B8D90F /* AdvertisementData.swift */,
+ 18BF1E342B83095500B8D90F /* PeripheralManagerRestoredState.swift */,
+ 18BF1E352B83095500B8D90F /* RxBluetoothKitLogger.swift */,
+ 18BF1E362B83095500B8D90F /* PeripheralProvider.swift */,
+ 18BF1E372B83095500B8D90F /* CBCentralManagerDelegateWrapper.swift */,
+ 18BF1E382B83095500B8D90F /* RestoredState.swift */,
+ 18BF1E392B83095500B8D90F /* Descriptor.swift */,
+ 18BF1E3A2B83095500B8D90F /* CharacteristicNotificationManager.swift */,
+ 18BF1E3B2B83095500B8D90F /* CentralManager+RestoredState.swift */,
+ 18BF1E3C2B83095500B8D90F /* Boxes.swift */,
+ 18BF1E3D2B83095500B8D90F /* CentralManager.swift */,
+ 18BF1E3E2B83095500B8D90F /* Observable+Absorb.swift */,
+ 18BF1E3F2B83095500B8D90F /* Array+Utils.swift */,
+ 18BF1E402B83095500B8D90F /* UUIDIdentifiable.swift */,
+ 18BF1E412B83095500B8D90F /* Connector.swift */,
+ 18BF1E422B83095500B8D90F /* ScannedPeripheral.swift */,
+ 18BF1E432B83095500B8D90F /* ManagerType.swift */,
+ 18BF1E442B83095500B8D90F /* PeripheralManager.swift */,
+ 18BF1E452B83095500B8D90F /* StartAdvertisingResult.swift */,
+ 18BF1E462B83095500B8D90F /* Characteristic.swift */,
+ 18BF1E472B83095500B8D90F /* Unimplemented.swift */,
+ );
+ path = RXBlue;
+ sourceTree = "<group>";
+ };
+ F212150B2A85EC2800BD4BEF = {
+ isa = PBXGroup;
+ children = (
+ F21215172A85EC2800BD4BEF /* AliIotConnectKit */,
+ F21215162A85EC2800BD4BEF /* Products */,
+ F2F8D4622A85EF22009C4D21 /* Recovered References */,
+ );
+ sourceTree = "<group>";
+ };
+ F21215162A85EC2800BD4BEF /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ F21215152A85EC2800BD4BEF /* AliIotConnectKit.framework */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ F21215172A85EC2800BD4BEF /* AliIotConnectKit */ = {
+ isa = PBXGroup;
+ children = (
+ F212151F2A85EC5100BD4BEF /* AliIotConnect.modulemap */,
+ F21215182A85EC2800BD4BEF /* AliIotConnectKit.h */,
+ F2F8D4922A85EF56009C4D21 /* Ble */,
+ F2F8D4852A85EF56009C4D21 /* HTTP */,
+ F2F8D46E2A85EF56009C4D21 /* Manager */,
+ F2F8D4772A85EF56009C4D21 /* Misc */,
+ F2F8D5E62A85EF58009C4D21 /* Model */,
+ F2F8D47D2A85EF56009C4D21 /* Mqtt */,
+ F2F8D4722A85EF56009C4D21 /* OC */,
+ F2F8D4632A85EF56009C4D21 /* res */,
+ F2F8D4652A85EF56009C4D21 /* Service */,
+ F2F8D4872A85EF56009C4D21 /* Thing */,
+ F2F8D49B2A85EF57009C4D21 /* ThirdLib */,
+ );
+ path = AliIotConnectKit;
+ sourceTree = "<group>";
+ };
+ F2F8D4622A85EF22009C4D21 /* Recovered References */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = "Recovered References";
+ sourceTree = "<group>";
+ };
+ F2F8D4632A85EF56009C4D21 /* res */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4642A85EF56009C4D21 /* srca.cer */,
+ );
+ path = res;
+ sourceTree = "<group>";
+ };
+ F2F8D4652A85EF56009C4D21 /* Service */ = {
+ isa = PBXGroup;
+ children = (
+ 185C1B0A2B21547C00695075 /* MqttFileManager.swift */,
+ 185C1B0C2B21549600695075 /* Data_category.swift */,
+ F2F8D4662A85EF56009C4D21 /* FGSCheckService_C.swift */,
+ F2F8D4672A85EF56009C4D21 /* BleService_C.swift */,
+ F2F8D4682A85EF56009C4D21 /* AliCloudIotAutomaticService_C.swift */,
+ F2F8D4692A85EF56009C4D21 /* BleBaseSendService_C.swift */,
+ F2F8D46A2A85EF56009C4D21 /* IotService_C.swift */,
+ F2F8D46B2A85EF56009C4D21 /* RideYardService_C.swift */,
+ F2F8D46C2A85EF56009C4D21 /* ID2Service_C.swift */,
+ F2F8D46D2A85EF56009C4D21 /* LPConnectService_C.swift */,
+ );
+ path = Service;
+ sourceTree = "<group>";
+ };
+ F2F8D46E2A85EF56009C4D21 /* Manager */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D46F2A85EF56009C4D21 /* AliCloudIotLogManager_C.swift */,
+ F2F8D4702A85EF56009C4D21 /* AliConnectMananger_C.swift */,
+ F2F8D4712A85EF56009C4D21 /* AliCloudIotWatchManager_C.swift */,
+ );
+ path = Manager;
+ sourceTree = "<group>";
+ };
+ F2F8D4722A85EF56009C4D21 /* OC */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4742A85EF56009C4D21 /* AFHttpHelper_C.h */,
+ F2F8D4752A85EF56009C4D21 /* AFHttpHelper_C.m */,
+ F2F8D4762A85EF56009C4D21 /* MqttManager_C.h */,
+ F2F8D4732A85EF56009C4D21 /* MqttManager_C.m */,
+ 18DE22732B21A5490096A835 /* MqttHelper.h */,
+ 18DE22742B21A5490096A835 /* MqttHelper.mm */,
+ );
+ path = OC;
+ sourceTree = "<group>";
+ };
+ F2F8D4772A85EF56009C4D21 /* Misc */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4782A85EF56009C4D21 /* AESEncode_C.swift */,
+ F2F8D4792A85EF56009C4D21 /* HMACTool.swift */,
+ F2F8D47A2A85EF56009C4D21 /* CommonConfig.swift */,
+ F2F8D47B2A85EF56009C4D21 /* IDGenerater_C.swift */,
+ F2F8D47C2A85EF56009C4D21 /* MyExtension.swift */,
+ );
+ path = Misc;
+ sourceTree = "<group>";
+ };
+ F2F8D47D2A85EF56009C4D21 /* Mqtt */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D47E2A85EF56009C4D21 /* IotMqtt */,
+ F2F8D4812A85EF56009C4D21 /* IotStateDelatege_C.swift */,
+ F2F8D4822A85EF56009C4D21 /* Base */,
+ );
+ path = Mqtt;
+ sourceTree = "<group>";
+ };
+ F2F8D47E2A85EF56009C4D21 /* IotMqtt */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D47F2A85EF56009C4D21 /* IotMqttManager_C.swift */,
+ F2F8D4802A85EF56009C4D21 /* IotMqttLegacyConfig_C.swift */,
+ );
+ path = IotMqtt;
+ sourceTree = "<group>";
+ };
+ F2F8D4822A85EF56009C4D21 /* Base */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4832A85EF56009C4D21 /* SdkMqttDelegate_C.swift */,
+ F2F8D4842A85EF56009C4D21 /* SdkMqttService_C.swift */,
+ );
+ path = Base;
+ sourceTree = "<group>";
+ };
+ F2F8D4852A85EF56009C4D21 /* HTTP */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4862A85EF56009C4D21 /* HttpTool_C.swift */,
+ );
+ path = HTTP;
+ sourceTree = "<group>";
+ };
+ F2F8D4872A85EF56009C4D21 /* Thing */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4882A85EF56009C4D21 /* ThingCommunicateService_C.swift */,
+ F2F8D4892A85EF56009C4D21 /* Model */,
+ );
+ path = Thing;
+ sourceTree = "<group>";
+ };
+ F2F8D4892A85EF56009C4D21 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D48A2A85EF56009C4D21 /* WearCommonEvent_C.swift */,
+ F2F8D48B2A85EF56009C4D21 /* EventIdentifier_C.swift */,
+ F2F8D48C2A85EF56009C4D21 /* ReplyPack_C.swift */,
+ F2F8D48D2A85EF56009C4D21 /* ThingPack_C.swift */,
+ F2F8D48E2A85EF56009C4D21 /* CommandIdentifier_C.swift */,
+ F2F8D48F2A85EF56009C4D21 /* WearCommonCommand_C.swift */,
+ F2F8D4902A85EF56009C4D21 /* FGSMsgType_C.swift */,
+ F2F8D4912A85EF56009C4D21 /* ThingType_C.swift */,
+ );
+ path = Model;
+ sourceTree = "<group>";
+ };
+ F2F8D4922A85EF56009C4D21 /* Ble */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4932A85EF56009C4D21 /* BleNeedSendDataDelegate_C.swift */,
+ F2F8D4942A85EF56009C4D21 /* BleExtension.swift */,
+ F2F8D4952A85EF56009C4D21 /* BleManager_C.swift */,
+ F2F8D4962A85EF56009C4D21 /* DeviceServiceCharacteristic.swift */,
+ F2F8D4972A85EF56009C4D21 /* BleDeviceModel_C.swift */,
+ F2F8D4982A85EF56009C4D21 /* BleSIdentifier_C.swift */,
+ F2F8D4992A85EF56009C4D21 /* BleAuthModel_C.swift */,
+ F2F8D49A2A85EF56009C4D21 /* BleTypes.swift */,
+ );
+ path = Ble;
+ sourceTree = "<group>";
+ };
+ F2F8D49B2A85EF57009C4D21 /* ThirdLib */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1E242B83095500B8D90F /* RXBlue */,
+ F2F8D49E2A85EF57009C4D21 /* IotMqtt */,
+ F2FB5A102AA0CE93001EEB99 /* IotRtc */,
+ F2F8D49C2A85EF57009C4D21 /* SwiftyJSON */,
+ F2F8D4A12A85EF57009C4D21 /* RXSwift */,
+ F2F8D5382A85EF57009C4D21 /* CryptoSwift */,
+ F2F8D5A72A85EF57009C4D21 /* SwiftyBeaver */,
+ F2F8D5B32A85EF57009C4D21 /* AFNetworking */,
+ );
+ path = ThirdLib;
+ sourceTree = "<group>";
+ };
+ F2F8D49C2A85EF57009C4D21 /* SwiftyJSON */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D49D2A85EF57009C4D21 /* COOKSwiftyJSON.swift */,
+ );
+ path = SwiftyJSON;
+ sourceTree = "<group>";
+ };
+ F2F8D49E2A85EF57009C4D21 /* IotMqtt */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4A02A85EF57009C4D21 /* iotMqtt.h */,
+ F2F8D49F2A85EF57009C4D21 /* libiotMqtt.a */,
+ );
+ path = IotMqtt;
+ sourceTree = "<group>";
+ };
+ F2F8D4A12A85EF57009C4D21 /* RXSwift */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D4A22A85EF57009C4D21 /* AsMaybe.swift */,
+ F2F8D4A32A85EF57009C4D21 /* SubjectType.swift */,
+ F2F8D4A42A85EF57009C4D21 /* DisposeBase.swift */,
+ F2F8D4A52A85EF57009C4D21 /* Cancelable.swift */,
+ F2F8D4A62A85EF57009C4D21 /* ScheduledItem.swift */,
+ F2F8D4A72A85EF57009C4D21 /* GroupedObservable.swift */,
+ F2F8D4A82A85EF57009C4D21 /* Timeout.swift */,
+ F2F8D4A92A85EF57009C4D21 /* RecursiveScheduler.swift */,
+ F2F8D4AA2A85EF57009C4D21 /* Never.swift */,
+ F2F8D4AB2A85EF57009C4D21 /* Completable+AndThen.swift */,
+ F2F8D4AC2A85EF57009C4D21 /* BehaviorSubject.swift */,
+ F2F8D4AD2A85EF57009C4D21 /* RetryWhen.swift */,
+ F2F8D4AE2A85EF57009C4D21 /* Do.swift */,
+ F2F8D4AF2A85EF57009C4D21 /* OperationQueueScheduler.swift */,
+ F2F8D4B02A85EF57009C4D21 /* Deferred.swift */,
+ F2F8D4B12A85EF57009C4D21 /* Bag+Rx.swift */,
+ F2F8D4B22A85EF57009C4D21 /* LockOwnerType.swift */,
+ F2F8D4B32A85EF57009C4D21 /* Platform.Darwin.swift */,
+ F2F8D4B42A85EF57009C4D21 /* SynchronizedDisposeType.swift */,
+ F2F8D4B52A85EF57009C4D21 /* Buffer.swift */,
+ F2F8D4B62A85EF57009C4D21 /* SubscribeOn.swift */,
+ F2F8D4B72A85EF57009C4D21 /* VirtualTimeScheduler.swift */,
+ F2F8D4B82A85EF57009C4D21 /* SynchronizedOnType.swift */,
+ F2F8D4B92A85EF57009C4D21 /* DefaultIfEmpty.swift */,
+ F2F8D4BA2A85EF57009C4D21 /* Using.swift */,
+ F2F8D4BB2A85EF57009C4D21 /* AsyncSubject.swift */,
+ F2F8D4BC2A85EF57009C4D21 /* DispatchQueue+Extensions.swift */,
+ F2F8D4BD2A85EF57009C4D21 /* DisposeBag.swift */,
+ F2F8D4BE2A85EF57009C4D21 /* SchedulerServices+Emulation.swift */,
+ F2F8D4BF2A85EF57009C4D21 /* CurrentThreadScheduler.swift */,
+ F2F8D4C02A85EF57009C4D21 /* Delay.swift */,
+ F2F8D4C12A85EF57009C4D21 /* AtomicInt.swift */,
+ F2F8D4C22A85EF57009C4D21 /* Producer.swift */,
+ F2F8D4C32A85EF57009C4D21 /* GroupBy.swift */,
+ F2F8D4C42A85EF57009C4D21 /* InvocableType.swift */,
+ F2F8D4C52A85EF57009C4D21 /* RxMutableBox.swift */,
+ F2F8D4C62A85EF57009C4D21 /* Concat.swift */,
+ F2F8D4C72A85EF57009C4D21 /* ReplaySubject.swift */,
+ F2F8D4C82A85EF57009C4D21 /* ScheduledDisposable.swift */,
+ F2F8D4C92A85EF57009C4D21 /* Event.swift */,
+ F2F8D4CA2A85EF57009C4D21 /* MainScheduler.swift */,
+ F2F8D4CB2A85EF57009C4D21 /* Take.swift */,
+ F2F8D4CC2A85EF57009C4D21 /* TakeWhile.swift */,
+ F2F8D4CD2A85EF57009C4D21 /* TakeLast.swift */,
+ F2F8D4CE2A85EF57009C4D21 /* Error.swift */,
+ F2F8D4CF2A85EF57009C4D21 /* PublishSubject.swift */,
+ F2F8D4D02A85EF57009C4D21 /* InvocableScheduledItem.swift */,
+ F2F8D4D12A85EF57009C4D21 /* CombineLatest.swift */,
+ F2F8D4D22A85EF57009C4D21 /* NopDisposable.swift */,
+ F2F8D4D32A85EF57009C4D21 /* Sample.swift */,
+ F2F8D4D42A85EF57009C4D21 /* AnyObserver.swift */,
+ F2F8D4D52A85EF57009C4D21 /* AsSingle.swift */,
+ F2F8D4D62A85EF57009C4D21 /* Completable.swift */,
+ F2F8D4D72A85EF57009C4D21 /* Empty.swift */,
+ F2F8D4D82A85EF57009C4D21 /* Enumerated.swift */,
+ F2F8D4D92A85EF57009C4D21 /* DistinctUntilChanged.swift */,
+ F2F8D4DA2A85EF57009C4D21 /* SubscriptionDisposable.swift */,
+ F2F8D4DB2A85EF57009C4D21 /* ConcurrentDispatchQueueScheduler.swift */,
+ F2F8D4DC2A85EF57009C4D21 /* First.swift */,
+ F2F8D4DD2A85EF57009C4D21 /* Lock.swift */,
+ F2F8D4DE2A85EF57009C4D21 /* Timer.swift */,
+ F2F8D4DF2A85EF57009C4D21 /* Single.swift */,
+ F2F8D4E02A85EF57009C4D21 /* AddRef.swift */,
+ F2F8D4E12A85EF57009C4D21 /* Sequence.swift */,
+ F2F8D4E22A85EF57009C4D21 /* ObserverBase.swift */,
+ F2F8D4E32A85EF57009C4D21 /* Sink.swift */,
+ F2F8D4E42A85EF57009C4D21 /* AsyncLock.swift */,
+ F2F8D4E52A85EF57009C4D21 /* Throttle.swift */,
+ F2F8D4E62A85EF57009C4D21 /* Range.swift */,
+ F2F8D4E72A85EF57009C4D21 /* CompactMap.swift */,
+ F2F8D4E82A85EF57009C4D21 /* Disposable.swift */,
+ F2F8D4E92A85EF57009C4D21 /* PrimitiveSequence.swift */,
+ F2F8D4EA2A85EF57009C4D21 /* RefCountDisposable.swift */,
+ F2F8D4EB2A85EF57009C4D21 /* WithLatestFrom.swift */,
+ F2F8D4EC2A85EF57009C4D21 /* ObserveOn.swift */,
+ F2F8D4ED2A85EF57009C4D21 /* CombineLatest+Collection.swift */,
+ F2F8D4EE2A85EF57009C4D21 /* ObservableType+Extensions.swift */,
+ F2F8D4EF2A85EF57009C4D21 /* CompositeDisposable.swift */,
+ F2F8D4F02A85EF57009C4D21 /* ConcurrentMainScheduler.swift */,
+ F2F8D4F12A85EF57009C4D21 /* Multicast.swift */,
+ F2F8D4F22A85EF57009C4D21 /* SkipWhile.swift */,
+ F2F8D4F32A85EF57009C4D21 /* ObserverType.swift */,
+ F2F8D4F42A85EF57009C4D21 /* TailRecursiveSink.swift */,
+ F2F8D4F52A85EF57009C4D21 /* SerialDispatchQueueScheduler.swift */,
+ F2F8D4F62A85EF57009C4D21 /* DelaySubscription.swift */,
+ F2F8D4F72A85EF57009C4D21 /* HistoricalScheduler.swift */,
+ F2F8D4F82A85EF57009C4D21 /* Bag.swift */,
+ F2F8D4F92A85EF57009C4D21 /* Repeat.swift */,
+ F2F8D4FA2A85EF57009C4D21 /* Platform.Linux.swift */,
+ F2F8D4FB2A85EF57009C4D21 /* Reactive.swift */,
+ F2F8D4FC2A85EF57009C4D21 /* Maybe.swift */,
+ F2F8D4FD2A85EF57009C4D21 /* Date+Dispatch.swift */,
+ F2F8D4FE2A85EF57009C4D21 /* ToArray.swift */,
+ F2F8D4FF2A85EF57009C4D21 /* TakeUntil.swift */,
+ F2F8D5002A85EF57009C4D21 /* DispatchQueueConfiguration.swift */,
+ F2F8D5012A85EF57009C4D21 /* Switch.swift */,
+ F2F8D5022A85EF57009C4D21 /* Window.swift */,
+ F2F8D5032A85EF57009C4D21 /* Debounce.swift */,
+ F2F8D5042A85EF57009C4D21 /* Dematerialize.swift */,
+ F2F8D5052A85EF57009C4D21 /* SynchronizedUnsubscribeType.swift */,
+ F2F8D5062A85EF57009C4D21 /* Zip+Collection.swift */,
+ F2F8D5072A85EF57009C4D21 /* Deprecated.swift */,
+ F2F8D5082A85EF57009C4D21 /* Catch.swift */,
+ F2F8D5092A85EF57009C4D21 /* AnonymousObserver.swift */,
+ F2F8D50A2A85EF57009C4D21 /* SwiftSupport.swift */,
+ F2F8D50B2A85EF57009C4D21 /* PriorityQueue.swift */,
+ F2F8D50C2A85EF57009C4D21 /* SingleAssignmentDisposable.swift */,
+ F2F8D50D2A85EF57009C4D21 /* Queue.swift */,
+ F2F8D50E2A85EF57009C4D21 /* ObservableConvertibleType.swift */,
+ F2F8D50F2A85EF57009C4D21 /* Filter.swift */,
+ F2F8D5102A85EF57009C4D21 /* ObservableType+PrimitiveSequence.swift */,
+ F2F8D5112A85EF57009C4D21 /* Amb.swift */,
+ F2F8D5122A85EF57009C4D21 /* Reduce.swift */,
+ F2F8D5132A85EF57009C4D21 /* VirtualTimeConverterType.swift */,
+ F2F8D5142A85EF57009C4D21 /* CombineLatest+arity.swift */,
+ F2F8D5152A85EF57009C4D21 /* HistoricalSchedulerTimeConverter.swift */,
+ F2F8D5162A85EF57009C4D21 /* Observable.swift */,
+ F2F8D5172A85EF57009C4D21 /* Zip.swift */,
+ F2F8D5182A85EF57009C4D21 /* Debug.swift */,
+ F2F8D5192A85EF57009C4D21 /* SchedulerType.swift */,
+ F2F8D51A2A85EF57009C4D21 /* SerialDisposable.swift */,
+ F2F8D51B2A85EF57009C4D21 /* ConnectableObservableType.swift */,
+ F2F8D51C2A85EF57009C4D21 /* Merge.swift */,
+ F2F8D51D2A85EF57009C4D21 /* ObservableType.swift */,
+ F2F8D51E2A85EF57009C4D21 /* Errors.swift */,
+ F2F8D51F2A85EF57009C4D21 /* Rx.swift */,
+ F2F8D5202A85EF57009C4D21 /* Skip.swift */,
+ F2F8D5212A85EF57009C4D21 /* Map.swift */,
+ F2F8D5222A85EF57009C4D21 /* BinaryDisposable.swift */,
+ F2F8D5232A85EF57009C4D21 /* SkipUntil.swift */,
+ F2F8D5242A85EF57009C4D21 /* InfiniteSequence.swift */,
+ F2F8D5252A85EF57009C4D21 /* PrimitiveSequence+Zip+arity.swift */,
+ F2F8D5262A85EF57009C4D21 /* AnonymousDisposable.swift */,
+ F2F8D5272A85EF57009C4D21 /* StartWith.swift */,
+ F2F8D5282A85EF57009C4D21 /* Just.swift */,
+ F2F8D5292A85EF57009C4D21 /* BooleanDisposable.swift */,
+ F2F8D52A2A85EF57009C4D21 /* Optional.swift */,
+ F2F8D52B2A85EF57009C4D21 /* Generate.swift */,
+ F2F8D52C2A85EF57009C4D21 /* ShareReplayScope.swift */,
+ F2F8D52D2A85EF57009C4D21 /* Disposables.swift */,
+ F2F8D52E2A85EF57009C4D21 /* RecursiveLock.swift */,
+ F2F8D52F2A85EF57009C4D21 /* ScheduledItemType.swift */,
+ F2F8D5302A85EF57009C4D21 /* Scan.swift */,
+ F2F8D5312A85EF57009C4D21 /* Zip+arity.swift */,
+ F2F8D5322A85EF57009C4D21 /* SwitchIfEmpty.swift */,
+ F2F8D5332A85EF57009C4D21 /* ElementAt.swift */,
+ F2F8D5342A85EF57009C4D21 /* Create.swift */,
+ F2F8D5352A85EF57009C4D21 /* Materialize.swift */,
+ F2F8D5362A85EF57009C4D21 /* SingleAsync.swift */,
+ F2F8D5372A85EF57009C4D21 /* ImmediateSchedulerType.swift */,
+ );
+ path = RXSwift;
+ sourceTree = "<group>";
+ };
+ F2F8D5382A85EF57009C4D21 /* CryptoSwift */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D5392A85EF57009C4D21 /* RSA.swift */,
+ F2F8D53A2A85EF57009C4D21 /* AES.swift */,
+ F2F8D53B2A85EF57009C4D21 /* String+FoundationExtension.swift */,
+ F2F8D53C2A85EF57009C4D21 /* BlockModeOptions.swift */,
+ F2F8D53D2A85EF57009C4D21 /* StreamDecryptor.swift */,
+ F2F8D53E2A85EF57009C4D21 /* Random.swift */,
+ F2F8D53F2A85EF57009C4D21 /* GCD.swift */,
+ F2F8D5402A85EF57009C4D21 /* BlockEncryptor.swift */,
+ F2F8D5412A85EF57009C4D21 /* OCB.swift */,
+ F2F8D5422A85EF57009C4D21 /* Hashable.swift */,
+ F2F8D5432A85EF57009C4D21 /* UInt64+Extension.swift */,
+ F2F8D5442A85EF57009C4D21 /* Cryptors.swift */,
+ F2F8D5452A85EF57009C4D21 /* HKDF.swift */,
+ F2F8D5462A85EF57009C4D21 /* PKCS7Padding.swift */,
+ F2F8D5472A85EF57009C4D21 /* CFB.swift */,
+ F2F8D5482A85EF57009C4D21 /* ISO10126Padding.swift */,
+ F2F8D5492A85EF57009C4D21 /* Strideable.swift */,
+ F2F8D54A2A85EF57009C4D21 /* Cipher.swift */,
+ F2F8D54B2A85EF57009C4D21 /* Addition.swift */,
+ F2F8D54C2A85EF57009C4D21 /* Data Conversion.swift */,
+ F2F8D54D2A85EF57009C4D21 /* CBCMAC.swift */,
+ F2F8D54E2A85EF57009C4D21 /* Int+Extension.swift */,
+ F2F8D54F2A85EF57009C4D21 /* AES.Cryptors.swift */,
+ F2F8D5502A85EF57009C4D21 /* Bit.swift */,
+ F2F8D5512A85EF57009C4D21 /* HMAC.swift */,
+ F2F8D5522A85EF57009C4D21 /* CBC.swift */,
+ F2F8D5532A85EF57009C4D21 /* Bitwise Ops.swift */,
+ F2F8D5542A85EF57009C4D21 /* CMAC.swift */,
+ F2F8D5552A85EF57009C4D21 /* UInt16+Extension.swift */,
+ F2F8D5562A85EF57009C4D21 /* Comparable.swift */,
+ F2F8D5572A85EF57009C4D21 /* Collection+Extension.swift */,
+ F2F8D5582A85EF57009C4D21 /* UInt32+Extension.swift */,
+ F2F8D5592A85EF57009C4D21 /* Digest.swift */,
+ F2F8D55A2A85EF57009C4D21 /* UInt128.swift */,
+ F2F8D55B2A85EF57009C4D21 /* RSA+Signature.swift */,
+ F2F8D55C2A85EF57009C4D21 /* ECB.swift */,
+ F2F8D55D2A85EF57009C4D21 /* Words and Bits.swift */,
+ F2F8D55E2A85EF57009C4D21 /* CCM.swift */,
+ F2F8D55F2A85EF57009C4D21 /* Scrypt.swift */,
+ F2F8D5602A85EF57009C4D21 /* PCBC.swift */,
+ F2F8D5612A85EF57009C4D21 /* NoPadding.swift */,
+ F2F8D5622A85EF57009C4D21 /* UInt8+Extension.swift */,
+ F2F8D5632A85EF57009C4D21 /* MD5.swift */,
+ F2F8D5642A85EF57009C4D21 /* ASN1Scanner.swift */,
+ F2F8D5652A85EF57009C4D21 /* Cryptor.swift */,
+ F2F8D5662A85EF57009C4D21 /* Square Root.swift */,
+ F2F8D5672A85EF57009C4D21 /* BatchedCollection.swift */,
+ F2F8D5682A85EF57009C4D21 /* Blowfish+Foundation.swift */,
+ F2F8D5692A85EF57009C4D21 /* CSCompactMap.swift */,
+ F2F8D56A2A85EF57009C4D21 /* ISO78164Padding.swift */,
+ F2F8D56B2A85EF57009C4D21 /* SecureBytes.swift */,
+ F2F8D56C2A85EF57009C4D21 /* Signature.swift */,
+ F2F8D56D2A85EF57009C4D21 /* ZeroPadding.swift */,
+ F2F8D56E2A85EF57009C4D21 /* String+Extension.swift */,
+ F2F8D56F2A85EF57009C4D21 /* Authenticator.swift */,
+ F2F8D5702A85EF57009C4D21 /* SHA3.swift */,
+ F2F8D5712A85EF57009C4D21 /* Operators.swift */,
+ F2F8D5722A85EF57009C4D21 /* Division.swift */,
+ F2F8D5732A85EF57009C4D21 /* Utils+Foundation.swift */,
+ F2F8D5742A85EF57009C4D21 /* PKCS5.swift */,
+ F2F8D5752A85EF57009C4D21 /* Prime Test.swift */,
+ F2F8D5762A85EF57009C4D21 /* Codable.swift */,
+ F2F8D5772A85EF57009C4D21 /* BlockMode.swift */,
+ F2F8D5782A85EF57009C4D21 /* BigUInt.swift */,
+ F2F8D5792A85EF57009C4D21 /* Rabbit.swift */,
+ F2F8D57A2A85EF57009C4D21 /* PKCS7.swift */,
+ F2F8D57B2A85EF57009C4D21 /* SHA1.swift */,
+ F2F8D57C2A85EF57009C4D21 /* Poly1305.swift */,
+ F2F8D57D2A85EF57009C4D21 /* BlockCipher.swift */,
+ F2F8D57E2A85EF57009C4D21 /* GCM.swift */,
+ F2F8D57F2A85EF57009C4D21 /* PKCS1v15.swift */,
+ F2F8D5802A85EF57009C4D21 /* Updatable.swift */,
+ F2F8D5812A85EF57009C4D21 /* DER.swift */,
+ F2F8D5822A85EF57009C4D21 /* ASN1Encoder.swift */,
+ F2F8D5832A85EF57009C4D21 /* PBKDF2.swift */,
+ F2F8D5842A85EF57009C4D21 /* Blowfish.swift */,
+ F2F8D5852A85EF57009C4D21 /* BlockDecryptor.swift */,
+ F2F8D5862A85EF57009C4D21 /* HMAC+Foundation.swift */,
+ F2F8D5872A85EF57009C4D21 /* StreamEncryptor.swift */,
+ F2F8D5882A85EF57009C4D21 /* Multiplication.swift */,
+ F2F8D5892A85EF57009C4D21 /* CS.swift */,
+ F2F8D58A2A85EF57009C4D21 /* CipherModeWorker.swift */,
+ F2F8D58B2A85EF57009C4D21 /* Data+Extension.swift */,
+ F2F8D58C2A85EF57009C4D21 /* DigestType.swift */,
+ F2F8D58D2A85EF57009C4D21 /* ChaCha20+Foundation.swift */,
+ F2F8D58E2A85EF57009C4D21 /* Utils.swift */,
+ F2F8D58F2A85EF57009C4D21 /* Exponentiation.swift */,
+ F2F8D5902A85EF57009C4D21 /* CTR.swift */,
+ F2F8D5912A85EF57009C4D21 /* AEADChaCha20Poly1305.swift */,
+ F2F8D5922A85EF57009C4D21 /* Generics.swift */,
+ F2F8D5932A85EF57009C4D21 /* Checksum.swift */,
+ F2F8D5942A85EF57009C4D21 /* Floating Point Conversion.swift */,
+ F2F8D5952A85EF57009C4D21 /* Shifts.swift */,
+ F2F8D5962A85EF57009C4D21 /* SHA2.swift */,
+ F2F8D5972A85EF57009C4D21 /* Padding.swift */,
+ F2F8D5982A85EF57009C4D21 /* AEAD.swift */,
+ F2F8D5992A85EF57009C4D21 /* ASN1Decoder.swift */,
+ F2F8D59A2A85EF57009C4D21 /* Array+Foundation.swift */,
+ F2F8D59B2A85EF57009C4D21 /* PBKDF1.swift */,
+ F2F8D59C2A85EF57009C4D21 /* ChaCha20.swift */,
+ F2F8D59D2A85EF57009C4D21 /* Subtraction.swift */,
+ F2F8D59E2A85EF57009C4D21 /* Integer Conversion.swift */,
+ F2F8D59F2A85EF57009C4D21 /* BigInt.swift */,
+ F2F8D5A02A85EF57009C4D21 /* ASN1.swift */,
+ F2F8D5A12A85EF57009C4D21 /* AES+Foundation.swift */,
+ F2F8D5A22A85EF57009C4D21 /* Rabbit+Foundation.swift */,
+ F2F8D5A32A85EF57009C4D21 /* OFB.swift */,
+ F2F8D5A42A85EF57009C4D21 /* Array+Extension.swift */,
+ F2F8D5A52A85EF57009C4D21 /* String Conversion.swift */,
+ F2F8D5A62A85EF57009C4D21 /* RSA+Cipher.swift */,
+ );
+ path = CryptoSwift;
+ sourceTree = "<group>";
+ };
+ F2F8D5A72A85EF57009C4D21 /* SwiftyBeaver */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D5A82A85EF57009C4D21 /* SBFilter.swift */,
+ F2F8D5A92A85EF57009C4D21 /* FilterValidator.swift */,
+ F2F8D5AA2A85EF57009C4D21 /* Extensions.swift */,
+ F2F8D5AB2A85EF57009C4D21 /* FileDestination.swift */,
+ F2F8D5AC2A85EF57009C4D21 /* GoogleCloudDestination.swift */,
+ F2F8D5AD2A85EF57009C4D21 /* Base64.swift */,
+ F2F8D5AE2A85EF57009C4D21 /* SBPlatformDestination.swift */,
+ F2F8D5AF2A85EF57009C4D21 /* SwiftyBeaver.swift */,
+ F2F8D5B02A85EF57009C4D21 /* BaseDestination.swift */,
+ F2F8D5B12A85EF57009C4D21 /* AES256CBC.swift */,
+ F2F8D5B22A85EF57009C4D21 /* ConsoleDestination.swift */,
+ );
+ path = SwiftyBeaver;
+ sourceTree = "<group>";
+ };
+ F2F8D5B32A85EF57009C4D21 /* AFNetworking */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D5B42A85EF57009C4D21 /* AFSecurityPolicy.h */,
+ F2F8D5B52A85EF57009C4D21 /* AFNetworkReachabilityManager.h */,
+ F2F8D5B62A85EF57009C4D21 /* AFURLSessionManager.h */,
+ F2F8D5B72A85EF57009C4D21 /* AFURLRequestSerialization.h */,
+ F2F8D5B82A85EF57009C4D21 /* AFURLResponseSerialization.m */,
+ F2F8D5B92A85EF57009C4D21 /* AFHTTPSessionManager.m */,
+ F2F8D5BA2A85EF57009C4D21 /* AFURLResponseSerialization.h */,
+ F2F8D5BB2A85EF57009C4D21 /* AFURLSessionManager.m */,
+ F2F8D5BC2A85EF57009C4D21 /* AFURLRequestSerialization.m */,
+ F2F8D5BD2A85EF57009C4D21 /* AFNetworking.h */,
+ F2F8D5BE2A85EF57009C4D21 /* AFNetworkReachabilityManager.m */,
+ F2F8D5BF2A85EF57009C4D21 /* AFSecurityPolicy.m */,
+ F2F8D5C02A85EF57009C4D21 /* AFCompatibilityMacros.h */,
+ F2F8D5C12A85EF57009C4D21 /* AFHTTPSessionManager.h */,
+ );
+ path = AFNetworking;
+ sourceTree = "<group>";
+ };
+ F2F8D5E62A85EF58009C4D21 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ F2F8D5E72A85EF58009C4D21 /* AliCloudIotBleDelegate_C.swift */,
+ F2F8D5E82A85EF58009C4D21 /* AliCloudIotError_C.swift */,
+ F2F8D5E92A85EF58009C4D21 /* AliCloudIotSdkInitDelegate_C.swift */,
+ F2F8D5EA2A85EF58009C4D21 /* AliCloudIotQulianConfig_C.swift */,
+ F2F8D5EB2A85EF58009C4D21 /* AliCloudIotConnectionState.swift */,
+ F2F8D5EC2A85EF58009C4D21 /* AliCloudIotSdkConfigBase_C.swift */,
+ F2F8D5ED2A85EF58009C4D21 /* MqttState_C.swift */,
+ F2F8D5EE2A85EF58009C4D21 /* AliCloudIotDelegate_C.swift */,
+ F2F8D5EF2A85EF58009C4D21 /* AliCloudIotDirectConfig_C.swift */,
+ );
+ path = Model;
+ sourceTree = "<group>";
+ };
+ F2FB5A102AA0CE93001EEB99 /* IotRtc */ = {
+ isa = PBXGroup;
+ children = (
+ F2FB5A132AA0CE9F001EEB99 /* iotMqtt.h */,
+ F2FB5A1C2AA20062001EEB99 /* libiotRtc.a */,
+ );
+ path = IotRtc;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ F21215102A85EC2800BD4BEF /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F2F8D7302A85EF58009C4D21 /* AFURLSessionManager.h in Headers */,
+ 18DE22752B21A5490096A835 /* MqttHelper.h in Headers */,
+ F21215192A85EC2800BD4BEF /* AliIotConnectKit.h in Headers */,
+ F2F8D73B2A85EF58009C4D21 /* AFHTTPSessionManager.h in Headers */,
+ F2F8D73A2A85EF58009C4D21 /* AFCompatibilityMacros.h in Headers */,
+ F2FB5A152AA0CE9F001EEB99 /* iotMqtt.h in Headers */,
+ F2F8D5FD2A85EF58009C4D21 /* AFHttpHelper_C.h in Headers */,
+ F2F8D5FF2A85EF58009C4D21 /* MqttManager_C.h in Headers */,
+ F2F8D7312A85EF58009C4D21 /* AFURLRequestSerialization.h in Headers */,
+ F2F8D72E2A85EF58009C4D21 /* AFSecurityPolicy.h in Headers */,
+ F2F8D72F2A85EF58009C4D21 /* AFNetworkReachabilityManager.h in Headers */,
+ F2F8D7342A85EF58009C4D21 /* AFURLResponseSerialization.h in Headers */,
+ F2F8D7372A85EF58009C4D21 /* AFNetworking.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ F21215142A85EC2800BD4BEF /* AliIotConnectKit */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F212151C2A85EC2800BD4BEF /* Build configuration list for PBXNativeTarget "AliIotConnectKit" */;
+ buildPhases = (
+ F21215102A85EC2800BD4BEF /* Headers */,
+ F21215112A85EC2800BD4BEF /* Sources */,
+ F21215122A85EC2800BD4BEF /* Frameworks */,
+ F21215132A85EC2800BD4BEF /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = AliIotConnectKit;
+ productName = AliIotConnectKit;
+ productReference = F21215152A85EC2800BD4BEF /* AliIotConnectKit.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ F212150C2A85EC2800BD4BEF /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastUpgradeCheck = 1430;
+ TargetAttributes = {
+ F21215142A85EC2800BD4BEF = {
+ CreatedOnToolsVersion = 14.3.1;
+ };
+ };
+ };
+ buildConfigurationList = F212150F2A85EC2800BD4BEF /* Build configuration list for PBXProject "AliIotConnectKit" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = F212150B2A85EC2800BD4BEF;
+ productRefGroup = F21215162A85EC2800BD4BEF /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ F21215142A85EC2800BD4BEF /* AliIotConnectKit */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ F21215132A85EC2800BD4BEF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F2F8D5F02A85EF58009C4D21 /* srca.cer in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ F21215112A85EC2800BD4BEF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F2F8D61A2A85EF58009C4D21 /* BleAuthModel_C.swift in Sources */,
+ F2F8D6922A85EF58009C4D21 /* HistoricalSchedulerTimeConverter.swift in Sources */,
+ F2F8D64F2A85EF58009C4D21 /* NopDisposable.swift in Sources */,
+ F2F8D6BF2A85EF58009C4D21 /* UInt64+Extension.swift in Sources */,
+ F2F8D6402A85EF58009C4D21 /* GroupBy.swift in Sources */,
+ F2F8D5F22A85EF58009C4D21 /* BleService_C.swift in Sources */,
+ F2F8D6442A85EF58009C4D21 /* ReplaySubject.swift in Sources */,
+ F2F8D7152A85EF58009C4D21 /* ASN1Decoder.swift in Sources */,
+ F2F8D6D02A85EF58009C4D21 /* CMAC.swift in Sources */,
+ F2F8D6112A85EF58009C4D21 /* WearCommonCommand_C.swift in Sources */,
+ F2F8D67B2A85EF58009C4D21 /* ToArray.swift in Sources */,
+ F2F8D6392A85EF58009C4D21 /* DispatchQueue+Extensions.swift in Sources */,
+ F2F8D5F32A85EF58009C4D21 /* AliCloudIotAutomaticService_C.swift in Sources */,
+ F2F8D70B2A85EF58009C4D21 /* Exponentiation.swift in Sources */,
+ F2F8D6BE2A85EF58009C4D21 /* Hashable.swift in Sources */,
+ 18BF1E542B83095500B8D90F /* Logger.swift in Sources */,
+ F2F8D7172A85EF58009C4D21 /* PBKDF1.swift in Sources */,
+ F2F8D6622A85EF58009C4D21 /* Throttle.swift in Sources */,
+ F2F8D6862A85EF58009C4D21 /* AnonymousObserver.swift in Sources */,
+ F2F8D6352A85EF58009C4D21 /* SynchronizedOnType.swift in Sources */,
+ F2F8D6892A85EF58009C4D21 /* SingleAssignmentDisposable.swift in Sources */,
+ F2F8D5FE2A85EF58009C4D21 /* AFHttpHelper_C.m in Sources */,
+ 18BF1E4A2B83095500B8D90F /* Peripheral.swift in Sources */,
+ F2F8D6F12A85EF58009C4D21 /* Prime Test.swift in Sources */,
+ F2F8D6362A85EF58009C4D21 /* DefaultIfEmpty.swift in Sources */,
+ F2F8D69B2A85EF58009C4D21 /* Errors.swift in Sources */,
+ F2F8D5F82A85EF58009C4D21 /* LPConnectService_C.swift in Sources */,
+ 18BF1E652B83095500B8D90F /* ScannedPeripheral.swift in Sources */,
+ F2F8D70E2A85EF58009C4D21 /* Generics.swift in Sources */,
+ F2F8D5F42A85EF58009C4D21 /* BleBaseSendService_C.swift in Sources */,
+ F2F8D60F2A85EF58009C4D21 /* ThingPack_C.swift in Sources */,
+ F2F8D6672A85EF58009C4D21 /* RefCountDisposable.swift in Sources */,
+ F2F8D6A22A85EF58009C4D21 /* PrimitiveSequence+Zip+arity.swift in Sources */,
+ 18BF1E4C2B83095500B8D90F /* Logging.swift in Sources */,
+ 18DE22762B21A5490096A835 /* MqttHelper.mm in Sources */,
+ F2F8D6992A85EF58009C4D21 /* Merge.swift in Sources */,
+ F2F8D6DF2A85EF58009C4D21 /* MD5.swift in Sources */,
+ F2F8D6AB2A85EF58009C4D21 /* RecursiveLock.swift in Sources */,
+ F2F8D6E22A85EF58009C4D21 /* Square Root.swift in Sources */,
+ F2F8D6A92A85EF58009C4D21 /* ShareReplayScope.swift in Sources */,
+ F2F8D6082A85EF58009C4D21 /* SdkMqttDelegate_C.swift in Sources */,
+ F2F8D7022A85EF58009C4D21 /* HMAC+Foundation.swift in Sources */,
+ F2F8D6B72A85EF58009C4D21 /* String+FoundationExtension.swift in Sources */,
+ 18BF1E5C2B83095500B8D90F /* Descriptor.swift in Sources */,
+ F2F8D6BA2A85EF58009C4D21 /* Random.swift in Sources */,
+ F2F8D6C92A85EF58009C4D21 /* CBCMAC.swift in Sources */,
+ F2F8D68A2A85EF58009C4D21 /* Queue.swift in Sources */,
+ F2F8D65B2A85EF58009C4D21 /* Timer.swift in Sources */,
+ F2F8D6022A85EF58009C4D21 /* CommonConfig.swift in Sources */,
+ F2F8D6962A85EF58009C4D21 /* SchedulerType.swift in Sources */,
+ F2F8D6132A85EF58009C4D21 /* ThingType_C.swift in Sources */,
+ F2F8D75F2A85EF58009C4D21 /* AliCloudIotBleDelegate_C.swift in Sources */,
+ F2F8D6C22A85EF58009C4D21 /* PKCS7Padding.swift in Sources */,
+ F2F8D6712A85EF58009C4D21 /* TailRecursiveSink.swift in Sources */,
+ F2F8D7322A85EF58009C4D21 /* AFURLResponseSerialization.m in Sources */,
+ F2F8D70A2A85EF58009C4D21 /* Utils.swift in Sources */,
+ F2F8D6092A85EF58009C4D21 /* SdkMqttService_C.swift in Sources */,
+ F2F8D6B62A85EF58009C4D21 /* AES.swift in Sources */,
+ 18BF1E522B83095500B8D90F /* CBCentral+Uuid.swift in Sources */,
+ F2F8D6CE2A85EF58009C4D21 /* CBC.swift in Sources */,
+ F2F8D7082A85EF58009C4D21 /* DigestType.swift in Sources */,
+ F2F8D6472A85EF58009C4D21 /* MainScheduler.swift in Sources */,
+ 18BF1E572B83095500B8D90F /* PeripheralManagerRestoredState.swift in Sources */,
+ F2F8D7162A85EF58009C4D21 /* Array+Foundation.swift in Sources */,
+ F2F8D61B2A85EF58009C4D21 /* BleTypes.swift in Sources */,
+ F2F8D6462A85EF58009C4D21 /* Event.swift in Sources */,
+ F2F8D68B2A85EF58009C4D21 /* ObservableConvertibleType.swift in Sources */,
+ F2F8D6B42A85EF58009C4D21 /* ImmediateSchedulerType.swift in Sources */,
+ F2F8D7352A85EF58009C4D21 /* AFURLSessionManager.m in Sources */,
+ F2F8D6B82A85EF58009C4D21 /* BlockModeOptions.swift in Sources */,
+ F2F8D7382A85EF58009C4D21 /* AFNetworkReachabilityManager.m in Sources */,
+ F2F8D6FC2A85EF58009C4D21 /* Updatable.swift in Sources */,
+ F2F8D7392A85EF58009C4D21 /* AFSecurityPolicy.m in Sources */,
+ F2F8D6D32A85EF58009C4D21 /* Collection+Extension.swift in Sources */,
+ F2F8D7012A85EF58009C4D21 /* BlockDecryptor.swift in Sources */,
+ F2F8D71E2A85EF58009C4D21 /* Rabbit+Foundation.swift in Sources */,
+ F2F8D5F72A85EF58009C4D21 /* ID2Service_C.swift in Sources */,
+ F2F8D7072A85EF58009C4D21 /* Data+Extension.swift in Sources */,
+ F2F8D6D62A85EF58009C4D21 /* UInt128.swift in Sources */,
+ F2F8D6492A85EF58009C4D21 /* TakeWhile.swift in Sources */,
+ F2F8D5F62A85EF58009C4D21 /* RideYardService_C.swift in Sources */,
+ F2F8D6002A85EF58009C4D21 /* AESEncode_C.swift in Sources */,
+ F2F8D6C42A85EF58009C4D21 /* ISO10126Padding.swift in Sources */,
+ F2F8D67C2A85EF58009C4D21 /* TakeUntil.swift in Sources */,
+ 185C1B0D2B21549600695075 /* Data_category.swift in Sources */,
+ 18BF1E4D2B83095500B8D90F /* CBPeripheralDelegateWrapper.swift in Sources */,
+ F2F8D6642A85EF58009C4D21 /* CompactMap.swift in Sources */,
+ F2F8D6B92A85EF58009C4D21 /* StreamDecryptor.swift in Sources */,
+ F2F8D6A82A85EF58009C4D21 /* Generate.swift in Sources */,
+ F2F8D6252A85EF58009C4D21 /* Timeout.swift in Sources */,
+ F2F8D6912A85EF58009C4D21 /* CombineLatest+arity.swift in Sources */,
+ 18BF1E6A2B83095500B8D90F /* Unimplemented.swift in Sources */,
+ F2F8D6AC2A85EF58009C4D21 /* ScheduledItemType.swift in Sources */,
+ F2F8D64A2A85EF58009C4D21 /* TakeLast.swift in Sources */,
+ 18BF1E502B83095500B8D90F /* DeviceIdentifiers.swift in Sources */,
+ F2F8D60A2A85EF58009C4D21 /* HttpTool_C.swift in Sources */,
+ F2F8D69E2A85EF58009C4D21 /* Map.swift in Sources */,
+ F2F8D6302A85EF58009C4D21 /* Platform.Darwin.swift in Sources */,
+ F2F8D6652A85EF58009C4D21 /* Disposable.swift in Sources */,
+ F2F8D63A2A85EF58009C4D21 /* DisposeBag.swift in Sources */,
+ F2F8D5FB2A85EF58009C4D21 /* AliCloudIotWatchManager_C.swift in Sources */,
+ F2F8D5FC2A85EF58009C4D21 /* MqttManager_C.m in Sources */,
+ F2F8D7332A85EF58009C4D21 /* AFHTTPSessionManager.m in Sources */,
+ F2F8D7362A85EF58009C4D21 /* AFURLRequestSerialization.m in Sources */,
+ F2F8D62A2A85EF58009C4D21 /* RetryWhen.swift in Sources */,
+ F2F8D66D2A85EF58009C4D21 /* ConcurrentMainScheduler.swift in Sources */,
+ F2F8D6E62A85EF58009C4D21 /* ISO78164Padding.swift in Sources */,
+ F2F8D6812A85EF58009C4D21 /* Dematerialize.swift in Sources */,
+ F2F8D6FB2A85EF58009C4D21 /* PKCS1v15.swift in Sources */,
+ F2F8D70D2A85EF58009C4D21 /* AEADChaCha20Poly1305.swift in Sources */,
+ F2F8D6BB2A85EF58009C4D21 /* GCD.swift in Sources */,
+ F2F8D62E2A85EF58009C4D21 /* Bag+Rx.swift in Sources */,
+ F2F8D6B22A85EF58009C4D21 /* Materialize.swift in Sources */,
+ F2F8D6932A85EF58009C4D21 /* Observable.swift in Sources */,
+ F2F8D6CC2A85EF58009C4D21 /* Bit.swift in Sources */,
+ F2F8D7662A85EF58009C4D21 /* AliCloudIotDelegate_C.swift in Sources */,
+ F2F8D7262A85EF58009C4D21 /* FileDestination.swift in Sources */,
+ F2F8D70C2A85EF58009C4D21 /* CTR.swift in Sources */,
+ 18BF1E692B83095500B8D90F /* Characteristic.swift in Sources */,
+ F2F8D6452A85EF58009C4D21 /* ScheduledDisposable.swift in Sources */,
+ F2F8D6D72A85EF58009C4D21 /* RSA+Signature.swift in Sources */,
+ F2F8D6742A85EF58009C4D21 /* HistoricalScheduler.swift in Sources */,
+ F2F8D69A2A85EF58009C4D21 /* ObservableType.swift in Sources */,
+ F2F8D6BC2A85EF58009C4D21 /* BlockEncryptor.swift in Sources */,
+ F2F8D6E42A85EF58009C4D21 /* Blowfish+Foundation.swift in Sources */,
+ F2F8D6A12A85EF58009C4D21 /* InfiniteSequence.swift in Sources */,
+ F2F8D7182A85EF58009C4D21 /* ChaCha20.swift in Sources */,
+ F2F8D6E72A85EF58009C4D21 /* SecureBytes.swift in Sources */,
+ F2F8D7272A85EF58009C4D21 /* GoogleCloudDestination.swift in Sources */,
+ F2F8D67F2A85EF58009C4D21 /* Window.swift in Sources */,
+ F2F8D6882A85EF58009C4D21 /* PriorityQueue.swift in Sources */,
+ F2F8D67E2A85EF58009C4D21 /* Switch.swift in Sources */,
+ F2F8D7682A85F329009C4D21 /* AliConnectMananger_C.swift in Sources */,
+ F2F8D7212A85EF58009C4D21 /* String Conversion.swift in Sources */,
+ 18BF1E5E2B83095500B8D90F /* CentralManager+RestoredState.swift in Sources */,
+ F2F8D7672A85EF58009C4D21 /* AliCloudIotDirectConfig_C.swift in Sources */,
+ F2F8D5F52A85EF58009C4D21 /* IotService_C.swift in Sources */,
+ F2F8D7042A85EF58009C4D21 /* Multiplication.swift in Sources */,
+ F2F8D66A2A85EF58009C4D21 /* CombineLatest+Collection.swift in Sources */,
+ F2F8D6192A85EF58009C4D21 /* BleSIdentifier_C.swift in Sources */,
+ F2F8D6282A85EF58009C4D21 /* Completable+AndThen.swift in Sources */,
+ F2F8D6C02A85EF58009C4D21 /* Cryptors.swift in Sources */,
+ F2F8D6602A85EF58009C4D21 /* Sink.swift in Sources */,
+ F2F8D6D22A85EF58009C4D21 /* Comparable.swift in Sources */,
+ F2F8D6F32A85EF58009C4D21 /* BlockMode.swift in Sources */,
+ F2F8D6752A85EF58009C4D21 /* Bag.swift in Sources */,
+ F2F8D6B32A85EF58009C4D21 /* SingleAsync.swift in Sources */,
+ F2F8D6C12A85EF58009C4D21 /* HKDF.swift in Sources */,
+ F2F8D7112A85EF58009C4D21 /* Shifts.swift in Sources */,
+ 18BF1E672B83095500B8D90F /* PeripheralManager.swift in Sources */,
+ F2F8D6A72A85EF58009C4D21 /* Optional.swift in Sources */,
+ 18BF1E632B83095500B8D90F /* UUIDIdentifiable.swift in Sources */,
+ F2F8D6AD2A85EF58009C4D21 /* Scan.swift in Sources */,
+ F2F8D69C2A85EF58009C4D21 /* Rx.swift in Sources */,
+ F2F8D6702A85EF58009C4D21 /* ObserverType.swift in Sources */,
+ F2F8D72D2A85EF58009C4D21 /* ConsoleDestination.swift in Sources */,
+ F2F8D6D42A85EF58009C4D21 /* UInt32+Extension.swift in Sources */,
+ F2F8D70F2A85EF58009C4D21 /* Checksum.swift in Sources */,
+ 18BF1E662B83095500B8D90F /* ManagerType.swift in Sources */,
+ F2F8D6CD2A85EF58009C4D21 /* HMAC.swift in Sources */,
+ F2F8D6ED2A85EF58009C4D21 /* Operators.swift in Sources */,
+ F2F8D6242A85EF58009C4D21 /* GroupedObservable.swift in Sources */,
+ F2F8D7142A85EF58009C4D21 /* AEAD.swift in Sources */,
+ F2F8D65C2A85EF58009C4D21 /* Single.swift in Sources */,
+ F2F8D7242A85EF58009C4D21 /* FilterValidator.swift in Sources */,
+ F2F8D6A52A85EF58009C4D21 /* Just.swift in Sources */,
+ F2F8D6632A85EF58009C4D21 /* Range.swift in Sources */,
+ F2F8D6AA2A85EF58009C4D21 /* Disposables.swift in Sources */,
+ F2F8D6502A85EF58009C4D21 /* Sample.swift in Sources */,
+ F2F8D6332A85EF58009C4D21 /* SubscribeOn.swift in Sources */,
+ F2F8D6EA2A85EF58009C4D21 /* String+Extension.swift in Sources */,
+ F2F8D6052A85EF58009C4D21 /* IotMqttManager_C.swift in Sources */,
+ F2F8D6EE2A85EF58009C4D21 /* Division.swift in Sources */,
+ F2F8D6102A85EF58009C4D21 /* CommandIdentifier_C.swift in Sources */,
+ F2F8D6372A85EF58009C4D21 /* Using.swift in Sources */,
+ F2F8D6032A85EF58009C4D21 /* IDGenerater_C.swift in Sources */,
+ F2F8D6DA2A85EF58009C4D21 /* CCM.swift in Sources */,
+ F2F8D6272A85EF58009C4D21 /* Never.swift in Sources */,
+ F2F8D6C72A85EF58009C4D21 /* Addition.swift in Sources */,
+ F2F8D6592A85EF58009C4D21 /* First.swift in Sources */,
+ F2F8D6E92A85EF58009C4D21 /* ZeroPadding.swift in Sources */,
+ F2F8D6C82A85EF58009C4D21 /* Data Conversion.swift in Sources */,
+ F2F8D6AF2A85EF58009C4D21 /* SwitchIfEmpty.swift in Sources */,
+ F2F8D71F2A85EF58009C4D21 /* OFB.swift in Sources */,
+ 18BF1E5B2B83095500B8D90F /* RestoredState.swift in Sources */,
+ F2F8D7612A85EF58009C4D21 /* AliCloudIotSdkInitDelegate_C.swift in Sources */,
+ 18BF1E582B83095500B8D90F /* RxBluetoothKitLogger.swift in Sources */,
+ F2F8D6E82A85EF58009C4D21 /* Signature.swift in Sources */,
+ F2F8D6CF2A85EF58009C4D21 /* Bitwise Ops.swift in Sources */,
+ F2F8D6782A85EF58009C4D21 /* Reactive.swift in Sources */,
+ F2F8D6D92A85EF58009C4D21 /* Words and Bits.swift in Sources */,
+ F2F8D64B2A85EF58009C4D21 /* Error.swift in Sources */,
+ 18BF1E492B83095500B8D90F /* BluetoothState.swift in Sources */,
+ F2F8D67A2A85EF58009C4D21 /* Date+Dispatch.swift in Sources */,
+ F2F8D6902A85EF58009C4D21 /* VirtualTimeConverterType.swift in Sources */,
+ F2F8D6312A85EF58009C4D21 /* SynchronizedDisposeType.swift in Sources */,
+ F2F8D6562A85EF58009C4D21 /* DistinctUntilChanged.swift in Sources */,
+ F2F8D6432A85EF58009C4D21 /* Concat.swift in Sources */,
+ F2F8D6BD2A85EF58009C4D21 /* OCB.swift in Sources */,
+ F2F8D6A42A85EF58009C4D21 /* StartWith.swift in Sources */,
+ F2F8D68E2A85EF58009C4D21 /* Amb.swift in Sources */,
+ F2F8D6E12A85EF58009C4D21 /* Cryptor.swift in Sources */,
+ F2F8D69F2A85EF58009C4D21 /* BinaryDisposable.swift in Sources */,
+ F2F8D7232A85EF58009C4D21 /* SBFilter.swift in Sources */,
+ F2F8D5F12A85EF58009C4D21 /* FGSCheckService_C.swift in Sources */,
+ F2F8D6162A85EF58009C4D21 /* BleManager_C.swift in Sources */,
+ F2F8D65E2A85EF58009C4D21 /* Sequence.swift in Sources */,
+ F2F8D6552A85EF58009C4D21 /* Enumerated.swift in Sources */,
+ F2F8D7602A85EF58009C4D21 /* AliCloudIotError_C.swift in Sources */,
+ F2F8D6D12A85EF58009C4D21 /* UInt16+Extension.swift in Sources */,
+ F2F8D6CA2A85EF58009C4D21 /* Int+Extension.swift in Sources */,
+ F2F8D62F2A85EF58009C4D21 /* LockOwnerType.swift in Sources */,
+ 18BF1E612B83095500B8D90F /* Observable+Absorb.swift in Sources */,
+ 18BF1E622B83095500B8D90F /* Array+Utils.swift in Sources */,
+ F2F8D6C62A85EF58009C4D21 /* Cipher.swift in Sources */,
+ F2F8D7002A85EF58009C4D21 /* Blowfish.swift in Sources */,
+ F2F8D60E2A85EF58009C4D21 /* ReplyPack_C.swift in Sources */,
+ F2F8D6DE2A85EF58009C4D21 /* UInt8+Extension.swift in Sources */,
+ F2F8D6542A85EF58009C4D21 /* Empty.swift in Sources */,
+ F2F8D6E52A85EF58009C4D21 /* CSCompactMap.swift in Sources */,
+ F2F8D6522A85EF58009C4D21 /* AsSingle.swift in Sources */,
+ F2F8D72A2A85EF58009C4D21 /* SwiftyBeaver.swift in Sources */,
+ F2F8D6B12A85EF58009C4D21 /* Create.swift in Sources */,
+ F2F8D72C2A85EF58009C4D21 /* AES256CBC.swift in Sources */,
+ F2F8D63E2A85EF58009C4D21 /* AtomicInt.swift in Sources */,
+ F2F8D7632A85EF58009C4D21 /* AliCloudIotConnectionState.swift in Sources */,
+ F2F8D6012A85EF58009C4D21 /* HMACTool.swift in Sources */,
+ F2F8D6872A85EF58009C4D21 /* SwiftSupport.swift in Sources */,
+ F2F8D62B2A85EF58009C4D21 /* Do.swift in Sources */,
+ F2F8D68F2A85EF58009C4D21 /* Reduce.swift in Sources */,
+ F2F8D6582A85EF58009C4D21 /* ConcurrentDispatchQueueScheduler.swift in Sources */,
+ F2F8D64C2A85EF58009C4D21 /* PublishSubject.swift in Sources */,
+ F2F8D6152A85EF58009C4D21 /* BleExtension.swift in Sources */,
+ F2F8D6982A85EF58009C4D21 /* ConnectableObservableType.swift in Sources */,
+ F2F8D62D2A85EF58009C4D21 /* Deferred.swift in Sources */,
+ F2F8D66B2A85EF58009C4D21 /* ObservableType+Extensions.swift in Sources */,
+ F2F8D63D2A85EF58009C4D21 /* Delay.swift in Sources */,
+ F2F8D6222A85EF58009C4D21 /* Cancelable.swift in Sources */,
+ F2F8D6072A85EF58009C4D21 /* IotStateDelatege_C.swift in Sources */,
+ F2F8D6422A85EF58009C4D21 /* RxMutableBox.swift in Sources */,
+ F2F8D7252A85EF58009C4D21 /* Extensions.swift in Sources */,
+ F2F8D6D52A85EF58009C4D21 /* Digest.swift in Sources */,
+ F2F8D60D2A85EF58009C4D21 /* EventIdentifier_C.swift in Sources */,
+ F2F8D6C32A85EF58009C4D21 /* CFB.swift in Sources */,
+ F2F8D6B52A85EF58009C4D21 /* RSA.swift in Sources */,
+ F2F8D7132A85EF58009C4D21 /* Padding.swift in Sources */,
+ F2F8D6412A85EF58009C4D21 /* InvocableType.swift in Sources */,
+ 18BF1E602B83095500B8D90F /* CentralManager.swift in Sources */,
+ F2F8D6F82A85EF58009C4D21 /* Poly1305.swift in Sources */,
+ F2F8D6062A85EF58009C4D21 /* IotMqttLegacyConfig_C.swift in Sources */,
+ F2F8D6682A85EF58009C4D21 /* WithLatestFrom.swift in Sources */,
+ F2F8D6952A85EF58009C4D21 /* Debug.swift in Sources */,
+ F2F8D6832A85EF58009C4D21 /* Zip+Collection.swift in Sources */,
+ F2F8D6E02A85EF58009C4D21 /* ASN1Scanner.swift in Sources */,
+ F2F8D71A2A85EF58009C4D21 /* Integer Conversion.swift in Sources */,
+ F2F8D65F2A85EF58009C4D21 /* ObserverBase.swift in Sources */,
+ F2F8D7622A85EF58009C4D21 /* AliCloudIotQulianConfig_C.swift in Sources */,
+ F2F8D69D2A85EF58009C4D21 /* Skip.swift in Sources */,
+ F2F8D66C2A85EF58009C4D21 /* CompositeDisposable.swift in Sources */,
+ 18BF1E4F2B83095500B8D90F /* CBPeripheral+Uuid.swift in Sources */,
+ F2F8D6142A85EF58009C4D21 /* BleNeedSendDataDelegate_C.swift in Sources */,
+ F2F8D6972A85EF58009C4D21 /* SerialDisposable.swift in Sources */,
+ 185C1B0B2B21547C00695075 /* MqttFileManager.swift in Sources */,
+ F2F8D6FE2A85EF58009C4D21 /* ASN1Encoder.swift in Sources */,
+ F2F8D7202A85EF58009C4D21 /* Array+Extension.swift in Sources */,
+ F2F8D6722A85EF58009C4D21 /* SerialDispatchQueueScheduler.swift in Sources */,
+ F2F8D6DD2A85EF58009C4D21 /* NoPadding.swift in Sources */,
+ F2F8D6DC2A85EF58009C4D21 /* PCBC.swift in Sources */,
+ F2F8D6EF2A85EF58009C4D21 /* Utils+Foundation.swift in Sources */,
+ F2F8D6292A85EF58009C4D21 /* BehaviorSubject.swift in Sources */,
+ F2F8D7122A85EF58009C4D21 /* SHA2.swift in Sources */,
+ F2F8D68C2A85EF58009C4D21 /* Filter.swift in Sources */,
+ F2F8D6EB2A85EF58009C4D21 /* Authenticator.swift in Sources */,
+ F2F8D6EC2A85EF58009C4D21 /* SHA3.swift in Sources */,
+ F2F8D7192A85EF58009C4D21 /* Subtraction.swift in Sources */,
+ F2F8D64D2A85EF58009C4D21 /* InvocableScheduledItem.swift in Sources */,
+ F2F8D7292A85EF58009C4D21 /* SBPlatformDestination.swift in Sources */,
+ 18BF1E512B83095500B8D90F /* Service.swift in Sources */,
+ 18BF1E682B83095500B8D90F /* StartAdvertisingResult.swift in Sources */,
+ F2F8D6852A85EF58009C4D21 /* Catch.swift in Sources */,
+ F2F8D64E2A85EF58009C4D21 /* CombineLatest.swift in Sources */,
+ F2F8D6042A85EF58009C4D21 /* MyExtension.swift in Sources */,
+ F2F8D60C2A85EF58009C4D21 /* WearCommonEvent_C.swift in Sources */,
+ 18BF1E532B83095500B8D90F /* BluetoothError.swift in Sources */,
+ F2F8D6F92A85EF58009C4D21 /* BlockCipher.swift in Sources */,
+ F2F8D6572A85EF58009C4D21 /* SubscriptionDisposable.swift in Sources */,
+ F2F8D6792A85EF58009C4D21 /* Maybe.swift in Sources */,
+ F2F8D65D2A85EF58009C4D21 /* AddRef.swift in Sources */,
+ F2F8D6A62A85EF58009C4D21 /* BooleanDisposable.swift in Sources */,
+ F2F8D71D2A85EF58009C4D21 /* AES+Foundation.swift in Sources */,
+ F2F8D6842A85EF58009C4D21 /* Deprecated.swift in Sources */,
+ 18BF1E5F2B83095500B8D90F /* Boxes.swift in Sources */,
+ F2F8D72B2A85EF58009C4D21 /* BaseDestination.swift in Sources */,
+ F2F8D6612A85EF58009C4D21 /* AsyncLock.swift in Sources */,
+ F2F8D6FD2A85EF58009C4D21 /* DER.swift in Sources */,
+ F2F8D7642A85EF58009C4D21 /* AliCloudIotSdkConfigBase_C.swift in Sources */,
+ F2F8D6662A85EF58009C4D21 /* PrimitiveSequence.swift in Sources */,
+ F2F8D63B2A85EF58009C4D21 /* SchedulerServices+Emulation.swift in Sources */,
+ F2F8D6532A85EF58009C4D21 /* Completable.swift in Sources */,
+ F2F8D6FF2A85EF58009C4D21 /* PBKDF2.swift in Sources */,
+ F2F8D6F72A85EF58009C4D21 /* SHA1.swift in Sources */,
+ F2F8D7062A85EF58009C4D21 /* CipherModeWorker.swift in Sources */,
+ F2F8D71C2A85EF58009C4D21 /* ASN1.swift in Sources */,
+ F2F8D6F42A85EF58009C4D21 /* BigUInt.swift in Sources */,
+ F2F8D65A2A85EF58009C4D21 /* Lock.swift in Sources */,
+ F2F8D6322A85EF58009C4D21 /* Buffer.swift in Sources */,
+ F2F8D6C52A85EF58009C4D21 /* Strideable.swift in Sources */,
+ F2F8D6822A85EF58009C4D21 /* SynchronizedUnsubscribeType.swift in Sources */,
+ F2F8D6262A85EF58009C4D21 /* RecursiveScheduler.swift in Sources */,
+ F2F8D7222A85EF58009C4D21 /* RSA+Cipher.swift in Sources */,
+ 18BF1E592B83095500B8D90F /* PeripheralProvider.swift in Sources */,
+ F2F8D6182A85EF58009C4D21 /* BleDeviceModel_C.swift in Sources */,
+ F2F8D6342A85EF58009C4D21 /* VirtualTimeScheduler.swift in Sources */,
+ F2F8D61F2A85EF58009C4D21 /* AsMaybe.swift in Sources */,
+ F2F8D7102A85EF58009C4D21 /* Floating Point Conversion.swift in Sources */,
+ F2F8D68D2A85EF58009C4D21 /* ObservableType+PrimitiveSequence.swift in Sources */,
+ F2F8D6172A85EF58009C4D21 /* DeviceServiceCharacteristic.swift in Sources */,
+ 18BF1E4B2B83095500B8D90F /* PeripheralManager+RestoredState.swift in Sources */,
+ F2F8D6F52A85EF58009C4D21 /* Rabbit.swift in Sources */,
+ F2F8D6122A85EF58009C4D21 /* FGSMsgType_C.swift in Sources */,
+ F2F8D6F62A85EF58009C4D21 /* PKCS7.swift in Sources */,
+ 18BF1E5A2B83095500B8D90F /* CBCentralManagerDelegateWrapper.swift in Sources */,
+ F2F8D6D82A85EF58009C4D21 /* ECB.swift in Sources */,
+ F2F8D6CB2A85EF58009C4D21 /* AES.Cryptors.swift in Sources */,
+ F2F8D6692A85EF58009C4D21 /* ObserveOn.swift in Sources */,
+ F2F8D6A02A85EF58009C4D21 /* SkipUntil.swift in Sources */,
+ F2F8D67D2A85EF58009C4D21 /* DispatchQueueConfiguration.swift in Sources */,
+ F2F8D6482A85EF58009C4D21 /* Take.swift in Sources */,
+ F2F8D7692A85F32C009C4D21 /* AliCloudIotLogManager_C.swift in Sources */,
+ F2F8D6E32A85EF58009C4D21 /* BatchedCollection.swift in Sources */,
+ F2F8D7092A85EF58009C4D21 /* ChaCha20+Foundation.swift in Sources */,
+ F2F8D60B2A85EF58009C4D21 /* ThingCommunicateService_C.swift in Sources */,
+ F2F8D6802A85EF58009C4D21 /* Debounce.swift in Sources */,
+ F2F8D6DB2A85EF58009C4D21 /* Scrypt.swift in Sources */,
+ F2F8D6512A85EF58009C4D21 /* AnyObserver.swift in Sources */,
+ F2F8D6202A85EF58009C4D21 /* SubjectType.swift in Sources */,
+ F2F8D63C2A85EF58009C4D21 /* CurrentThreadScheduler.swift in Sources */,
+ F2F8D6AE2A85EF58009C4D21 /* Zip+arity.swift in Sources */,
+ F2F8D6F22A85EF58009C4D21 /* Codable.swift in Sources */,
+ 18BF1E642B83095500B8D90F /* Connector.swift in Sources */,
+ 18BF1E562B83095500B8D90F /* AdvertisementData.swift in Sources */,
+ F2F8D6732A85EF58009C4D21 /* DelaySubscription.swift in Sources */,
+ F2F8D6382A85EF58009C4D21 /* AsyncSubject.swift in Sources */,
+ F2F8D66E2A85EF58009C4D21 /* Multicast.swift in Sources */,
+ 18BF1E552B83095500B8D90F /* Peripheral+Convenience.swift in Sources */,
+ F2F8D71B2A85EF58009C4D21 /* BigInt.swift in Sources */,
+ F2F8D7032A85EF58009C4D21 /* StreamEncryptor.swift in Sources */,
+ F2F8D66F2A85EF58009C4D21 /* SkipWhile.swift in Sources */,
+ 18BF1E5D2B83095500B8D90F /* CharacteristicNotificationManager.swift in Sources */,
+ F2F8D6B02A85EF58009C4D21 /* ElementAt.swift in Sources */,
+ F2F8D6F02A85EF58009C4D21 /* PKCS5.swift in Sources */,
+ F2F8D6212A85EF58009C4D21 /* DisposeBase.swift in Sources */,
+ F2F8D6942A85EF58009C4D21 /* Zip.swift in Sources */,
+ F2F8D6772A85EF58009C4D21 /* Platform.Linux.swift in Sources */,
+ F2F8D6762A85EF58009C4D21 /* Repeat.swift in Sources */,
+ F2F8D7052A85EF58009C4D21 /* CS.swift in Sources */,
+ 18BF1E482B83095500B8D90F /* CentralManagerRestoredState.swift in Sources */,
+ F2F8D7282A85EF58009C4D21 /* Base64.swift in Sources */,
+ F2F8D6FA2A85EF58009C4D21 /* GCM.swift in Sources */,
+ F2F8D7652A85EF58009C4D21 /* MqttState_C.swift in Sources */,
+ F2F8D6A32A85EF58009C4D21 /* AnonymousDisposable.swift in Sources */,
+ F2F8D6232A85EF58009C4D21 /* ScheduledItem.swift in Sources */,
+ F2F8D63F2A85EF58009C4D21 /* Producer.swift in Sources */,
+ F2F8D62C2A85EF58009C4D21 /* OperationQueueScheduler.swift in Sources */,
+ F2F8D61C2A85EF58009C4D21 /* COOKSwiftyJSON.swift in Sources */,
+ 18BF1E4E2B83095500B8D90F /* CBPeripheralManagerDelegateWrapper.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ F212151A2A85EC2800BD4BEF /* 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;
+ CURRENT_PROJECT_VERSION = 1;
+ 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;
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ 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";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ F212151B2A85EC2800BD4BEF /* 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;
+ CURRENT_PROJECT_VERSION = 1;
+ 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;
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ F212151D2A85EC2800BD4BEF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLY_RULES_IN_COPY_HEADERS = NO;
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = M33X987285;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = (
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib\"",
+ "\"$(SRCROOT)/AliIotConnectKit/OC\"",
+ );
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib\"",
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib/IotMqtt\"",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotMqtt",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotMqtt/IotRtc",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotRtc",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_FILE = "";
+ MODULEMAP_PRIVATE_FILE = AliIotConnectKit/AliIotConnect.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = Cookies.AliIotConnectKit;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = macosx;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ F212151E2A85EC2800BD4BEF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ APPLY_RULES_IN_COPY_HEADERS = NO;
+ BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = M33X987285;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = (
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib\"",
+ "\"$(SRCROOT)/AliIotConnectKit/OC\"",
+ );
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib\"",
+ "\"$(SRCROOT)/../Demo/AliIotConnect/ThirdLib/IotMqtt\"",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotMqtt",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotMqtt/IotRtc",
+ "$(PROJECT_DIR)/AliIotConnectKit/ThirdLib/IotRtc",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MARKETING_VERSION = 1.0;
+ MODULEMAP_FILE = "";
+ MODULEMAP_PRIVATE_FILE = AliIotConnectKit/AliIotConnect.modulemap;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
+ PRODUCT_BUNDLE_IDENTIFIER = Cookies.AliIotConnectKit;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = macosx;
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ F212150F2A85EC2800BD4BEF /* Build configuration list for PBXProject "AliIotConnectKit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F212151A2A85EC2800BD4BEF /* Debug */,
+ F212151B2A85EC2800BD4BEF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F212151C2A85EC2800BD4BEF /* Build configuration list for PBXNativeTarget "AliIotConnectKit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F212151D2A85EC2800BD4BEF /* Debug */,
+ F212151E2A85EC2800BD4BEF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = F212150C2A85EC2800BD4BEF /* Project object */;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array/>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1430"
+ version = "1.7">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "F21215142A85EC2800BD4BEF"
+ BuildableName = "AliIotConnectKit.framework"
+ BlueprintName = "AliIotConnectKit"
+ ReferencedContainer = "container:AliIotConnectKit.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ shouldAutocreateTestPlan = "YES">
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Release"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "F21215142A85EC2800BD4BEF"
+ BuildableName = "AliIotConnectKit.framework"
+ BlueprintName = "AliIotConnectKit"
+ ReferencedContainer = "container:AliIotConnectKit.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+ uuid = "5BC8000C-BD25-44E4-9EED-AD21B45F81C4"
+ type = "1"
+ version = "2.0">
+</Bucket>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SchemeUserState</key>
+ <dict>
+ <key>AliIotConnectKit.xcscheme_^#shared#^_</key>
+ <dict>
+ <key>orderHint</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SchemeUserState</key>
+ <dict>
+ <key>AliIotConnectKit.xcscheme_^#shared#^_</key>
+ <dict>
+ <key>orderHint</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>F21215142A85EC2800BD4BEF</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+ framework module AliIotConnectKit_Private {
+ module OC {
+ private header "iotMqtt.h"
+ private header "AFHttpHelper_C.h"
+ private header "MqttManager_C.h"
+
+// export *
+ }
+// umbrella "ThirdLib"
+// module * {export *}
+}
+
--- /dev/null
+//
+// AliIotConnectKit.h
+// AliIotConnectKit
+//
+// Created by cookies on 2023/8/11.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for AliIotConnectKit.
+FOUNDATION_EXPORT double AliIotConnectKitVersionNumber;
+
+//! Project version string for AliIotConnectKit.
+FOUNDATION_EXPORT const unsigned char AliIotConnectKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <AliIotConnectKit/PublicHeader.h>
+
+
--- /dev/null
+//
+// BleAuthModel.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/26.
+//
+
+import Foundation
+struct BleAuthModel_C: Codable {
+ var productKey: String//产品秘钥
+ var deviceName: String//设备名称
+ var timeStamp: String//时间戳
+ var signmethod: String//签名方法
+ var sign: String //签名
+ var sonce: String//用于签名的随机盐值
+}
--- /dev/null
+//\r
+// BleDeviceModel.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+\r
+struct BleDeviceModel_C: Codable {\r
+ var mac: String\r
+ \r
+ init(mac: String) {\r
+ self.mac = mac\r
+ }\r
+}\r
+\r
+extension BleDeviceModel_C: Equatable {\r
+ static func == (lhs: BleDeviceModel_C, rhs: BleDeviceModel_C) -> Bool {\r
+ return lhs.mac == rhs.mac\r
+ }\r
+}\r
--- /dev/null
+//\r
+// BleExtension.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+/// import RXBluetoothKit\r
+///// import SwiftyJSON\r
+/// import RXSWift\r
+\r
+typealias Byte = UInt8\r
+\r
+\r
+extension ScannedPeripheral: Equatable {\r
+ public static func == (lhs: ScannedPeripheral, rhs: ScannedPeripheral) -> Bool {\r
+ return lhs.peripheral == rhs.peripheral\r
+ }\r
+}\r
+\r
+extension Data {\r
+ init?(randomOfLength length: Int) {\r
+ var bytes = [UInt8](repeating: 0, count: length)\r
+ let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)\r
+ if status == errSecSuccess {\r
+ self.init(bytes)\r
+ } else {\r
+ return nil\r
+ }\r
+ }\r
+ \r
+ // Convert data to hex String, with or without separator\r
+ public func hexString(uppercase: Bool = true, separator: String = "") -> String {\r
+ let format = uppercase ? "%02hhX" : "%02hhx"\r
+ return self.map { String(format: format, $0) }.joined(separator: separator)\r
+ }\r
+ \r
+ var byte: Byte {\r
+ get {\r
+ var number: UInt8 = 0\r
+ self.copyBytes(to:&number, count: MemoryLayout<UInt8>.size)\r
+ return number\r
+ }\r
+ }\r
+ \r
+ var uint16: UInt16 {\r
+ get {\r
+ let i16array = Data(self).withUnsafeBytes { $0.load(as: UInt16.self) }\r
+ return i16array\r
+ }\r
+ }\r
+ \r
+ var uint32: UInt32 {\r
+ get {\r
+ let i32array = Data(self).withUnsafeBytes { $0.load(as: UInt32.self) }\r
+ return i32array\r
+ }\r
+ }\r
+ \r
+ \r
+ var int16: Int16 {\r
+ get {\r
+ let i16array = Data(self).withUnsafeBytes { $0.load(as: Int16.self) }\r
+ return i16array\r
+ }\r
+ }\r
+ \r
+ var int32: Int32 {\r
+ get {\r
+ let i32array = Data(self).withUnsafeBytes { $0.load(as: Int32.self) }\r
+ return i32array\r
+ }\r
+ }\r
+ \r
+ var float: Float {\r
+ get {\r
+ let floatArray = Data(self).withUnsafeBytes { $0.load(as: Float.self) }\r
+ return floatArray\r
+ }\r
+ }\r
+ \r
+ var double: Double {\r
+ get {\r
+ let doubleArray = Data(self).withUnsafeBytes { $0.load(as: Double.self) }\r
+ return doubleArray\r
+ }\r
+ }\r
+ \r
+ var reversedBytes: [UInt8] {\r
+ var values = [UInt8](repeating: 0, count: count)\r
+ copyBytes(to: &values, count: count)\r
+ return values.reversed()\r
+ }\r
+ \r
+ var reversedData: Data {\r
+ return Data(bytes: reversedBytes, count: count)\r
+ }\r
+}\r
+\r
+extension String {\r
+ /// Create `Data` from hexadecimal string representation\r
+ ///\r
+ /// This creates a `Data` object from hex string. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.\r
+ ///\r
+ /// - returns: Data represented by this hexadecimal string.\r
+ var hexadecimal: Data? {\r
+ var data = Data(capacity: self.count / 2)\r
+\r
+ let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)\r
+ regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in\r
+ let byteString = (self as NSString).substring(with: match!.range)\r
+ let num = Byte(byteString, radix: 16)!\r
+ data.append(num)\r
+ }\r
+\r
+ guard data.count > 0 else { return nil }\r
+\r
+ return data\r
+ }\r
+ \r
+ var trimmedString: String {\r
+ return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).replacingOccurrences(of: "\0", with: "")\r
+ }\r
+\r
+ var utf8Encoded: Data {\r
+ return data(using: .utf8)!\r
+ }\r
+ \r
+ func ck_compare(with version: String) -> ComparisonResult {\r
+ return compare(version, options: .numeric, range: nil, locale: nil)\r
+ }\r
+ \r
+}\r
+\r
+protocol UIntToBytesConvertable_C {\r
+ var toLittleEndianBytes: [Byte] { get }\r
+ var toLittleEndianData: Data { get }\r
+ \r
+ var toBigEndianBytes: [Byte] { get }\r
+ var toBigEndianData: Data { get }\r
+}\r
+\r
+extension UIntToBytesConvertable_C {\r
+ func toBytes <T: BinaryInteger>(endian: T) -> [Byte]{\r
+ var _endian = endian\r
+ let count = endian.bitWidth/8\r
+ let bytePtr = withUnsafePointer(to: &_endian) {\r
+ $0.withMemoryRebound(to: Byte.self, capacity: count) {\r
+ UnsafeBufferPointer(start: $0, count: count)\r
+ }\r
+ }\r
+ return [Byte](bytePtr)\r
+ }\r
+ \r
+ var toLittleEndianData: Data {\r
+ let bytesArray = toLittleEndianBytes\r
+ return Data(bytes: bytesArray, count: bytesArray.count)\r
+ }\r
+ \r
+ var toBigEndianData: Data {\r
+ let bytesArray = toBigEndianBytes\r
+ return Data(bytes: bytesArray, count: bytesArray.count)\r
+ }\r
+}\r
+\r
+extension Byte: UIntToBytesConvertable_C {\r
+ var toLittleEndianBytes: [Byte] {\r
+ return toBytes(endian: self.littleEndian)\r
+ }\r
+ \r
+ var toBigEndianBytes: [Byte] {\r
+ return toBytes(endian: self.bigEndian)\r
+ }\r
+}\r
+\r
+extension UInt16: UIntToBytesConvertable_C {\r
+ var toLittleEndianBytes: [Byte] {\r
+ return toBytes(endian: self.littleEndian)\r
+ }\r
+ \r
+ var toBigEndianBytes: [Byte] {\r
+ return toBytes(endian: self.bigEndian)\r
+ }\r
+}\r
+\r
+extension UInt32: UIntToBytesConvertable_C {\r
+ var toLittleEndianBytes: [Byte] {\r
+ return toBytes(endian: self.littleEndian)\r
+ }\r
+ \r
+ var toBigEndianBytes: [Byte] {\r
+ return toBytes(endian: self.bigEndian)\r
+ }\r
+}\r
+\r
+extension UInt64: UIntToBytesConvertable_C {\r
+ var toLittleEndianBytes: [Byte] {\r
+ return toBytes(endian: self.littleEndian)\r
+ }\r
+ \r
+ var toBigEndianBytes: [Byte] {\r
+ return toBytes(endian: self.bigEndian)\r
+ }\r
+}\r
+\r
+extension Int {\r
+ var data: Data {\r
+ var int = self\r
+ return Data(bytes: &int, count: MemoryLayout<Int>.size).reversedData\r
+ }\r
+}\r
+\r
+extension Int8 {\r
+ var data: Data {\r
+ var int = self\r
+ return Data(bytes: &int, count: MemoryLayout<Int8>.size).reversedData\r
+ }\r
+}\r
+\r
+extension Int16 {\r
+ var data: Data {\r
+ var int = self\r
+ return Data(bytes: &int, count: MemoryLayout<Int16>.size).reversedData\r
+ }\r
+}\r
+\r
+extension Int32 {\r
+ var data: Data {\r
+ var int = self\r
+ return Data(bytes: &int, count: MemoryLayout<Int32>.size).reversedData\r
+ }\r
+}\r
+\r
--- /dev/null
+//\r
+// BleManager.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+/// import RXSWift\r
+/// import RXBluetoothKit\r
+import CoreBluetooth\r
+\r
+enum BleStatus_C: Int {\r
+ case connecting = 0\r
+ case connected = 1\r
+ case disconnected = 2\r
+}\r
+\r
+class BleManager_C {\r
+ static let shared = BleManager_C()\r
+ \r
+ // 蓝牙管理器\r
+ fileprivate var centralManager = CentralManager()\r
+ \r
+ // 当前已连接的外设\r
+ fileprivate var currentPeripheral: Peripheral?\r
+ // 当前连接设备的mac地址\r
+ fileprivate var currentMac = ""\r
+ \r
+ // 当前连接状态\r
+ fileprivate(set) var currentBleStatus = BleStatus_C.disconnected {\r
+ didSet {\r
+ if oldValue != currentBleStatus {\r
+ bleStatusSubject.onNext(currentBleStatus)\r
+ \r
+ externalDelegate?.onBleConnectionStateChanged(state: currentBleStatus, mac: currentMac)\r
+ }\r
+ }\r
+ }\r
+ \r
+ var isConnected: Bool {\r
+ return currentBleStatus == .connected\r
+ }\r
+ \r
+ /**\r
+ 是否正在前台连接\r
+ */\r
+ var isConnectForgeground: Bool = false\r
+ \r
+ // 连接状态监听\r
+ var bleStatusSubject = PublishSubject<BleStatus_C>()\r
+ \r
+ // 仅用于内部指令收发\r
+ fileprivate var internalDisposeBag = DisposeBag()\r
+ \r
+ \r
+ /// 外部回调\r
+ var externalDelegate: AliCloudIotBleDelegate_C?\r
+ \r
+ init() {\r
+ #if DEBUG\r
+ RxBluetoothKitLogger.defaultLogger.setLogLevel(.debug)\r
+ #else\r
+ RxBluetoothKitLogger.defaultLogger.setLogLevel(.info)\r
+ #endif\r
+ }\r
+}\r
+\r
+// 发现和连接设备\r
+extension BleManager_C {\r
+ // 蓝牙状态\r
+ var centralManagerState: BluetoothState {\r
+ return centralManager.state\r
+ }\r
+ \r
+ // 等待蓝牙状态\r
+ func bluetoothStateObservable() -> Observable<BluetoothState> {\r
+ return centralManager.observeState()\r
+ .startWith(centralManager.state)\r
+ }\r
+ \r
+ // 蓝牙已准备好\r
+ func waitBluetoothAvailable() -> Observable<BluetoothState> {\r
+ return bluetoothStateObservable()\r
+ .filter { $0 == .poweredOn }\r
+ .take(1)\r
+ .timeout(.seconds(8), scheduler: MainScheduler.instance)\r
+ }\r
+ \r
+ // 仅扫描发现设备\r
+ func scanDevicesOnly() -> Observable<ScannedPeripheral> {\r
+ return waitBluetoothAvailable()\r
+ .flatMap { _ in self.centralManager.scanForPeripherals(withServices: [DeviceService_C.mainService.uuid]) }\r
+ }\r
+ \r
+ func isDeviceConnected(device: BleDeviceModel_C) -> Bool {\r
+ return currentMac == device.mac && currentBleStatus == .connected && (currentPeripheral?.isConnected ?? false)\r
+ }\r
+ \r
+ // 直接连接设备,或者扫描到再尝试连接\r
+ func connect(device: BleDeviceModel_C, peripheral: Peripheral?, scanTimeout: Int = 10) {\r
+ if isDeviceConnected(device: device) {\r
+ return\r
+ }\r
+ \r
+ disconnectCurrentDevice(isReconnect: true)\r
+ \r
+ MLog.debug("开始连接设备")\r
+ \r
+ scanOrConnect(device: device, peripheral: peripheral, timeout: scanTimeout)\r
+ .subscribe { peripheral in\r
+ MLog.debug("连接设备成功")\r
+ self.observePeripheralConnection(peripheral: peripheral)\r
+ } onError: { error in\r
+ MLog.error("连接设备失败:\(error)")\r
+ self.currentBleStatus = .disconnected\r
+ } onCompleted: {\r
+ MLog.debug("连接设备onCompleted")\r
+ } onDisposed: {\r
+ MLog.debug("连接设备onDisposed")\r
+ }\r
+ .disposed(by: internalDisposeBag)\r
+ }\r
+ \r
+ // 断开当前的连接,释放蓝牙资源\r
+ func disconnectCurrentDevice(isReconnect: Bool = false) {\r
+ internalDisposeBag = DisposeBag()\r
+ currentPeripheral = nil\r
+ \r
+ currentBleStatus = isReconnect ? .connecting : .disconnected\r
+ }\r
+ \r
+ // 拆包发送指令,注意onComplete才是全部发送完毕\r
+ func send(data: Data) -> Observable<Void> {\r
+ guard let peripheral = currentPeripheral else {\r
+ return .error(BleError_C.disconnected)\r
+ }\r
+ return peripheral.writeValue(data, for: DeviceCharacteristic_C.write, type: .withoutResponse)\r
+ .asObservable()\r
+ .flatMap { _ in Observable<Void>.just(()) }\r
+ }\r
+ \r
+ \r
+ func logScannedPeripheral(_ scannedPeripheral: ScannedPeripheral) {\r
+ MLog.debug("扫描到设备:\(scannedPeripheral.peripheral.name ?? "No name") \(scannedPeripheral.advertisementData.advertisementData)")\r
+ }\r
+ \r
+}\r
+\r
+fileprivate extension BleManager_C {\r
+ // 扫描并连接设备,如果不能直接连接,则尝试先扫描再连接\r
+ func scanOrConnect(device: BleDeviceModel_C, peripheral: Peripheral?, timeout: Int) -> Observable<Peripheral> {\r
+ return waitBluetoothAvailable()\r
+ .flatMap({ (_) -> Observable<Peripheral> in\r
+ if let peripheral = peripheral {\r
+ return self.connect(device: device, peripheral: peripheral, timeout: timeout)\r
+ } else {\r
+ return self.scanConnect(device: device, timeout: timeout)\r
+ }\r
+ })\r
+ }\r
+ \r
+ // 扫描并连接指定的设备\r
+ func scanConnect(device: BleDeviceModel_C, timeout: Int) -> Observable<Peripheral> {\r
+ return waitBluetoothAvailable()\r
+ .flatMap { _ in self.centralManager.scanForPeripherals(withServices: [DeviceService_C.mainService.uuid]) }\r
+ .filter({ scannedPeripheral in\r
+ BleManager_C.shared.logScannedPeripheral(scannedPeripheral)\r
+ return self.externalDelegate?.parseMacAddressFromManufacturerDataData(manufacturerData: scannedPeripheral.advertisementData.manufacturerData) == device.mac\r
+ })\r
+ .take(1)\r
+ .timeout(.seconds(timeout), scheduler: MainScheduler.instance)\r
+ .flatMap { self.connect(device: device, peripheral: $0.peripheral, timeout: timeout) }\r
+ }\r
+ \r
+ // 连接蓝牙设备,设置数据读取notify\r
+ func connect(device: BleDeviceModel_C, peripheral: Peripheral, timeout: Int) -> Observable<Peripheral> {\r
+ return waitBluetoothAvailable()\r
+ .flatMap { _ in peripheral.establishConnection()\r
+ .amb(Observable<Peripheral>\r
+ .error(RxError.timeout)\r
+ //添加连接超时,timeout时间是和搜索的时间同步开始计算的,所以需要乘于2,真正连接超时刚好为timeout\r
+ .delaySubscription(.seconds(timeout*2), scheduler: MainScheduler.instance))}\r
+// .delay(.seconds(30), scheduler: MainScheduler.instance)\r
+ .flatMap { (peripheral) -> Observable<Peripheral> in\r
+ MLog.debug("连接蓝牙设备成功: \(peripheral.identifier.uuidString)")\r
+ \r
+ // 监听并读取蓝牙数据\r
+ peripheral.observeValueUpdateAndSetNotification(for: DeviceCharacteristic_C.notify)\r
+ .subscribe { (characteristic) in\r
+ self.bleDataReceived(characteristic: characteristic)\r
+ } onError: { (error) in\r
+ MLog.error("监听并读取蓝牙数据通道错误:\(error)")\r
+ }\r
+ .disposed(by: self.internalDisposeBag)\r
+ \r
+ self.currentPeripheral = peripheral\r
+ self.currentMac = device.mac\r
+ self.currentBleStatus = .connected\r
+\r
+ return Observable.just(peripheral)\r
+ }\r
+ \r
+ }\r
+ \r
+ // 监听设备连接状态\r
+ func observePeripheralConnection(peripheral: Peripheral) {\r
+ peripheral.observeConnection()\r
+ .subscribe { isConnected in\r
+ self.currentBleStatus = isConnected ? .connected : .disconnected\r
+ } onError: { error in\r
+ MLog.debug("设备连接监听出错:\(error)")\r
+ }\r
+ .disposed(by: internalDisposeBag)\r
+ }\r
+}\r
+\r
+fileprivate extension BleManager_C {\r
+ // 收到蓝牙数据,统一处理\r
+ func bleDataReceived(characteristic: Characteristic) {\r
+ guard let data = characteristic.value, 0 < data.count else {\r
+ MLog.error("蓝牙接收的数据为空")\r
+ return\r
+ }\r
+ let hex = data.hexString(separator: " ")\r
+ let uuid = characteristic.uuid.uuidString\r
+ MLog.debug("蓝牙Characteristic:\(characteristic.uuid.uuidString) 接收到数据:\(hex)")\r
+ \r
+ externalDelegate?.onBleDataReceived(uuid: uuid, data: data)\r
+ \r
+ //解析蓝牙的消息\r
+ let bleData = hex.data(using: String.Encoding.utf8, allowLossyConversion: false) ?? Data()\r
+ \r
+ if let json = try! JSON(data: bleData).dictionaryObject, let sid = json["sid"], let data = json["data"] {\r
+ let type = BleSIdentifier_C.init(rawValue: sid as! String)\r
+ \r
+ switch type {\r
+ case .fgs:\r
+ MLog.debug("====>>>> 收到蓝牙飞鸽书消息 \(data)")\r
+ FGSCheckService_C.shared.didReceiveBleData(data: data as! [String : Any])\r
+ case .fnd:\r
+ MLog.debug("====>>>> 收到蓝牙飞鸟盾信息 \(data)")\r
+ LPConnectService_C.shared.didReceiveBleData(dataStr: data as! String)\r
+ case .ccm:\r
+ MLog.debug("====>>>> 收到蓝牙乘车码消息 --- 开始请求乘车码")\r
+ RideYardService_C.shared.didReceiveBleData(dataStr: data as! String)\r
+// do {\r
+// try RideYardService.shared.didReceiveBleData(dataStr: data as! String)\r
+// } catch {\r
+// MLog.debug("====>>>> 请求乘车码 抛异常")\r
+// }\r
+ \r
+ case .gtd:\r
+ MLog.debug("====>>>> 收到蓝牙id2消息")\r
+ //解析topic和msg\r
+ ID2Service_C.shared.didReceiveBleData(data: data as! [String : Any])\r
+ case .none:\r
+ MLog.debug("====>>>> none")\r
+ }\r
+ }\r
+ }\r
+ \r
+}\r
--- /dev/null
+//
+// BleNeedSendDataDelegate.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/28.
+//
+
+import Foundation
+
+@objc public protocol BleNeedSendDataDelegate_C {
+
+ func sendBleData(data: Data)
+
+}
--- /dev/null
+//
+// BleSIdentifier.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/26.
+//
+
+import Foundation
+
+enum BleSIdentifier_C:String {
+ case fgs = "FGS"
+ case fnd = "FND"
+ case ccm = "CCM"
+ case gtd = "GTD"
+}
--- /dev/null
+//\r
+// BleTypes.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+/// import RXBluetoothKit\r
+//import UIKit\r
+\r
+\r
+// 蓝牙相关的错误\r
+enum BleError_C: Error, Equatable {\r
+ // 系统蓝牙未打开\r
+ case powerOff\r
+ // 设备断连\r
+ case disconnected\r
+ // 连接或者指令超时\r
+ case timeout\r
+ // 设备认证失败\r
+ case authenticationFailed\r
+ // 配对异常,需在设置中忽略设备\r
+ case pairErrorNeedRemoveInIOS\r
+ // 指令解析出错\r
+ case commandError\r
+ \r
+ var errorMessage: String {\r
+ switch self {\r
+ case .powerOff: return "powerOff"\r
+ case .disconnected: return "disconnected"\r
+ case .timeout: return "timeout"\r
+ case .authenticationFailed: return "authenticationFailed"\r
+ case .pairErrorNeedRemoveInIOS: return "pairErrorNeedRemoveInIOS"\r
+ case .commandError: return "commandError"\r
+ }\r
+ }\r
+ \r
+ var errorCode: String {\r
+ switch self {\r
+ case .powerOff: return "1001"\r
+ case .disconnected: return "1002"\r
+ case .timeout: return "1003"\r
+ case .authenticationFailed: return "1004"\r
+ case .pairErrorNeedRemoveInIOS: return "1005"\r
+ case .commandError: return "1006"\r
+ }\r
+ }\r
+ \r
+ static func fromError(_ error: Error) -> BleError_C {\r
+ if let error = error as? BleError_C {\r
+ return error\r
+ } else if let error = error as? BluetoothError {\r
+ switch error {\r
+ case .bluetoothUnsupported, .bluetoothUnauthorized, .bluetoothPoweredOff, .bluetoothInUnknownState, .bluetoothResetting:\r
+ return .powerOff\r
+ case .peripheralDisconnected:\r
+ return .disconnected\r
+ default:\r
+ return .commandError\r
+ }\r
+ } else {\r
+ return .commandError\r
+ }\r
+ }\r
+}\r
+\r
+enum BlePairStatus_C: Int {\r
+ case discovableConnectable = 0\r
+ case connecting\r
+ case connected\r
+ case undiscovableConnectable\r
+ case undiscovableUnconnectable\r
+}\r
+\r
+struct BleConstants_C {\r
+ static let bleStateId: Byte = 0x00\r
+ static let bleProductType: Byte = 0xE2\r
+ \r
+ static let mtu = 20\r
+ static let timeout: TimeInterval = 3\r
+ \r
+ static let cid1 = "0870"\r
+ static let cid2 = "7008"\r
+}\r
--- /dev/null
+//\r
+// DeviceServiceCharacteristic.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+/// import RXBluetoothKit\r
+import CoreBluetooth\r
+\r
+enum DeviceService_C: String, ServiceIdentifier {\r
+ case mainService = "0000ff01-0000-1000-8000-00805f9b34fb"\r
+\r
+ var uuid: CBUUID {\r
+ return CBUUID(string: self.rawValue)\r
+ }\r
+}\r
+\r
+enum DeviceCharacteristic_C: String, CharacteristicIdentifier {\r
+ case write = "0000fa04-0000-1000-8000-00805f9b34fb"\r
+ case notify = "0000fa05-0000-1000-8000-00805f9b34fb"\r
+\r
+ var uuid: CBUUID {\r
+ return CBUUID(string: self.rawValue)\r
+ }\r
+ \r
+ // Service to which characteristic belongs\r
+ var service: ServiceIdentifier {\r
+ DeviceService_C.mainService\r
+ }\r
+}\r
--- /dev/null
+//
+// HttpTool.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/26.
+//
+
+import Foundation
+import AliIotConnectKit_Private.OC
+
+//class AFManager : NSObject {
+//
+// private var sessionManager: AF.s?
+//
+//
+//required override init() {
+// super.init()
+//
+// enableCertificatePinning()
+//
+//}
+//
+//private func enableCertificatePinning() {
+// let certificates = getCertificates()
+// ServerTrustManager(evaluators: [String : ServerTrustEvaluating])
+// let trustPolicy = ServerTrustPolicy.pinCertificates(
+// certificates: certificates,
+// validateCertificateChain: true,
+// validateHost: true)
+// let trustPolicies = [ "myWebsite.com": trustPolicy ]
+// let policyManager = ServerTrustPolicyManager(policies: trustPolicies)
+// sessionManager = SessionManager(
+// configuration: .default,
+// serverTrustPolicyManager: policyManager)
+//}
+//
+//private func getCertificates() -> [SecCertificate] {
+// let url = Bundle.main.url(forResource: "scra", withExtension: "cer")!
+// let localCertificate = try! Data(contentsOf: url) as CFData
+// guard let certificate = SecCertificateCreateWithData(nil, localCertificate)
+// else { return [] }
+//
+// return [certificate]
+//}
+
+class HttpTool_C {
+ static let shared = HttpTool_C()
+
+ static let baseUrl:String = "https://iot-auth.cn-shanghai.aliyuncs.com/auth/register/device"
+
+ func registerDeviceInfoToIot(parameters:[String : Any], complete:@escaping (_ isSuccess:Bool, _ result:[String: Any]) -> ()) {
+
+// AF.request(URL(string: HttpTool.baseUrl)!, method: .post, parameters: parameters)
+// .responseString { (responses) in
+// let ste:String = responses.value ?? ""
+// let jsonData = ste.data(using: .utf8)
+//
+// MLog.debug("====>>>> 网络请求结果\(String(describing: jsonData))")
+// let json:[String:Any] = try! JSONSerialization.jsonObject(with: jsonData!) as! [String : Any]
+// if let data = json["data"] {
+// complete(true, data as! [String : Any])
+// }else {
+// complete(false, json)
+// }
+// }
+
+ registerDeviceInfoByOC(parameters: parameters, complete: complete)
+ }
+
+ func registerDeviceInfoByOC(parameters:[String : Any], complete:@escaping (_ isSuccess:Bool, _ result:[String: Any]) -> ()) {
+ MLog.debug("====>>>> 开始oc网络请求方法请求 \(parameters)")
+
+ AFHttpHelper_C.registerDeviceInfoToIot(withParameters: parameters) { json, isSuccess in
+ complete(isSuccess, json as! [String : Any])
+ }
+ }
+
+}
--- /dev/null
+//\r
+// AliCloudIotLogManager.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+/// import SwiftyBeaver\r
+\r
+private enum MQTTLogStatus {\r
+ case ready;\r
+ case running;\r
+ case stopping;\r
+ \r
+}\r
+\r
+let MLog = SwiftyBeaver.self\r
+\r
+@objcMembers public class AliCloudIotLogManager_C: NSObject {\r
+ public static let shared = AliCloudIotLogManager_C()\r
+ \r
+ private override init() {\r
+ super.init()\r
+ }\r
+ \r
+ private var mqttLog: MQTTLogStatus = .ready\r
+ \r
+ private func setupDestination() {\r
+ \r
+ let console = ConsoleDestination() // log to Xcode Console\r
+ // use custom format and set console output to short time, log level & message\r
+ console.format = "$Dyyyy-MM-dd HH:mm:ss.SSS$d $T $C$L$c $N.$F:$l - $M"\r
+ console.asynchronously = true\r
+ MLog.addDestination(console)\r
+ }\r
+ \r
+ \r
+ /// 切换日志显示\r
+ /// - Parameter isOpen: 是否开启日志,为TRUE时,打印日志\r
+ public func switchLogStatus(_ isOpen: Bool = true) {\r
+ //清空日志显示\r
+ MLog.removeAllDestinations()\r
+ \r
+ guard isOpen else {\r
+ return\r
+ }\r
+ setupDestination()\r
+ }\r
+ \r
+ public func switchMQTTLogStatus(_ isOpen: Bool = true) {\r
+ output(isEnable: isOpen)\r
+ }\r
+ \r
+ func output(isEnable: Bool) {\r
+ \r
+ if isEnable == false &&\r
+ mqttLog == .running {\r
+ mqttLog = .stopping\r
+ return\r
+ }\r
+ \r
+ if mqttLog != .ready {\r
+ return\r
+ }\r
+ \r
+ mqttLog = .running\r
+ \r
+ let queue = DispatchQueue(label: "mqtt_log_info_queue")\r
+ \r
+ queue.async { [self] in\r
+ \r
+ while mqttLog == .running {\r
+ sleep(1)\r
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: IotMqttManager_C.shared.mqttInfo())\r
+ }\r
+ \r
+ mqttLog = .ready\r
+ }\r
+ \r
+ }\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotWatchManager.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+/// import SwiftyJSON\r
+\r
+ class AliCloudIotWatchManager_C: NSObject {\r
+ public static let shared = AliCloudIotWatchManager_C()\r
+ \r
+ static let LOG_TAG = "AliCloudIotWatchManager"\r
+ \r
+ private var sdkDelegate: AliCloudIotSdkInitDelegate_C?\r
+ private var iotDelegate: AliCloudIotDelegate_C?\r
+ \r
+ /// SDK初始化和注销初始化是否在子线程运行\r
+ /// 回调在主线程\r
+ public var sdkInitOrDeInitInAsync: Bool = false\r
+ \r
+ var sdkService: SdkMqttService_C?\r
+ \r
+ \r
+ /// 初始化SDK\r
+ /// - Parameters:\r
+ /// - config: 配置信息\r
+ /// - sdkDelegate: sdk初始化回调\r
+ /// - iotDelegate: iot相关回调\r
+ public func initSdk<T: AliCloudIotSdkConfigBase_C>(config: T, sdkDelegate: AliCloudIotSdkInitDelegate_C, iotDelegate: AliCloudIotDelegate_C) throws {\r
+\r
+ \r
+ self.sdkDelegate = sdkDelegate\r
+ self.iotDelegate = iotDelegate\r
+ \r
+ sdkService = IotMqttManager_C.shared\r
+ try sdkService!.initMqtt(config: config, delegate: self)\r
+ }\r
+ \r
+ \r
+ /// 初始化SDK\r
+ /// - Parameters:\r
+ /// - config: 配置信息\r
+ /// - sdkDelegate: sdk初始化回调\r
+ /// - iotDelegate: iot相关回调\r
+ public func initSdk(config: AliCloudIotSdkConfigBaseClass_C, sdkDelegate: AliCloudIotSdkInitDelegate_C, iotDelegate: AliCloudIotDelegate_C) throws {\r
+ \r
+ \r
+ self.sdkDelegate = sdkDelegate\r
+ self.iotDelegate = iotDelegate\r
+ \r
+ sdkService = IotMqttManager_C.shared\r
+ sdkService?.sdkInitOrDeInitInAsync = sdkInitOrDeInitInAsync\r
+ \r
+ try sdkService!.initMqtt(config: config.getConfig().config, delegate: self)\r
+ }\r
+ \r
+ \r
+ /// 注销初始化,断开设备与物联网平台的MQTT长连接。\r
+ public func deInitSdk() {\r
+ sdkService?.deInitMqtt()\r
+ }\r
+ \r
+ \r
+}\r
+\r
+extension AliCloudIotWatchManager_C{\r
+ public func subscribeTopic(){\r
+ MqttFileManager.shared.subscribeTopic();\r
+ }\r
+ public func sendFile(_ fileName:String,_ content:Data){\r
+ MqttFileManager.shared.sendFile(fileName,content);\r
+ }\r
+}\r
+\r
+extension AliCloudIotWatchManager_C: SdkMqttDelegate_C {\r
+ func onIotLog(log: String) {\r
+ iotDelegate?.onIotLog(log: log)\r
+ }\r
+\r
+ func onInit(successed: Bool, error: Error?) {\r
+ sdkDelegate?.onInit(successed: successed, error: error)\r
+ }\r
+ \r
+ func onDeinit(successed: Bool, error: Error?) {\r
+ sdkDelegate?.onDeinit(successed: successed, error: error)\r
+ }\r
+ \r
+ func onConnectStateChange(state: AliCludIotConnectionState) {\r
+ switch state {\r
+ case .connected:\r
+ iotDelegate?.onIotConnected()\r
+ subscribeTopic()\r
+ case .disconnected:\r
+ iotDelegate?.onIotDisconnected()\r
+ case .connecting:\r
+ iotDelegate?.onIotConnecting()\r
+ default:\r
+ break\r
+ }\r
+ }\r
+ \r
+ func onMessageArrival(topic: String, payload: Any?) {\r
+ MLog.debug("====>>>> 接收到 topic: \(topic) 的消息")\r
+ \r
+ iotDelegate?.onMessageArrived(topic: topic, message: payload)\r
+ \r
+ if topic.hasSuffix(MqttFileManager.send_reply_topic) || topic.hasSuffix(MqttFileManager.init_reply_topic) {\r
+ MqttFileManager.shared.onMessageArrival(topic: topic, payload: payload)\r
+ }\r
+ else if let model = ThingCommunicateService_C.shared.tryResolve(topic: topic, messageData: payload), let type = CommandIdentifier_C.init(rawValue: model.cmdIdentifier) {\r
+ switch type {\r
+ case .alipayRideyard:\r
+ MLog.debug("====>>>> iot响应乘车码数据 \(model.data)")\r
+ iotDelegate?.onBusCodeMessageArrived(data: model.data, message: model.message, code: model.code)\r
+ rideYardService.didReceiveMqttData(model: model)\r
+ case .iot:\r
+ iotDelegate?.onIotMessageArrived(data: model.data, message: model.message, code: model.code)\r
+ case .systemCommand:\r
+ MLog.debug("====>>>> 收到系统消息 \(model.data)")\r
+ }\r
+ }else {\r
+ let subStrs = topic.components(separatedBy: "thing/")\r
+ if subStrs.count == 2 {\r
+ guard let topicFragment:String = subStrs.last else {\r
+ MLog.debug("====>>>> iot 数据异常 topic : \(topic)")\r
+ return }\r
+ MLog.debug("====>>>> topicFragment : \(topicFragment)")\r
+ \r
+ guard let str:String = payload as? String else {\r
+ MLog.debug("====>>>> payLoad不是str")\r
+ return }\r
+ let data = str.data(using: .utf8)\r
+ \r
+ guard let dic = try? JSON(data: data!).dictionaryObject else {\r
+ MLog.debug("====>>>> 发送蓝牙id2 转payload失败")\r
+ return }\r
+ guard let params:[String:Any] = dic["params"] as? [String : Any] else {\r
+ MLog.debug("====>>>> 发送蓝牙id2 获取params失败")\r
+ return }\r
+ let msgId:String = dic["id"] as! String\r
+ MLog.debug("====>>>> 开始回复id2 \(msgId)")\r
+ ID2Service_C.shared.id2Reply(topicFragment: topicFragment, dataStr: params, id: msgId)\r
+\r
+\r
+ guard let data = try? JSONSerialization.data(withJSONObject: params) else {\r
+ MLog.debug("====>>>> 发送蓝牙id2 数据,数据转码失败 ")\r
+ return }\r
+ guard let msg:String = String(data: data, encoding: .utf8) else {\r
+ MLog.debug("====>>>> 发送蓝牙id2 数据,数据转码失败2")\r
+ return }\r
+ MLog.debug("====>>>> 发送蓝牙数据 \(msg)")\r
+ ID2Service_C.shared.sendId2ToBle(topic: topicFragment, msg: msg)\r
+ }else {\r
+ MLog.debug("====>>>> iot 数据不能识别 topic : \(topic)")\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+//发布消息,订阅1·\r
+ extension AliCloudIotWatchManager_C {\r
+ \r
+ func publishMessage(topic: String, data: Data) throws {\r
+ try sdkService?.publishMessage(topic: topic , data: data)\r
+ }\r
+ \r
+ func publishMessage(topic: String, data: Data, qos: Int32, resultBlock: @escaping (Bool, Error?) -> Void) throws {\r
+ try sdkService?.publishMessage(topic: topic, data: data, qos: qos, resultBlock: resultBlock)\r
+ }\r
+ \r
+ \r
+ /// 订阅主题\r
+ /// - Parameter topic: 主题\r
+ func subscribeTopic(topic: String) throws {\r
+ try sdkService?.subscribeTopic(topic: topic, qos: 0)\r
+ }\r
+ \r
+ /// 取消订阅主题\r
+ /// - Parameter topic: 主题\r
+ func unsubscribeTopic(topic: String) throws {\r
+ try sdkService?.unsubscribeTopic(topic: topic)\r
+ }\r
+}\r
+\r
+//抛出调用实例\r
+ extension AliCloudIotWatchManager_C {\r
+ \r
+ /// 乘车码服务\r
+ var rideYardService: RideYardService_C {\r
+ RideYardService_C.shared.sdkService = sdkService\r
+ return RideYardService_C.shared\r
+ }\r
+ \r
+ /// ble服务\r
+ var bleService: BleService_C {\r
+ return BleService_C.shared\r
+ }\r
+}\r
+\r
+\r
--- /dev/null
+//
+// AliConnectMananger.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/27.
+//
+
+import Foundation
+/// import SwiftyJSON
+
+public typealias checkFgsStateComplete = (_ isSuccess:Bool, _ data:[String:Any])->()
+public typealias fndStateComplete = (_ isSuccess:Bool, _ data:[String:Any])->()
+
+@objcMembers public class AliConnectMananger_C: NSObject {
+ public static let shared = AliConnectMananger_C()
+
+ override init() {
+ MLog.debug("AliIotConnectKit Initial on \(sdkVer_C)")
+ }
+
+ //收到需要发送给蓝牙的数据
+ public var bleSendDataDelegate: BleNeedSendDataDelegate_C?
+
+ /// +++
+ public var iotStateDelegate:IotStateDelatege_C? {
+ didSet {
+ guard let iotStateDelegate = iotStateDelegate else {
+ MLog.debug("result 不能为空")
+ return
+ }
+ connectLpService.iotStateDelegate = iotStateDelegate
+ }
+ }
+
+
+ public var appVersion: String? {
+ didSet {
+ guard let appVersion = appVersion else {
+ return
+ }
+
+ connectLpService.appVersion = appVersion
+ }
+ }
+
+ /// 收到来自蓝牙的消息
+ /// - Parameter data: 二进制数据
+ open func bleDataReceived(data: Data) {
+ assert(data.isEmpty == false, "data 不能为空")
+ assert(data.count > 0, "data ")
+ dataReceivedFromBle(bleData: data)
+ }
+
+ /// 发起飞鸽书请求
+ open func checkFgsState(result:@escaping checkFgsStateComplete) -> Void {
+ let res:checkFgsStateComplete? = result
+ print("------------------------------------------------------------------")
+ print("------------------------------------------------------------------")
+
+ guard let _ = res else {
+ MLog.debug("result 不能为空")
+ assert(false, "result 不能为空")
+ return
+ }
+
+ fgsCheckService.checkFgsState(result: result)
+ }
+
+ /// 连接Lp
+ open func startConnectLpState(result:@escaping fndStateComplete) -> Void {
+ let res:fndStateComplete? = result
+
+ guard let _ = res else {
+ MLog.debug("result 不能为空")
+ assert(false, "result 不能为空")
+ return
+ }
+
+ guard let _ = iotStateDelegate else {
+ MLog.error("请监听 iotStateDelegate")
+ result(false, ["error": "请监听 iotStateDelegate"])
+ return
+ }
+
+ guard let _ = appVersion else {
+ MLog.error("请设置 App 版本")
+ result(false, ["error": "请设置 App 版本"])
+ return
+ }
+
+ connectLpService.startConnectLpState { isSuccess, data in
+ result(isSuccess, data)
+ if(isSuccess){
+// MqttFileManager.shared.subscribeTopic();
+ }
+ }
+ }
+
+ /// 主动断开Lp
+ open func disConnect(result:@escaping fndStateComplete) -> Void {
+ connectLpService.disConnect(result: result)
+ }
+
+ open func uploadFile(fileName:String,content:Data){
+ AliCloudIotWatchManager_C.shared.sendFile( fileName,content);
+ }
+}
+
+private extension AliConnectMananger_C {
+ /// 检查飞鸽书
+ var fgsCheckService: FGSCheckService_C {
+ return FGSCheckService_C.shared
+ }
+
+ /// 连接LP
+ var connectLpService: LPConnectService_C {
+ return LPConnectService_C.shared
+ }
+}
+
+private extension AliConnectMananger_C {
+
+// open func didReceiveBleData(dataStr:String) {
+// //返回数据的base64字符串
+// if let data = NSData(base64Encoded: dataStr) {
+// let decodeStr:String = String(data: data as Data, encoding: .utf8)!
+// MLog.debug("====>>>> 飞鸟盾数据解码 \(decodeStr)")
+//
+// let productKey = decodeStr.inFix(startIndex: 3, endIndex: 3 + 11)
+// let deviceName = decodeStr.inFix(startIndex: 3 + 11, endIndex: 3 + 11 + 29)
+// let passWd = decodeStr.inFix(startIndex: 3 + 11 + 29, endIndex: 3 + 11 + 29 + 40)
+//// startIotLogin(productKey: productKey, deviceName: deviceName, passWd: passWd)
+// }else {
+// MLog.debug("====>>>> 转base")
+// }
+// }
+
+// open func testFunc(dataStr:String) {
+//// if let json = try! JSON(dataStr).dictionaryObject {
+//// debugPrint("开始打印 \(json)")
+//// }
+//
+// let data = dataStr.data(using: .utf8)
+// if let json = try! JSON(data: data!).dictionaryObject {
+// debugPrint("开始打印 \(json)")
+// }
+//// let dic = JSONSerialization.jsonObject(with: data!)
+//// ID2Service.shared.didReceiveBleData(data: data as! [String : Any])
+// }
+
+ func dataReceivedFromBle(bleData: Data) {
+ let bleStr = String(data: bleData, encoding: .utf8)
+ if let bleJson = try? JSONSerialization.jsonObject(with: bleData) {
+ MLog.debug("====>>>> 收到来自蓝牙的数据 bleStr : \(bleStr ?? "null")")
+ MLog.debug("====>>>> 收到来自蓝牙的数据 bleJson : \(bleJson)")
+
+ }
+
+ if let json = try? JSON(data: bleData).dictionaryObject, let sid = json["sid"], let data:String = json["data"] as? String {
+ let type = BleSIdentifier_C.init(rawValue: sid as! String)
+
+ switch type {
+ case .fgs:
+ MLog.debug("====>>>> 收到蓝牙飞鸽书消息 \(data)")
+ let jsonData = data.data(using: .utf8)
+ if let json = try! JSON(data: jsonData!).dictionaryObject {
+ FGSCheckService_C.shared.didReceiveBleData(data: json)
+ }
+ case .fnd:
+ MLog.debug("====>>>> 收到蓝牙飞鸟盾信息 \(data)")
+ LPConnectService_C.shared.didReceiveBleData(dataStr: data )
+ case .ccm:
+ //转成id2数据发送
+ MLog.debug("====>>>> 收到蓝牙乘车码消息 原始数据: \(json) data: \(data)")
+
+ RideYardService_C.shared.didReceiveBleData(dataStr: data)
+// //解析topic和msg
+// let jsonData = data.data(using: .utf8)
+// if let json = try! JSON(data: jsonData!).dictionaryObject {
+// ID2Service.shared.didReceiveBleData(data: json)
+// }
+ case .gtd:
+
+ MLog.debug("====>>>> 收到蓝牙id2消息 原始数据: \(json) data: \(data)")
+ //解析topic和msg
+ let jsonData = data.data(using: .utf8)
+ if let json = try! JSON(data: jsonData!).dictionaryObject {
+ ID2Service_C.shared.didReceiveBleData(data: json)
+ }
+
+ case .none:
+ MLog.debug("====>>>> none")
+ }
+ }else {
+ MLog.debug("====>>>> 接收的蓝牙数据解析失败")
+ }
+ }
+}
+
--- /dev/null
+//
+// AESEncode.swift
+// AliCloudIotKit
+//
+// Created by IYCDev on 2022/11/26.
+//
+
+import Foundation
+//import CryptoSwift
+
+class AESEncode_C {
+
+ /// AES加密
+ /// aes_key : key
+ /// iv : 偏移量
+ class func aes_encrypt(_ str: String, aes_key: String, iv: Array<UInt8>) -> String{
+ var encryptedStr = ""
+ do {
+ let encrypted = try AES(key: aes_key.bytes, blockMode: CBC(iv: iv)).encrypt(str.bytes)
+
+ let data = Data(bytes: encrypted, count: encrypted.count)
+
+ //加密结果从data转成string 转换失败 返回""
+ encryptedStr = data.base64EncodedString(options: .lineLength64Characters)
+
+ } catch {
+ print(error.localizedDescription)
+ }
+ return encryptedStr
+
+ }
+
+}
+
--- /dev/null
+//
+// CommonConfig.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/12/15.
+//
+
+import Foundation
+
+let sdkVer_C = "2.0.11"//20231016
--- /dev/null
+//\r
+// HMACTool.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+import CommonCrypto\r
+enum CryptoAlgorithm_C {\r
+ case MD5, SHA1, SHA224, SHA256, SHA384, SHA512\r
+ \r
+ var HMACAlgorithm: CCHmacAlgorithm {\r
+ var result: Int = 0\r
+ switch self {\r
+ case .MD5: result = kCCHmacAlgMD5\r
+ case .SHA1: result = kCCHmacAlgSHA1\r
+ case .SHA224: result = kCCHmacAlgSHA224\r
+ case .SHA256: result = kCCHmacAlgSHA256\r
+ case .SHA384: result = kCCHmacAlgSHA384\r
+ case .SHA512: result = kCCHmacAlgSHA512\r
+ }\r
+ return CCHmacAlgorithm(result)\r
+ }\r
+ \r
+ var digestLength: Int {\r
+ var result: Int32 = 0\r
+ switch self {\r
+ case .MD5: result = CC_MD5_DIGEST_LENGTH\r
+ case .SHA1: result = CC_SHA1_DIGEST_LENGTH\r
+ case .SHA224: result = CC_SHA224_DIGEST_LENGTH\r
+ case .SHA256: result = CC_SHA256_DIGEST_LENGTH\r
+ case .SHA384: result = CC_SHA384_DIGEST_LENGTH\r
+ case .SHA512: result = CC_SHA512_DIGEST_LENGTH\r
+ }\r
+ return Int(result)\r
+ }\r
+}\r
+\r
+extension String {\r
+ func hmac(algorithm: CryptoAlgorithm_C, key: String) -> String {\r
+ let str = self.cString(using: String.Encoding.utf8)\r
+ let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))\r
+ let digestLen = algorithm.digestLength\r
+ let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)\r
+ let keyStr = key.cString(using: String.Encoding.utf8)\r
+ let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))\r
+ \r
+ CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)\r
+ \r
+ let digest = stringFromResult(result: result, length: digestLen)\r
+ result.deallocate()\r
+ \r
+ return digest\r
+ }\r
+ \r
+ private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>,\r
+ length: Int) -> String {\r
+ let hash = NSMutableString()\r
+ for i in 0..<length {\r
+ hash.appendFormat("%02x", result[i])\r
+ }\r
+ return String(hash)\r
+ }\r
+}\r
--- /dev/null
+//\r
+// IDGenerater.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+class IDGenerater_C {\r
+ static let shared = IDGenerater_C()\r
+ \r
+ private let lock = DispatchSemaphore(value: 1)\r
+ private var _value = Int64(0)\r
+ \r
+ var sequenceId: Int64 {\r
+ lock.wait()\r
+ defer { lock.signal() }\r
+ \r
+ let id = Int64(_value % Int64.max)\r
+ _value += 1\r
+ MLog.debug("====>>>> 消息自增长id \(id)")\r
+ return id\r
+ }\r
+}\r
--- /dev/null
+//\r
+// MyExtension.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+extension Date {\r
+ /// 获取当前 秒级 时间戳 - 10位\r
+ var timeStamp : String {\r
+ let timeInterval: TimeInterval = self.timeIntervalSince1970\r
+ let timeStamp = Int(timeInterval)\r
+ return "\(timeStamp)"\r
+ }\r
+ \r
+ /// 获取当前 毫秒级 时间戳 - 13位\r
+ var milliStamp : String {\r
+ let timeInterval: TimeInterval = self.timeIntervalSince1970\r
+ let millisecond = CLongLong(round(timeInterval*1000))\r
+ return "\(millisecond)"\r
+ }\r
+}\r
+\r
+extension String {\r
+ func inFix(startIndex:Int, endIndex:Int) -> String {\r
+ let indexN = self.index(self.startIndex, offsetBy: startIndex)\r
+ let indexM = self.index(self.startIndex, offsetBy: endIndex)\r
+ let substr = self[indexN..<indexM]\r
+ return String(substr)\r
+ }\r
+}\r
+\r
+extension NSObject {\r
+ func bleDataToString(json:[String:Any]) -> String {\r
+ MLog.debug("====>>>> 发送给蓝牙的data \(json)")\r
+// JSONSerialization.data(withJSONObject: json)\r
+ guard let data = try? JSONSerialization.data(withJSONObject: json) else {\r
+ MLog.debug("====>>>> 发送给蓝牙的data转字符串失败")\r
+ return "" }\r
+ let str:String = String(data: data, encoding: .utf8)!\r
+ MLog.debug("====>>>> 发送给蓝牙的data转字符串 \(str)")\r
+ return str\r
+ }\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotBleDelegate.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+import CoreBluetooth\r
+\r
+protocol AliCloudIotBleDelegate_C {\r
+ \r
+ /// 从广播数据中解析出Mac地址,用于匹配连接指定mac地址的设备\r
+ /// - Parameter manufacturerData:对应CoreBluetooth的AdvertisementData 的manufacturerData\r
+ /// - Returns: 返回16进制,全大写,以":"分隔的字符串,如"EF:20:11:10:AD:BD"\r
+ func parseMacAddressFromManufacturerDataData(manufacturerData: Data?) -> String?\r
+ \r
+ /// Ble 连接状态变化回调\r
+ /// - Parameters:\r
+ /// - state: Ble 连接状态\r
+ /// - mac: 对应现在的设备mac地址\r
+ func onBleConnectionStateChanged(state: BleStatus_C, mac: String)\r
+ \r
+ /// 接受到Ble数据的回调\r
+ /// - Parameters:\r
+ /// - uuid: 对应uuid\r
+ /// - data: 数据内容\r
+ func onBleDataReceived(uuid: String, data: Data)\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotConnectionState.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+/// mqtt长连接通道连接状态枚举\r
+public enum AliCludIotConnectionState: Int {\r
+ case connected = 1\r
+ case disconnected = 2\r
+ case connecting = 3\r
+ case unknown = -1\r
+}\r
+\r
--- /dev/null
+//\r
+// AliCloudIotDelegate.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/1.\r
+//\r
+\r
+import Foundation\r
+\r
+public protocol AliCloudIotDelegate_C {\r
+\r
+ /// iot断开\r
+ func onIotDisconnected()\r
+\r
+ /// iot已连接\r
+ func onIotConnected()\r
+ \r
+ /// iot连接失败\r
+ func onIotConnecting()\r
+ \r
+ /// 消息已到达,下行数据\r
+ /// - Parameters:\r
+ /// - topic: 订阅主题\r
+ /// - message: 消息体\r
+ func onMessageArrived(topic: String, message: Any?)\r
+\r
+ \r
+ /// iot消息已到达\r
+ /// - Parameters:\r
+ /// - data: 业务数据\r
+ /// - message: 错误消息内容\r
+ /// - code: 错误码\r
+ func onIotMessageArrived(data: String, message: String, code: Int32)\r
+\r
+ \r
+ /// 乘车码消息已到达\r
+ /// - Parameters:\r
+ /// - data: 业务数据\r
+ /// - message: 错误消息内容\r
+ /// - code: 错误码\r
+ func onBusCodeMessageArrived(data: String, message: String, code: Int32)\r
+ \r
+ func onIotLog(log: String)\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotDirectLinkConfig.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+\r
+/// 三元组直连方式\r
+struct AliCloudIotDirectConfig_C: AliCloudIotSdkConfigBase_C {\r
+ \r
+ public var productKey: String\r
+ \r
+ public var deviceName: String\r
+ \r
+ public var server: String\r
+ \r
+ public var port: Int32\r
+ \r
+ let deviceSecret: String\r
+ \r
+ public init(productKey: String, deviceName: String, server: String, deviceSecret: String) {\r
+ self.init(productKey: productKey, deviceName: deviceName, server: server, port: 443, deviceSecret: deviceSecret)\r
+ }\r
+ \r
+ public init(productKey: String, deviceName: String, server: String, port: Int32, deviceSecret: String) {\r
+ self.productKey = productKey\r
+ self.deviceName = deviceName\r
+ self.server = server\r
+ self.port = port\r
+ self.deviceSecret = deviceSecret\r
+ }\r
+}\r
+\r
+class AliCloudIotDirectConfigClass_C: NSObject, AliCloudIotSdkConfigBaseClass_C {\r
+ \r
+ var config: AliCloudIotSdkConfigBase_C;\r
+ \r
+ @objc public init(productKey: String, deviceName: String, server: String, deviceSecret: String) {\r
+ config = AliCloudIotDirectConfig_C(productKey: productKey, deviceName: deviceName, server: server, deviceSecret: deviceSecret)\r
+ }\r
+ \r
+ @objc public init(productKey: String, deviceName: String, server: String, port: Int32, deviceSecret: String) {\r
+ config = AliCloudIotDirectConfig_C(productKey: productKey, deviceName: deviceName, server: server, port: port, deviceSecret: deviceSecret)\r
+ }\r
+ \r
+ public func getConfig() -> ConfigWrapper_C {\r
+ return ConfigWrapper_C.init(config: config);\r
+ }\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotError.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+enum AliCloudIotError_C: Error {\r
+ case sdkNotInit\r
+ case sdkInitError(_ message: String?)\r
+ case notConnected\r
+ case mqttMsgError\r
+ \r
+ \r
+ var code: Int {\r
+ switch self {\r
+ case .sdkNotInit:\r
+ return 1000\r
+ case .sdkInitError(_):\r
+ return 1001\r
+ case .notConnected:\r
+ return 1002\r
+ case .mqttMsgError:\r
+ return 1003\r
+ }\r
+ }\r
+ \r
+ var message: String {\r
+ switch self {\r
+ case .sdkNotInit:\r
+ return "SDK未初始化"\r
+ case .sdkInitError(let message):\r
+ return message ?? "请检查是否已经初始化SDK,以及参数是否正确"\r
+ case .notConnected:\r
+ return "mqtt未连接!"\r
+ case .mqttMsgError:\r
+ return "mqtt 消息格式化异常!"\r
+ }\r
+ }\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotQulianLinkConfig.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+/// ble飞鸟盾方式连接\r
+struct AliCloudIotQulianConfig_C: AliCloudIotSdkConfigBase_C {\r
+ \r
+ public var productKey: String\r
+ \r
+ public var deviceName: String\r
+ \r
+ public var server: String\r
+ \r
+ public var port: Int32\r
+ \r
+ let timestamp: String\r
+ \r
+ let password: String\r
+ \r
+ public init(productKey: String, deviceName: String, server: String, timestamp: String, password: String) {\r
+ self.init(productKey: productKey, deviceName: deviceName, server: server, port: 443, timestamp: timestamp, password: password)\r
+ }\r
+ \r
+ public init(productKey: String, deviceName: String, server: String, port: Int32, timestamp: String, password: String) {\r
+ self.productKey = productKey\r
+ self.deviceName = deviceName\r
+ self.server = server\r
+ self.port = port\r
+ self.timestamp = timestamp\r
+ self.password = password\r
+ }\r
+}\r
+\r
+class AliCloudIotQulianConfigClass_C:NSObject, AliCloudIotSdkConfigBaseClass_C {\r
+ \r
+ var config: AliCloudIotSdkConfigBase_C;\r
+ \r
+ @objc public init(productKey: String, deviceName: String, server: String, timestamp: String, password: String) {\r
+ config = AliCloudIotQulianConfig_C(productKey: productKey, deviceName: deviceName, server: server, timestamp: timestamp, password: password)\r
+ }\r
+ \r
+ @objc public init(productKey: String, deviceName: String, server: String, port: Int32, timestamp: String, password: String) {\r
+ config = AliCloudIotQulianConfig_C(productKey: productKey, deviceName: deviceName, server: server, port: port, timestamp: timestamp, password: password)\r
+ }\r
+ \r
+ public func getConfig() -> ConfigWrapper_C {\r
+ return ConfigWrapper_C.init(config: config);\r
+ }\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotSdkConfigBase.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+protocol AliCloudIotSdkConfigBase_C {\r
+ var productKey: String { get set }\r
+ var deviceName: String { get set }\r
+ var server: String { get set }\r
+ var port: Int32 { get set }\r
+}\r
+\r
+class ConfigWrapper_C: NSObject {\r
+ let config:AliCloudIotSdkConfigBase_C\r
+ init(config:AliCloudIotSdkConfigBase_C) {\r
+ self.config = config\r
+ }\r
+}\r
+ \r
+protocol AliCloudIotSdkConfigBaseClass_C {\r
+ func getConfig() -> ConfigWrapper_C;\r
+}\r
--- /dev/null
+//\r
+// AliCloudIotSdkInitDelegate.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+@objc public protocol AliCloudIotSdkInitDelegate_C {\r
+ \r
+ /// SDK初始化结果,注意:成功不代表MQTT长连接通道建立成功\r
+ /// - Parameters:\r
+ /// - successed: 是否初始化成功\r
+ /// - error: 初始化失败时的错误信息\r
+ func onInit(successed: Bool, error: Error?)\r
+ \r
+ /// SDK注销初始化结果\r
+ /// - Parameters:\r
+ /// - successed: 是否注销初始化成功\r
+ /// - error: 失败时的错误信息\r
+ func onDeinit(successed: Bool, error: Error?)\r
+}\r
--- /dev/null
+//\r
+// MqttState.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+\r
+/// mq状态\r
+enum MqttState_C: UInt8 {\r
+ ///上线成功\r
+ case onlineSuccess = 0x00\r
+ ///鉴权失败\r
+ case authenticationFailure = 0x01\r
+ ///鉴权超时\r
+ case authenticationTimeout = 0x02\r
+ ///下线成功\r
+// case offlineSuccess = 0x03\r
+}\r
--- /dev/null
+//\r
+// SdkMqttDelegate.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+protocol SdkMqttDelegate_C {\r
+ \r
+ /// SDK初始化结果,注意:成功不代表MQTT长连接通道建立成功\r
+ /// - Parameters:\r
+ /// - successed: 是否初始化成功\r
+ /// - error: 初始化失败时的错误信息\r
+ func onInit(successed: Bool, error: Error?)\r
+ \r
+ /// SDK注销初始化结果\r
+ /// - Parameters:\r
+ /// - successed: 是否注销初始化成功\r
+ /// - error: 失败时的错误信息\r
+ func onDeinit(successed: Bool, error: Error?)\r
+ \r
+ /// iot 连接状态改变时回调\r
+ /// - Parameter state: 连接状态\r
+ func onConnectStateChange(state: AliCludIotConnectionState)\r
+ \r
+ /// 接受到消息\r
+ /// - Parameters:\r
+ /// - topic: 主题\r
+ /// - payload: 数据\r
+ func onMessageArrival(topic: String, payload: Any?)\r
+ \r
+ func onIotLog(log: String)\r
+}\r
--- /dev/null
+//\r
+// SdkMqttService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+protocol SdkMqttService_C {\r
+ \r
+ /// mqtt长连接通道连接状态\r
+ var connectionState: AliCludIotConnectionState { get set }\r
+ \r
+ var config: AliCloudIotSdkConfigBase_C? { get set }\r
+ \r
+ var delegate: SdkMqttDelegate_C? { get set }\r
+ \r
+ /// SDK初始化和注销初始化是否在子线程运行\r
+ var sdkInitOrDeInitInAsync: Bool { get set }\r
+ \r
+ func initMqtt(config: AliCloudIotSdkConfigBase_C, delegate: SdkMqttDelegate_C) throws\r
+ \r
+ /// 注销初始化,断开设备与物联网平台的MQTT长连接。\r
+ func deInitMqtt()\r
+ \r
+ /// 发布消息\r
+ /// - Parameters:\r
+ /// - topic: 主题\r
+ /// - data: 数据\r
+ func publishMessage(topic: String, data: Data) throws\r
+ \r
+ /// 发布消息\r
+ /// - Parameters:\r
+ /// - topic: 主题\r
+ /// - data: 数据\r
+ /// - qos: qos\r
+ /// - resultBlock: 结果回调\r
+ func publishMessage(topic: String, data: Data, qos: Int32, resultBlock: @escaping (Bool, Error?) -> Void) throws\r
+ \r
+ \r
+ /// 订阅主题\r
+ /// - Parameter topic: 主题\r
+ func subscribeTopic(topic: String, qos: Int32) throws\r
+ \r
+ /// 取消订阅主题\r
+ /// - Parameter topic: 主题\r
+ func unsubscribeTopic(topic: String) throws\r
+ \r
+ \r
+ /// 检查连接是否已经准备好了\r
+ func checkIsReady() throws\r
+}\r
+\r
+extension SdkMqttService_C {\r
+ \r
+ \r
+ func checkIsReady() throws {\r
+ guard connectionState == .connected else {\r
+ MLog.error("mqtt 未连接")\r
+ throw AliCloudIotError_C.notConnected\r
+ }\r
+ }\r
+}\r
--- /dev/null
+//\r
+// IotMqttLegacyConfig.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/16.\r
+//\r
+\r
+import Foundation\r
+\r
+struct IotMqttLegacyConfig_C {\r
+ \r
+ private let productKey: String\r
+ private let deviceName: String\r
+ let timestamp: String\r
+ \r
+ var clientId: String {\r
+ return "\(productKey)&\(deviceName)|securemode=2,signmethod=hmacsha1,timestamp=\(timestamp)|"\r
+ }\r
+ \r
+ var username: String {\r
+ return "\(deviceName)&\(productKey)"\r
+ }\r
+ \r
+ let password: String\r
+ \r
+ \r
+ init(productKey: String, deviceName: String, deviceSecret: String) {\r
+ let millisecond = Date().milliStamp\r
+ print("当前毫秒级时间戳是 millisecond == ",millisecond)\r
+ \r
+ self.productKey = productKey\r
+ self.deviceName = deviceName\r
+ self.timestamp = Date().milliStamp\r
+ \r
+ let waitProcessMsg = "clientId\(productKey)&\(deviceName)deviceName\(deviceName)productKey\(productKey)timestamp\(timestamp)"\r
+ \r
+// [[IotMQTT shareInstance] mqttLegacyInit:@"gja71QSxX5d.guest_lbh|securemode=2,signmethod=hmacsha256,timestamp=1662106757304|" userName:@"guest_lbh&gja71QSxX5d" passWord:@"c3c25cdf28866a6b0a00fd28975b296d1b324a6b4a9aad21ddf5639b568872af" hostUrl:@"iot-060a09dt.mqtt.iothub.aliyuncs.com"\r
+// port:443];\r
+ \r
+ self.password = waitProcessMsg.hmac(algorithm: .SHA256, key: deviceSecret)\r
+ \r
+ MLog.debug(self.clientId)\r
+ MLog.debug(self.username)\r
+ MLog.debug(self.password)\r
+ }\r
+ \r
+ \r
+ init(productKey: String, deviceName: String, password: String, timestamp: String) {\r
+ \r
+ self.productKey = productKey\r
+ self.deviceName = deviceName\r
+ self.timestamp = timestamp\r
+ self.password = password\r
+ }\r
+}\r
--- /dev/null
+//\r
+// IotMqttManager.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/15.\r
+//\r
+\r
+import Foundation\r
+import AliIotConnectKit_Private.OC\r
+\r
+class IotMqttManager_C: NSObject, SdkMqttService_C {\r
+ \r
+ static let shared = IotMqttManager_C()\r
+ \r
+ private let mqttShared = IotMQTT.shareInstance()\r
+ \r
+ \r
+ /// mqtt长连接通道连接状态\r
+ var connectionState: AliCludIotConnectionState = .unknown {\r
+ didSet {\r
+ delegate?.onConnectStateChange(state: connectionState)\r
+ }\r
+ }\r
+ \r
+ var config: AliCloudIotSdkConfigBase_C?\r
+ \r
+ var delegate: SdkMqttDelegate_C?\r
+ \r
+ /// SDK初始化和注销初始化是否在子线程运行\r
+ var sdkInitOrDeInitInAsync = false\r
+ \r
+ let queue = DispatchQueue(label: "lp_queue")\r
+ \r
+ func initMqtt(config: AliCloudIotSdkConfigBase_C, delegate: SdkMqttDelegate_C) throws {\r
+ self.config = config\r
+ self.delegate = delegate\r
+ \r
+ //NOTE: SDK 有问题,只能自己封装一层object-c代码处理\r
+ setupSdkWrapperCallback()\r
+ \r
+ do {\r
+ \r
+ if config is AliCloudIotDirectConfig_C {\r
+ try startDirectConnect()\r
+ } else if config is AliCloudIotQulianConfig_C {\r
+ try startQulianConnect()\r
+ } else {\r
+ MLog.error("不支持的参数类型")\r
+ }\r
+ }\r
+ }\r
+ \r
+ func mqttInfo() -> String! {\r
+ return MqttManager_C.sharedInstance().mqttInfo()\r
+ }\r
+ \r
+ /// 设置监听事件,由于SDK object-c的回调设置了无效,需要自己手动封装使用\r
+ private func setupSdkWrapperCallback() {\r
+ \r
+ MqttManager_C.sharedInstance().connectStatusBlock = { status in\r
+ MLog.debug("====>>>> 连接状态1\(status)")\r
+ switch status {\r
+ case .DISCONNECTED:\r
+ self.connectionState = .disconnected\r
+ case .CONNECTED:\r
+ self.connectionState = .connected\r
+ default:\r
+ self.connectionState = .unknown\r
+ }\r
+ }\r
+ \r
+ MqttManager_C.sharedInstance().messageArrivalBlock = { topic, payload in\r
+ MLog.debug("====>>>> 接收到消息: \(payload)")\r
+ self.delegate?.onMessageArrival(topic: topic, payload: payload)\r
+ }\r
+ \r
+ MqttManager_C.sharedInstance().debugAddrBlock = { data in\r
+ MLog.debug(data)\r
+ }\r
+ \r
+ MqttManager_C.sharedInstance().logBlock = { log in\r
+// MLog.warning(log)\r
+ self.delegate?.onIotLog(log: log)\r
+ }\r
+ \r
+ //设置接收消息监听\r
+ MqttManager_C.sharedInstance().setupCallback()\r
+ }\r
+ \r
+ \r
+ /// 直接设置SDK的监听事件\r
+ private func setupSdkCallback() {\r
+ //设置连接状态监听状态\r
+ mqttShared.setConnectStatusCallback { status in\r
+ MLog.debug("====>>>> 连接状态\(status)")\r
+ switch status {\r
+ case .DISCONNECTED:\r
+ self.connectionState = .disconnected\r
+ case .CONNECTED:\r
+ self.connectionState = .connected\r
+ default:\r
+ self.connectionState = .unknown\r
+ }\r
+ }\r
+ \r
+ //设置接收消息监听\r
+ mqttShared.setMessageArrivalCallback { topic, payload in\r
+ MLog.debug("====>>>> 接收到消息: \(payload)")\r
+ self.delegate?.onMessageArrival(topic: topic, payload: payload)\r
+ }\r
+ }\r
+ \r
+ /// 三元组直连\r
+ private func startDirectConnect() throws {\r
+ guard let config = (config as? AliCloudIotDirectConfig_C) else {\r
+ MLog.error("startDirectConnect 数据转换异常")\r
+ throw AliCloudIotError_C.sdkInitError(nil)\r
+ }\r
+ \r
+ let initSdk:() -> () = { [self] in\r
+ let result = self.mqttShared.mqttInit(config.productKey, deviceName: config.deviceName, deviceSecret: config.deviceSecret, hostUrl: config.server, port: config.port)\r
+ MLog.debug("三元组直连 初始化 结果: \(result)")\r
+ \r
+ if Thread.isMainThread {\r
+ delegate?.onInit(successed: result == 0, error: nil)\r
+ } else {\r
+ DispatchQueue.main.sync {\r
+ delegate?.onInit(successed: result == 0, error: nil)\r
+ }\r
+ }\r
+ }\r
+ \r
+ if Thread.isMainThread && sdkInitOrDeInitInAsync {\r
+ DispatchQueue.global().async {\r
+ MLog.debug("初始化:异步线程")\r
+ initSdk()\r
+ }\r
+ } else {\r
+ MLog.debug("初始化:主线程")\r
+ initSdk()\r
+ }\r
+ \r
+ /*\r
+ [1663325824.135][LK-0313] MQTT user calls aiot_mqtt_connect api, connect\r
+ \r
+ [1663325824.135][LK-032A] mqtt host: iot-06z00botgwyzu09.mqtt.iothub.aliyuncs.com\r
+ \r
+ [1663325824.135][LK-0317] user name: Device_a&hm13mqT7qPU\r
+ \r
+ [1663325824.135][LK-0318] password: 2959462FF9EFE47191B11A0AB8A7EAC1B3F972925E391B230A5E56E152CD0962\r
+ */\r
+ }\r
+ \r
+ \r
+ /// 曲连\r
+ private func startQulianConnect() throws {\r
+\r
+ guard let config = (config as? AliCloudIotQulianConfig_C) else {\r
+ MLog.error("startQulianConnect 数据转换异常")\r
+ throw AliCloudIotError_C.sdkInitError(nil)\r
+ }\r
+ \r
+ let legacyConfig = IotMqttLegacyConfig_C.init(productKey: config.productKey, deviceName: config.deviceName, password: config.password, timestamp: config.timestamp)\r
+ \r
+ let initSdk:() -> () = { [self] in\r
+ let result = mqttShared.mqttLegacyInit(legacyConfig.clientId, userName: legacyConfig.username, passWord: legacyConfig.password, hostUrl: config.server, port: config.port)\r
+ MLog.debug("曲连 初始化 结果: \(result)")\r
+ \r
+ delegate?.onInit(successed: result == 0, error: nil)\r
+ \r
+ }\r
+ \r
+ if Thread.isMainThread && sdkInitOrDeInitInAsync {\r
+ DispatchQueue.global().async {\r
+ MLog.debug("初始化:异步线程")\r
+ initSdk()\r
+ }\r
+ } else {\r
+ MLog.debug("初始化:主线程")\r
+ initSdk()\r
+ }\r
+ }\r
+ \r
+ func deInitMqtt() {\r
+ let deInitSdk:() -> () = { [self] in\r
+ let result = mqttShared.mqttDeinit()\r
+ MLog.debug("注销 结果: \(result)")\r
+ delegate?.onDeinit(successed: result == 0, error: nil)\r
+ }\r
+ \r
+ if Thread.isMainThread && sdkInitOrDeInitInAsync {\r
+ DispatchQueue.global().async {\r
+ MLog.debug("注销:异步线程")\r
+ deInitSdk()\r
+ }\r
+ } else {\r
+ MLog.debug("注销:主线程")\r
+ deInitSdk()\r
+ }\r
+ \r
+ }\r
+ \r
+ func publishMessage(topic: String, data: Data) throws {\r
+ MLog.debug("====>>>> IotMqttManager publishMessage topic:\(topic)")\r
+\r
+ do {\r
+ try checkIsReady()\r
+ \r
+ try publishMessage(topic: topic, data: data, qos: 1, resultBlock: { (successed: Bool, error: Error?) in\r
+ //\r
+ })\r
+ }\r
+ }\r
+ \r
+ func publishMessage(topic: String, json: [String:Any]) throws {\r
+ MLog.debug("====>>>> 发送mqtt jsoon数据2 \(json)")\r
+\r
+ guard let data = try? JSONSerialization.data(withJSONObject: json) else {\r
+ MLog.error("====>>>> 发送mqtt jsoon数据 数据格式化异常")\r
+ return\r
+ }\r
+\r
+ try publishMessage(topic: topic, data: data)\r
+ \r
+ }\r
+ \r
+// func publishMessage(topic: String, data: Data, qos: Int32, resultBlock: @escaping (Bool, Error?) -> Void) throws {\r
+// do {\r
+// try checkIsReady()\r
+// \r
+// guard let payload = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else {\r
+// MLog.error("====>>>> 数据格式化异常")\r
+// throw AliCloudIotError_C.mqttMsgError\r
+// }\r
+// MLog.debug("====>>>>> 发布消息 topic: \(topic) payload: \(payload)")\r
+// let result = mqttShared.mqttPub(topic, payload: payload as String, qos: qos)\r
+// MLog.debug("====>>>>> 发布消息 结果: \(result)")\r
+// //对于qos=1,返回的>=0的数值表示消息id,返回负数表示失败\r
+// if qos == 1 {\r
+// resultBlock(result >= 0, nil)\r
+// return\r
+// }\r
+// resultBlock(result == 0, nil)\r
+// }\r
+// }\r
+ func publishMessage(topic: String, data: Data, qos: Int32, resultBlock: @escaping (Bool, Error?) -> Void) throws {\r
+ do {\r
+ try checkIsReady()\r
+ let buf = MqttManager_C.data(toPoint: data)!;\r
+ let result = mqttShared.mqttPub(topic, payloadByBytes: buf, length: UInt32(data.count), qos: qos)\r
+ MLog.debug("====>>>>> 发布消息 结果: \(result)")\r
+ //对于qos=1,返回的>=0的数值表示消息id,返回负数表示失败\r
+ if qos == 1 {\r
+ resultBlock(result >= 0, nil)\r
+ return\r
+ }\r
+ resultBlock(result == 0, nil)\r
+ }\r
+ }\r
+ \r
+ func subscribeTopic(topic: String, qos: Int32) throws {\r
+ do {\r
+ try checkIsReady()\r
+ \r
+ let result = mqttShared.mqttSub(topic, qos: 0)\r
+ MLog.debug("订阅主题 结果: \(result)")\r
+ }\r
+ }\r
+ \r
+ func unsubscribeTopic(topic: String) throws {\r
+ do {\r
+ try checkIsReady()\r
+ \r
+ let result = mqttShared.mqttUnSub(topic)\r
+ MLog.debug("取消订阅主题 结果: \(result)")\r
+ }\r
+ }\r
+ \r
+}\r
--- /dev/null
+//
+// IotStateDelatege.swift
+// AliCloudIotKitAdvance
+//
+// Created by Trill Gates on 2023/8/8.
+//
+
+/// +++
+import Foundation
+
+@objc public protocol IotStateDelatege_C {
+
+ //连接成功
+ func onIotConnected()
+
+ //连接失败
+ func onIotConnectFailed()
+
+ //断开连接
+ func onIotDisconeted()
+
+ //连接中
+ func onIotConnecting()
+
+ func onLog(log: String)
+
+}
--- /dev/null
+//
+// AFHttpHelper.h
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2023/1/10.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AFHttpHelper_C : NSObject
+
++ (void)registerDeviceInfoToIotWithParameters:(NSDictionary *)params requestComplete:(void (^)(NSDictionary *JSON,BOOL isSuccess))requestComplete;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+
--- /dev/null
+//
+// AFHttpHelper.m
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2023/1/10.
+//
+
+@import AliIotConnectKit_Private.OC;
+
+#import "AFNetworking.h"
+//@interface AFHttpHelper_C ()
+static AFHTTPSessionManager *manager_C = nil;
+//@end
+
+
+@implementation AFHttpHelper_C
+
++ (AFHTTPSessionManager *)getManager {
+ if (!manager_C) {
+ manager_C = AFHTTPSessionManager.manager;
+ manager_C.requestSerializer.timeoutInterval = 120;
+ manager_C.responseSerializer.acceptableContentTypes = [NSSet setWithArray: @[@"text/plain", @"application/json", @"text/json", @"text/javascript", @"text/html"]];
+ AFSecurityPolicy *policy = [AFSecurityPolicy defaultPolicy];
+ policy.allowInvalidCertificates = YES;
+ policy.validatesDomainName = NO;
+ manager_C.securityPolicy = policy;
+ }
+ return manager_C;
+}
+
++ (void)registerDeviceInfoToIotWithParameters:(NSDictionary *)params requestComplete:(void (^)(NSDictionary *JSON,BOOL isSuccess))requestComplete {
+ NSString *registerDeviceUrl = @"https://iot-auth.cn-shanghai.aliyuncs.com/auth/register/device";
+ [[AFHttpHelper_C getManager] POST:registerDeviceUrl parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
+ NSLog(@"====>>>> 注册设备信息结果 %@", responseObject);
+ NSDictionary *data = responseObject[@"data"];
+ if (requestComplete) {
+ if (data) {
+ NSLog(@"====>>>> 注册设备信息成功");
+ requestComplete(data, YES);
+ }else {
+ NSLog(@"====>>>> 数据解析失败");
+ requestComplete(responseObject, NO);
+ }
+ }
+ } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
+ NSLog(@"====>>>> 网络请求失败");
+ if (requestComplete) {
+ requestComplete(@{}, NO);
+ }
+ }];
+}
+
+
+
+@end
--- /dev/null
+//
+// MqttHelper.h
+// AliIotConnectKit
+//
+// Created by incar on 2023/12/7.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MqttHelper : NSObject
+
++(uint8_t *)dataToPoint:(NSData *)data;
+
++ (NSData*)crc16Data:(NSData *)data;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// MqttHelper.m
+// AliIotConnectKit
+//
+// Created by incar on 2023/12/7.
+//
+
+#import "MqttHelper.h"
+
+@implementation MqttHelper
+
+
++(uint8_t *)dataToPoint:(NSData *)data{
+ uint8_t *buf = static_cast<uint8_t *>(const_cast<void *>(data.bytes));
+ return buf;
+}
+
+
+
+//+ (NSData*)crc16Data:(NSData *)data {
+// const uint8_t *byte = (const uint8_t *)data.bytes;
+// unsigned long length = ( unsigned long)data.length;
+// uint16_t res = crc16_ccitt_false(byte, length,NULL);
+//
+// NSData *val = [NSData dataWithBytes:&res length:2];
+//
+// return val;
+//}
+//
+//uint16_t crc16_ccitt_false(uint8_t const * p_data, u_long size, uint16_t const * p_crc)
+//{
+// uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
+// uint8_t b;
+//
+// for (u_long i = 0; i < size; i++) {
+// for (uint8_t j = 0; j < 8; j++) {
+// b = ((p_data[i] << j) & 0x80) ^ ((crc & 0x8000) >> 8);
+// crc <<= 1;
+// if(b != 0) {
+// crc ^= 0x1021;
+// }
+// }
+// }
+//
+// return crc;
+//}
+
+
++ (NSData*)crc16Data:(NSData *)data {
+ uint8_t *buf = static_cast<uint8_t *>(const_cast<void *>(data.bytes));
+ uint32_t length = ( uint32_t)data.length;
+ uint16_t res = crc16_ccitt_false(buf, length);
+
+ NSData *val = [NSData dataWithBytes:&res length:2];
+
+ return val;
+}
+
+uint16_t crc16_ccitt_false(uint8_t *di, uint32_t len)
+{
+ uint16_t crc_poly = 0x8005; //X^16+X^15+X^2+1 total 16 effective bits without X^16. Computed total data shall be compensated 16-bit '0' before CRC computing.
+
+ uint32_t clen = len+2;
+ uint8_t cdata[clen] ;
+ for(uint32_t j=0;j<len;j++)
+ {
+ cdata[j] = 0;
+
+ for(uint8_t m=0; m<=7; m++)
+ {
+ cdata[j] |= ( ( di[j]>>(7-m) ) & 1 ) << m;
+ }
+ }
+ cdata[len]=0; cdata[len+1]=0;
+
+ uint16_t data_t = (((uint16_t)cdata[0]) << 8) + cdata[1]; //CRC register
+
+ for (uint32_t i = 2; i < clen; i++)
+ {
+ for (uint8_t j = 0; j <= 7; j++)
+ {
+ if(data_t&0x8000)
+ data_t = ( (data_t<<1) | ( (cdata[i]>>(7-j))&0x01) ) ^ crc_poly;
+ else
+ data_t = ( (data_t<<1) | ( (cdata[i]>>(7-j))&0x01) ) ;
+ }
+ }
+
+ uint16_t i_data_t = 0;
+
+ for(uint8_t n=0; n<=15; n++)
+ {
+ i_data_t |= ( ( data_t>>(15-n) ) & 1 ) << n;
+ }
+
+ return i_data_t;
+}
+
+@end
--- /dev/null
+//\r
+// MqttManagerHeader.h\r
+// AliCloudIotKit\r
+//\r
+// Created by xzh on 2022/9/19.\r
+//\r
+\r
+#import <AliIotConnectKit/iotMqtt.h>\r
+#import <Foundation/Foundation.h>\r
+#ifndef MqttManager_h\r
+#define MqttManager_h\r
+\r
+\r
+typedef void (^ _Nonnull ConnectStatusBlock)(MqttConnectStatus_C);\r
+typedef void (^ _Nonnull MessageArrivalBlock)(NSString* _Nonnull topic, NSString * _Nonnull payload);\r
+typedef void (^ _Nonnull MQTTLogBlock)(NSString * _Nonnull log);\r
+\r
+@interface MqttManager_C : NSObject\r
+\r
+@property (nonatomic,strong) ConnectStatusBlock connectStatusBlock;\r
+@property (nonatomic,strong) MessageArrivalBlock messageArrivalBlock;\r
+@property (nonatomic,strong) MQTTLogBlock logBlock;\r
+\r
+@property (nonatomic, strong) void(^ _Nullable DebugAddrBlock)(id _Nonnull data);\r
+- (void)setupCallback;\r
+- (NSString *)mqttInfo;\r
++ (MqttManager_C*)sharedInstance;\r
+\r
+\r
++(uint8_t *)dataToPoint:(NSData *)data;\r
+\r
++ (NSData*)crc16Data:(NSData *)data;\r
+@end\r
+\r
+\r
+#endif /* MqttManager_h */\r
--- /dev/null
+//\r
+// MqttManager.m\r
+// AliCloudIotKit\r
+//\r
+// Created by xzh on 2022/9/19.\r
+//\r
+\r
+#import <Foundation/Foundation.h>\r
+//#import "MqttManager_C.h"\r
+#import "MqttHelper.h"\r
+@import AliIotConnectKit_Private.OC;\r
+\r
+void sdk_iot_mqtt_log_callback(const char * _Nonnull log) {\r
+ !MqttManager_C.sharedInstance.logBlock ? : MqttManager_C.sharedInstance.logBlock([NSString stringWithFormat:@"iot mqtt log ==> %@",[NSString stringWithCString:log encoding:NSUTF8StringEncoding]]);\r
+}\r
+\r
+@interface MqttManager_C ()\r
+\r
+@end\r
+\r
+@implementation MqttManager_C\r
+\r
++ (MqttManager_C*)sharedInstance{\r
+ static dispatch_once_t once;\r
+ static MqttManager_C *sharedInstance;\r
+ dispatch_once(&once,^{\r
+ sharedInstance = [[MqttManager_C alloc]init];\r
+// register_log_callback(sdk_iot_mqtt_log_callback);\r
+ });\r
+ return sharedInstance;\r
+}\r
+\r
+- (void)setupCallback {\r
+// __weak typeof(self) weak_self = self;\r
+ \r
+ [[IotMQTT shareInstance] setConnectStatusCallback:^(MqttConnectStatus_C ConnectStatus) {\r
+ NSLog(@"---------------connect status :%lu", ConnectStatus);\r
+// if (weak_self.DebugAddrBlock)\r
+ MqttManager_C.sharedInstance.DebugAddrBlock(@{@"mqtt addr": IotMQTT.shareInstance,\r
+ @"mqtt mgr addr " : [NSString stringWithFormat:@"%@ - %@", MqttManager_C.sharedInstance, MqttManager_C.sharedInstance]\r
+ });\r
+ \r
+ MqttManager_C.sharedInstance.connectStatusBlock(ConnectStatus);\r
+ }];\r
+ \r
+ [[IotMQTT shareInstance] setMessageArrivalCallback:^(NSString * _Nonnull topic, NSString * _Nonnull payload) {\r
+ NSLog(@"---------topic :%@", topic);\r
+ NSLog(@"payload :%@", payload);\r
+ \r
+ MqttManager_C.sharedInstance.DebugAddrBlock(@{@"mqtt addr": IotMQTT.shareInstance,\r
+ @"mqtt mgr addr " : [NSString stringWithFormat:@"%@ - %@", MqttManager_C.sharedInstance, MqttManager_C.sharedInstance]\r
+ });\r
+ \r
+ MqttManager_C.sharedInstance.messageArrivalBlock(topic, payload);\r
+ }];\r
+}\r
+\r
+- (NSString *)mqttInfo {\r
+ id s = IotMQTT.shareInstance;\r
+// return [NSString stringWithFormat:@"shared: %@, clientId: %@, host: %@, port: %@",\r
+// [NSString stringWithFormat:@"mem addr: %p <-> %p, pointer addr: %p",IotMQTT.shareInstance,s, &s],\r
+// (IotMQTT.shareInstance.clientId ? IotMQTT.shareInstance.clientId : @"-"),\r
+// (IotMQTT.shareInstance.hostUrl ? IotMQTT.shareInstance.hostUrl : @"-"),\r
+// @(IotMQTT.shareInstance.port)\r
+// ];\r
+//\r
+// return [NSString stringWithFormat:@"shared: %@, clientId: %@, host: %@, port: %@",\r
+ return [NSString stringWithFormat:@"mem addr: %p <-> %p, pointer addr: %p",IotMQTT.shareInstance,s, &s];\r
+// (IotMQTT.shareInstance.clientId ? IotMQTT.shareInstance.clientId : @"-"),\r
+// (IotMQTT.shareInstance.hostUrl ? IotMQTT.shareInstance.hostUrl : @"-"),\r
+// @(IotMQTT.shareInstance.port)\r
+// ];\r
+}\r
+\r
++(uint8_t *)dataToPoint:(NSData *)data{\r
+ return [MqttHelper dataToPoint:data];\r
+}\r
++ (NSData*)crc16Data:(NSData *)data {\r
+ NSData *data1 = [MqttHelper crc16Data:data];\r
+ return data1;\r
+}\r
+\r
+@end\r
--- /dev/null
+//\r
+// AliCloudIotAutomaticService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+\r
+@objcMembers class AliCloudIotAutomaticService_C: NSObject {\r
+ static let shared = AliCloudIotAutomaticService_C()\r
+ \r
+ private var timeStampStr: String?\r
+ \r
+ func connect(mac: String) {\r
+ \r
+ }\r
+ \r
+ /// 发送mqtt连接状态到设备\r
+ /// - Parameter state: mqtt连接状态\r
+ func sendMqttStateToDevice(state: MqttState_C) {\r
+ var data = Data()\r
+ data.append(contentsOf: [0x03, 0x01, 0x00, 0x55])\r
+ data.append(state.rawValue)\r
+ BleService_C.shared.sendBleData(data: data)\r
+ }\r
+ \r
+ \r
+ /// 设备认证处理\r
+ func deviceAuth() {\r
+ let now = Int64(Date().timeIntervalSince1970 * 1000)\r
+ timeStampStr = "\(now)"\r
+ guard let timeData = "\(now)".data(using: String.Encoding.utf8) else {\r
+ return\r
+ }\r
+ \r
+ var data = Data()\r
+ data.append(contentsOf: [0x01, 0x0d, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33])\r
+ for i in 0..<13 {\r
+ \r
+ data[i + 3] = timeData[i]\r
+ }\r
+ BleService_C.shared.sendBleData(data: data)\r
+ }\r
+}\r
+\r
+extension AliCloudIotAutomaticService_C: AliCloudIotBleDelegate_C {\r
+ \r
+ func parseMacAddressFromManufacturerDataData(manufacturerData: Data?) -> String? {\r
+ //TODO: 解析逻辑\r
+ return nil\r
+ }\r
+ \r
+ public func onBleConnectionStateChanged(state: BleStatus_C, mac: String) {\r
+ guard state == .connected else {\r
+ return\r
+ }\r
+ \r
+ deviceAuth()\r
+ }\r
+ \r
+ public func onBleDataReceived(uuid: String, data: Data) {\r
+ let productKey = data[3..<(3 + 11)]\r
+ let deviceName = data[(3 + 11)..<(3 + 11 + 29)]\r
+ let passWd = data[(3 + 11 + 29)..<(3 + 11 + 29 + 40)]\r
+ \r
+ guard let productKey = String.init(data: productKey, encoding: .utf8), let deviceName = String.init(data: deviceName, encoding: .utf8), let passWd = String.init(data: passWd, encoding: .utf8) else {\r
+ MLog.error("数据解析错误")\r
+ return\r
+ }\r
+ \r
+ MLog.debug(" productKey: \(productKey) \n deviceName: \(deviceName) \n timeStamp: \(timeStampStr) \n password: \(passWd)")\r
+\r
+ //TODO: deviceSecret 如何处理?\r
+// aliFdIotObject.setDeviceInfo(productKey, deviceName);\r
+// aliFdIotObject.setAuthCode(timeStamp, passWd);\r
+// aliFdIotObject.connect();\r
+ }\r
+}\r
--- /dev/null
+//
+// BleBaseSendService.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/28.
+//
+
+import Foundation
+
+class BleBaseSendService_C: NSObject {
+ func sendBleData(data: Data) -> Void {
+ if (AliConnectMananger_C.shared.bleSendDataDelegate != nil) {
+ AliConnectMananger_C.shared.bleSendDataDelegate?.sendBleData(data: data)
+ }else {
+ if checkBleStatus() {
+ AliCloudIotWatchManager_C.shared.bleService.sendBleData(data: data)
+ }
+ }
+ }
+ @objc func checkBleStatus() -> Bool {
+ if BleManager_C.shared.isConnected {
+ return true
+ }
+ return false
+ }
+
+}
+
--- /dev/null
+//\r
+// BleService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/6.\r
+//\r
+\r
+import Foundation\r
+/// import RXSWift\r
+\r
+class BleService_C: NSObject {\r
+ static let shared = BleService_C()\r
+ \r
+ //蓝牙版本号-检查飞鸽书的时候获取\r
+ var bleVer:String = "1.0"\r
+ \r
+ fileprivate let disposeBag = DisposeBag()\r
+ \r
+ /// 扫描连接指定设备\r
+ /// - Parameters:\r
+ /// - mac: mac地址\r
+ /// - scanTimeout: 扫描超时时间\r
+ /// - delegate: ble 相关回调\r
+ public func connectBle(mac: String, scanTimeout: Int = 30, delegate: AliCloudIotBleDelegate_C) {\r
+ let bleManager = BleManager_C.shared\r
+ bleManager.externalDelegate = delegate\r
+ let model = BleDeviceModel_C.init(mac: mac)\r
+ bleManager.connect(device: model, peripheral: nil, scanTimeout: scanTimeout)\r
+ }\r
+ \r
+ /// 断开连接设备\r
+ public func disconnect() {\r
+ let bleManager = BleManager_C.shared\r
+ bleManager.disconnectCurrentDevice()\r
+ bleManager.externalDelegate = nil\r
+ }\r
+ \r
+ /// 发送数据到BLE\r
+ /// - Parameter data: 数据\r
+ public func sendBleData(data: Data) {\r
+ BleManager_C.shared.send(data: data)\r
+ .subscribe { _ in\r
+ // do nothing\r
+ } onError: { error in\r
+ let bleError = BleError_C.fromError(error)\r
+ MLog.error(bleError)\r
+ } onCompleted: {\r
+ //\r
+ }\r
+ .disposed(by: disposeBag)\r
+ }\r
+}\r
--- /dev/null
+//
+// Data_category.swift
+// AliIotConnectKit
+//
+// Created by incar on 2023/12/5.
+//
+
+import Foundation
+extension Data {
+ struct HexEncodingOptions: OptionSet {
+ let rawValue: Int
+ static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
+ }
+
+ func hexEncodedString(options: HexEncodingOptions = []) -> String {
+ let format = options.contains(.upperCase) ?"%02hhX" :"%02hhx"
+ return map { String(format: format, $0) }.joined()
+ }
+ func toString()->String{
+ return String(data: self, encoding: .ascii) ?? ""
+ }
+
+ func toInt()->Int{
+ var receive1: Int = 0
+ (self as NSData).getBytes(&receive1, length: 8)
+ return receive1
+ }
+ func toInt16()->Int16{
+ var receive1: Int16 = 0
+ (self as NSData).getBytes(&receive1, length: 16)
+ return receive1
+ }
+
+ static func int16ToData(num:Int16,isLittle:Bool = true) ->Data{
+ var value = num
+ if isLittle{
+ var data = Data(bytes: &value, count: 2)
+ return data
+ }
+ else{
+ var data = Data(bytes: &value, count: 2)
+ var data1 = Data()
+ data1.append(data.subdata(in: Range(NSMakeRange(1, 1))!))
+ data1.append(data.subdata(in: Range(NSMakeRange(0, 1))!))
+ return data1
+ }
+ }
+}
+
+class CRC16Data{
+ static func crc16(_ data:Data) ->Data{
+ var crc = CRC16Data.xmodem(arr: data.bytes)
+ let data = Data(bytes: &crc, count: 2)
+ return data
+ }
+ public static func xmodem(arr: [UInt8]) -> Int {
+ var wCRCin = 0x0000;
+ let wCPoly = 0x1021;
+ let len = arr.count;
+ for i in 0...len-1 {
+ for j in 0...7 {
+ let bit = ((arr[i] >> (7 - j) & 1) == 1);
+ let c15 = ((wCRCin >> 15 & 1) == 1);
+ wCRCin <<= 1;
+ if(c15 != bit) {
+ wCRCin ^= wCPoly;
+ }
+ }
+ }
+ wCRCin &= 0xFFFF;
+ wCRCin ^= 0x0000;
+ return wCRCin
+ }
+}
--- /dev/null
+//
+// FGSCheckService.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/25.
+//
+
+import Foundation
+
+/// 检查飞鸽书
+class FGSCheckService_C: BleBaseSendService_C {
+
+ static let shared = FGSCheckService_C()
+
+ var checkFgsState : checkFgsStateComplete?
+ var bleAuthModel :BleAuthModel_C?//蓝牙设备三元组信息
+
+ /// 发起飞鸽书请求
+ open func checkFgsState(result:@escaping checkFgsStateComplete) -> Void {
+ checkFgsState = result
+ let json = [
+ "sid":BleSIdentifier_C.fgs.rawValue,
+ "data": bleDataToString(json: [
+ "msg_type":FGSMsgType_C.startReq.rawValue,
+ "sidver":1
+ ]),
+ "ver":1
+ ] as [String : Any]
+ MLog.debug("====>>>> 发送蓝牙飞鸽书请求 \(json)")
+// JSONSerialization.data(withJSONObject: json)
+ guard let data = try? JSONSerialization.data(withJSONObject: json) else {
+ MLog.error("====>>>> 发送蓝牙飞鸽书请求,数据转码失败 ")
+ return }
+ sendBleData(data: data)
+ }
+
+ override func checkBleStatus() -> Bool {
+ if BleManager_C.shared.isConnected {
+ return true
+ }
+ checkFgsState?(false, ["msg":"蓝牙未连接"])
+ return false
+ }
+}
+
+extension FGSCheckService_C {
+ //接收来着蓝牙数据
+ func didReceiveBleData(data:[String:Any]) {
+ if let type = FGSMsgType_C.init(rawValue: data["msg_type"] as! String) {
+ switch type {
+ case .startResp:
+ MLog.debug("====>>>> 返回飞鸽书消息\(data)")
+ guard let tripplestatus:String = data["tripplestatus"] as? String else {
+ MLog.error("====>>>> 有三元组信息 但找不到 tripplestatus")
+ checkFgsState?(false, ["msg":"找不到 tripplestatus "])
+ return }
+ if tripplestatus == "needfetch" {
+ getAuthReqInfo()
+ }else {
+ MLog.debug("====>>>> 有三元组信息2")
+ checkFgsState?(true, ["msg":"success"])
+ }
+
+ guard let sdk_ver:Int = data["sidver"] as? Int else {
+ MLog.error("====>>>> 没有获取到SDK版本")
+ return }
+ BleService_C.shared.bleVer = "\(sdk_ver)"
+ case .authResp:
+ MLog.debug("====>>>> 返回动态注册凭证 \(data)")
+ parseBleInfo(jsonData: data)
+ case .downloadResp:
+ MLog.debug("====>>>> 返回数据写入结果 \(data)")
+ checkFgsState?(true, ["msg":"success"])
+ default :
+ MLog.debug("====>>>> 其它")
+ }
+ }
+ }
+
+//开始获取三元组信息
+ func getAuthReqInfo() -> Void {
+ let json = [
+ "sid":BleSIdentifier_C.fgs.rawValue,
+ "data":
+ bleDataToString(json: [
+ "msg_type":FGSMsgType_C.authReq.rawValue,
+ "timeStamp":Date().milliStamp,
+ "message":"success",
+ "sidver":1
+ ]),
+ "ver":1
+
+ ] as [String : Any]
+ MLog.debug("====>>>> 开始获取三元组信息 \(json)")
+ guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
+ MLog.error("====>>>> 开始获取三元组信息,数据转码失败 ")
+ checkFgsState?(false, ["msg":"获取三元组信息,数据转码失败"])
+ return }
+ sendBleData(data: data)
+ }
+
+ //开始解析蓝牙返回的三元组信息
+ func parseBleInfo(jsonData:[String:Any]) -> Void {
+// BleAuthModel
+ if let data = try? JSONSerialization.data(withJSONObject: jsonData, options: []) {
+ if let model = try? JSONDecoder().decode(BleAuthModel_C.self, from: data) {
+ bleAuthModel = model
+ MLog.debug("====>>>> 成功解析蓝牙返回的三元组信息")
+ registerDeviceInfoToIot()
+ return
+ }
+ }
+ checkFgsState?(false, ["msg":"解析蓝牙设备返回三元组信息失败"])
+ MLog.error("====>>>> 解析蓝牙返回的三元组信息失败")
+ }
+
+ func registerDeviceInfoToIot() -> Void {
+ MLog.debug("====>>>> 开始注册设备信息")
+ let signStr = "deviceName\(bleAuthModel!.deviceName)productKey\(bleAuthModel!.productKey)random\(bleAuthModel!.timeStamp)"
+ let sign = signStr.hmac(algorithm: CryptoAlgorithm_C.SHA1, key: bleAuthModel!.sonce)
+// MLog.debug("====>>>> 加密前: \(signStr) 加密后 : \(sign)")
+
+ let params = [
+ "productKey" : bleAuthModel!.productKey,
+ "deviceName" : bleAuthModel!.deviceName,
+ "random" : bleAuthModel!.timeStamp,
+ "signMethod" : bleAuthModel!.signmethod,
+ "sign" : bleAuthModel!.sign
+ ] as [String : Any]
+ MLog.debug("====>>>> 参数请求 \(params)")
+ HttpTool_C.shared.registerDeviceInfoToIot(parameters: params) { [self] isSuccess, result in
+ if isSuccess {
+ let deviceSecret:String = result["deviceSecret"] as! String//设备秘钥
+ MLog.debug("====>>>> 注册设备信息成功,返回 设备秘钥\(deviceSecret)")
+ writeDeviceInfoToBle(deviceSecret:deviceSecret)
+ }else {
+ MLog.error("====>>>>" + String(describing: result))
+ checkFgsState?(false, ["msg": "错误码:\(String(describing: result["code"])) 错误信息:\(String(describing: result["message"]))" as Any])
+ }
+ }
+ }
+//
+// open func testEncodeCBC() {
+//
+// let encryptData = AESEncode.aes_encrypt("deviceSecret", aes_key: "kekemimi1236m76g", iv: [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F])
+// MLog.debug("====>>>> 加密字符串: \(encryptData)")
+// }
+
+ func encodeByCBC(deviceSecret: String) -> String {
+ let secretKey = getSecretKey()
+// MLog.debug("====>>>> 开始aes加密 deviceSecret : \(deviceSecret), secretKey : \(secretKey)")
+
+ let iv:Array<UInt8> = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F]
+ let encryptData = AESEncode_C.aes_encrypt(deviceSecret, aes_key: secretKey, iv: iv)
+// MLog.debug("====>>>> aes加密结果 \(encryptData)")
+ return encryptData
+ }
+
+ func getSecretKey() -> String {
+ let sonce:String = bleAuthModel!.sonce
+ let timeStamp:String = "\(bleAuthModel!.timeStamp)"
+ let productKey:String = bleAuthModel!.productKey
+
+ if sonce.count < 8 {
+ MLog.error("====>>>> sonce 小于8字节")
+ checkFgsState?(false, ["msg":"sonce 小于8字节"])
+ return ""
+ }
+
+ if timeStamp.count < 4 {
+ MLog.error("====>>>> ts 小于4个字节")
+ checkFgsState?(false, ["msg":"ts 小于4个字节"])
+ return ""
+ }
+
+ if productKey.count < 4 {
+ MLog.error("====>>>> pk 小于4个字节")
+ checkFgsState?(false, ["msg":"pk 小于4个字节"])
+ return ""
+ }
+
+ let secretKey = String(sonce.prefix(8)+timeStamp.prefix(4)+productKey.prefix(4))
+
+ return secretKey
+
+ }
+
+//开始写入三元组信息
+ func writeDeviceInfoToBle(deviceSecret: String) -> Void {
+ MLog.debug("====>>>> 蓝牙设备信息写入")
+ let encryptData = encodeByCBC(deviceSecret: deviceSecret)
+ if encryptData.count == 0 {
+ MLog.error("====>>>> 三元组信息加密失败")
+
+ checkFgsState?(false, ["msg":"三元组信息加密失败"])
+ return
+ }
+ let json = [
+ "sid":BleSIdentifier_C.fgs.rawValue,
+ "data":
+ bleDataToString(json: [
+ "msg_type":FGSMsgType_C.downloadReq.rawValue,
+ "code":200,
+ "deviceSecret":encryptData,
+ "message":"success",
+ "sidver":1
+ ]),
+ "ver":1
+ ] as [String : Any]
+ MLog.debug("====>>>> 开始写入三元组信息 \(json)")
+ guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
+ MLog.error("====>>>> 写入三元组信息,数据转码失败 ")
+ checkFgsState?(false, ["msg":"三元组信息,数据转码失败"])
+ return }
+ sendBleData(data: data)
+ }
+}
--- /dev/null
+//
+// ID2Service.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/27.
+//
+
+import Foundation
+/// import SwiftyJSON
+
+class ID2Service_C: BleBaseSendService_C {
+ static let shared = ID2Service_C()
+
+ //发送id2数据给蓝牙
+ func sendId2ToBle(topic: String, msg: String) -> Void {
+ let json = [
+ "sid":BleSIdentifier_C.gtd.rawValue,
+ "data":
+ bleDataToString(json: [
+ "topic":topic,
+ "msg":msg
+ ]),
+ "ver":1
+ ] as [String : Any]
+ MLog.debug("====>>>> 发送蓝牙id2 数据 \(json)")
+ guard let data = try? JSONSerialization.data(withJSONObject: json) else {
+ MLog.debug("====>>>> 发送蓝牙id2 数据,数据转码失败 ")
+ return }
+ sendBleData(data: data)
+ }
+}
+
+extension ID2Service_C {
+ //接收来着蓝牙数据
+ func didReceiveBleData(data:[String:Any]) {
+ MLog.debug("====>>>> GTD didReceiveBleData")
+ if let topic: String = data["topic"] as? String, let msg: String = data["msg"] as? String {
+ MLog.debug("====>>>> 解析收到的蓝牙GTD消息 msg: \(msg)")
+ let jsonData = msg.data(using: .utf8)
+ if let json = try! JSON(data: jsonData!).dictionaryObject {
+ ThingCommunicateService_C.shared.setupSdkService(sdkService: IotMqttManager_C.shared)
+
+ let sendJson = [
+ "id":IDGenerater_C.shared.sequenceId,
+ "method":ThingCommunicateService_C.getMethod(topic: topic),
+ "version":"1.0",
+ "params":json
+ ] as [String : Any]
+ MLog.debug("====>>>> 开始发送GTD 数据 \(sendJson)")
+
+ do {
+ try IotMqttManager_C.shared.publishMessage(topic: ThingCommunicateService_C.addTopicPrefix(topic: topic), json: sendJson)
+// try ThingCommunicateService.shared.publishJsonMessage(topic: ThingCommunicateService.addTopicPrefix(topic: topic), obj: sendJson)
+ } catch {
+ MLog.debug("====>>>> 向后台发送GTD数据失败")
+ }
+ }else {
+ MLog.debug("====>>>> 解析收到的蓝牙GTD消息 失败")
+ }
+
+ }else {
+ MLog.debug("====>>>> 收到GTD消息,没有topic或msg")
+ }
+ }
+
+ //回复iot平台
+ func id2Reply(topicFragment: String, dataStr: [String:Any], id: String) -> Void {
+ MLog.debug("====>>>> 收到id2数据,开始进行回复")
+ let replyTopic = ThingCommunicateService_C.shared.thingTopicPrefix! + "/" + topicFragment + "_reply"
+ MLog.debug("====>>>> 收到id2数据,开始进行回复 \(replyTopic)")
+
+ let replyJson = [
+ "id" : id,
+ "code" : 200,
+ "data" : [:]
+ ] as [String : Any]
+
+ MLog.debug("====>>>> id2 回复数据 \(replyJson)")
+ do {
+ try IotMqttManager_C.shared.publishMessage(topic: replyTopic, json: replyJson)
+ } catch {
+ MLog.debug("====>>>> 向后台发送id2数据失败")
+ }
+ }
+}
--- /dev/null
+//\r
+// IotService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+class IotService_C: NSObject {\r
+ static let shared = IotService_C()\r
+ \r
+ /// 上报iot数据\r
+ /// - Parameters:\r
+ /// - eventContent: 时间内容\r
+ /// - trackId: id\r
+ public func uploadData(eventContent: String, trackId: String?) throws {\r
+ //NOTE: trackId 判空处理?\r
+ let event = WearCommonEvent_C(eventIdentifier: EventIdentifier_C.iot.rawValue, eventContent: eventContent, trackId: trackId)\r
+ try ThingCommunicateService_C.shared.wearCommonEventUpload(event: event)\r
+ }\r
+\r
+}\r
--- /dev/null
+//
+// LPConnectService.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/27.
+//
+
+import Foundation
+
+enum IOTMqttState {
+ case ready
+ case connecting
+ case connected
+ case disconnecting
+ case disconnected
+}
+
+extension IOTMqttState: CustomStringConvertible {
+ var description: String {
+ switch self {
+ case .ready: /// 从未发起连接, 主动 init 失败, 或调用 deinit 之后返回
+ return "准备就绪"
+ case .connecting: /// 主动连接 init
+ return "连接中"
+ case .connected: /// 收到 mqtt 连接成功毁掉
+ return "已连接"
+ case .disconnecting: ///主动断开 deinit
+ return "断开中"
+ case .disconnected: /// 收到 mqtt 已断开毁掉
+ return "已断开"
+ }
+ }
+}
+
+let server = "iot-06z00c6kgq6t1w4.mqtt.iothub.aliyuncs.com"
+
+class LPConnectService_C: BleBaseSendService_C {
+
+ static let shared = LPConnectService_C()
+ //iot创建连接block
+ var connectLpState : fndStateComplete? {
+ didSet {
+ guard let connectLpState = connectLpState else { return }
+ }
+ }
+ //发起请求时间戳
+ var sendDataTime : String?
+
+ var iotStateDelegate:IotStateDelatege_C?
+
+ var appVersion : String!
+
+ private var countdownTimer: Timer?
+
+ var canConnect: IOTMqttState = .ready {
+ didSet {
+ let log = "\(Thread.callStackSymbols.forEach{print($0)}) ====>>>> [新流程] LP 状态更新: \(canConnect.description)"
+ MLog.debug(log)
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: log)
+
+
+ if canConnect == .connecting {
+ var countdownNum = 5
+ countdownTimer = Timer(timeInterval: 1.0, repeats: true) { [unowned self] timer in
+
+ let log = " ====>>>> [新流程] LP 倒计时: "
+
+ if countdownNum == 0 {
+ timer.invalidate()
+ canConnect = .ready
+ } else {
+ countdownNum -= 1
+ }
+ MLog.debug(log + String(describing: countdownNum))
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: log + String(describing: countdownNum))
+ }
+
+ countdownTimer?.tolerance = 0.2
+
+ RunLoop.main.add(countdownTimer!, forMode: .default)
+
+ countdownTimer?.fire()
+ }
+ }
+ }
+
+ var disconnRes: fndStateComplete? {
+ didSet {
+
+ }
+ }
+
+ override init() {
+ super.init()
+ }
+
+ //开始连接
+ func startConnectLpState(result:@escaping fndStateComplete) -> Void {
+ if canConnect != .ready {
+ let log = "====>>>> [新流程] LP \(canConnect.description), 调用失败"
+ MLog.debug(log)
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: log)
+ result(false, ["msg":"调用失败"])
+ return
+ }
+
+ canConnect = .connecting
+
+ MLog.debug("====>>>> 开始飞鸟盾认证请求")
+ connectLpState = result
+ sendDataTime = Date().milliStamp
+ guard let timeData = sendDataTime?.data(using: String.Encoding.utf8) else {
+ return
+ }
+ var data = Data()
+ data.append(contentsOf: [0x01,0x0d,0x00,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x31,0x32,0x33])
+ for i in 0..<13 {
+ data[i + 3] = timeData[i]
+ }
+ let encodeStr = data.base64EncodedString()
+
+ let json = [
+ "sid":BleSIdentifier_C.fnd.rawValue,
+ "data":encodeStr,
+ "ver":1
+
+ ] as [String : Any]
+ MLog.debug("====>>>> 飞鸟盾发起认证 \(json)")
+ guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
+ MLog.debug("====>>>> 飞鸟盾发起认证,数据转码失败 ")
+ connectLpState?(false, ["msg":"飞鸟盾发起认证,数据转码失败"])
+ canConnect = .ready
+ sendMqttStateToDevice(state: MqttState_C.authenticationFailure)
+ return }
+ sendBleData(data: data)
+
+ }
+
+ //主动断开链接
+ open func disConnect(result:@escaping fndStateComplete) -> Void {
+ if canConnect != .connected && // 没点接
+ (
+ canConnect == .connecting || // 连接中
+ canConnect == .disconnecting || // 断开中
+ canConnect == .ready
+ ) {
+ MLog.debug("====>>>> [新流程] LP \(canConnect.description) , 调用失败")
+ result(false, ["msg":"调用失败"])
+ return
+ }
+
+ connectLpState = nil
+
+ disconnRes = result
+
+ canConnect = .disconnecting
+ AliCloudIotWatchManager_C.shared.deInitSdk()
+ }
+
+ func startIotLogin(productKey:String, deviceName:String, passWd:String) -> Void {
+ MLog.debug("====>>>> 开始连接iot平台 productKey: \(productKey) deviceName: \(deviceName) passWd: \(passWd) timestamp:\(sendDataTime!)")
+ let config = AliCloudIotQulianConfig_C(productKey: productKey,
+ deviceName: deviceName,
+ server: server,
+ timestamp: sendDataTime!,
+ password:passWd)
+
+ /*
+ AliCloudIotQulianConfig_C(productKey:"a1unMrw34rM",
+ deviceName: "A306FEFA3FB2",
+ server: "iot-06z00c6kgq6t1w4.mqtt.iothub.aliyuncs.com",
+ timestamp: "1693464021430",
+ password:"FB6FD32CF66886FFAF7E2BA00D4F77458160C6CF")
+ */
+ do {
+ try AliCloudIotWatchManager_C.shared.initSdk(config: config, sdkDelegate: self, iotDelegate: self)
+ } catch {
+ MLog.error(error)
+ connectLpState?(false, ["msg":"连接iot平台失败"])
+ //连接失败
+ AliConnectMananger_C.shared.iotStateDelegate?.onIotConnectFailed()
+ canConnect = .ready
+ sendMqttStateToDevice(state: MqttState_C.authenticationFailure)
+ }
+ }
+ // 开始连接iot平台 productKey: a1WZqR1Jgqq deviceName: 4142FFFF0004 passWd: 7244d273ff2fea28411d7d21898d7e84ae379907 server:iot-06z00c6kgq6t1w4.mqtt.iothub.aliyuncs.com timestamp:1692157661780
+ // 开始连接iot平台 productKey: a1WZqR1Jgqq deviceName: 4142FFFF0004 passWd: 81e4dbae4b9eca99bff6fa162e6d0e0fd013d937 server:iot-06z00c6kgq6t1w4.mqtt.iothub.aliyuncs.com timestamp:1692157801132
+
+ override func checkBleStatus() -> Bool {
+ if BleManager_C.shared.isConnected {
+ return true
+ }
+ connectLpState?(false, ["msg":"蓝牙未连接"])
+ return false
+ }
+
+}
+
+extension LPConnectService_C {
+ //接收来着蓝牙数据
+ func didReceiveBleData(dataStr:String) {
+ //返回数据的base64字符串
+ if let data = NSData(base64Encoded: dataStr) {
+ countdownTimer?.invalidate()
+ countdownTimer = nil
+
+ if canConnect != .connecting {
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: "====>>>> [新流程] LP 不在正确状态内接收 FND 信息,请重新发起。 状态: \(canConnect.description)")
+ return
+ }
+
+ let decodeStr:String = String(data: data as Data, encoding: .utf8)!
+ MLog.debug("====>>>> 飞鸟盾数据解码 \(decodeStr)")
+
+ let productKey = decodeStr.inFix(startIndex: 3, endIndex: 3 + 11)
+ let dn = decodeStr.count - (3 + 11 + 40)
+ let deviceName = decodeStr.inFix(startIndex: 3 + 11, endIndex: 3 + 11 + dn)
+ let passWd = decodeStr.inFix(startIndex: 3 + 11 + dn, endIndex: 3 + 11 + dn + 40)
+ startIotLogin(productKey: productKey, deviceName: deviceName, passWd: passWd)
+ }else {
+ MLog.debug("====>>>> 转base")
+ }
+ }
+}
+
+extension LPConnectService_C {
+
+ //通知蓝牙连接状态
+ func sendMqttStateToDevice(state: MqttState_C) {
+ MLog.debug("====>>>> 开始通知蓝牙,iot连接状态")
+ var data = Data()
+ data.append(contentsOf: [0x03, 0x01, 0x00])
+ data.append(state.rawValue)
+
+ let encodeStr = data.base64EncodedString()
+
+ let json = [
+ "sid":BleSIdentifier_C.fnd.rawValue,
+ "data":encodeStr,
+ "ver":1
+
+ ] as [String : Any]
+ MLog.debug("====>>>> 通知蓝牙,iot连接状态 \(json)")
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) else {
+ MLog.debug("====>>>> 通知蓝牙,iot连接状态,数据转码失败 ")
+ return }
+ sendBleData(data: jsonData)
+ }
+
+ func uploadSDKVersion() -> Void {
+ guard let appVersion = appVersion else { return }
+
+ let topic = "event/property/post"
+ let json = [
+ "AppSdkVersion":"iOS_SDK_Ver:\(sdkVer_C) Dev_SDK_Ver:\(BleService_C.shared.bleVer) APP_Ver:\(appVersion) "
+ ]
+
+ let sendJson = [
+ "id":IDGenerater_C.shared.sequenceId,
+ "method":"thing.service.property.post",
+ "version":"1.0",
+ "params":json
+ ] as [String : Any]
+ MLog.debug("====>>>> 上报SDK版本号 \(sendJson)")
+
+ ThingCommunicateService_C.shared.setupSdkService(sdkService: IotMqttManager_C.shared)
+
+ do {
+ try IotMqttManager_C.shared.publishMessage(topic: ThingCommunicateService_C.addTopicPrefix(topic: topic), json: sendJson)
+ } catch {
+ MLog.debug("====>>>> 上报SDK版本号失败")
+ }
+ }
+}
+
+extension LPConnectService_C : AliCloudIotDelegate_C, AliCloudIotSdkInitDelegate_C {
+ func onIotLog(log: String) {
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: log.trimmedString)
+ }
+
+ public func onInit(successed: Bool, error: Error?) {
+ if successed {
+ MLog.debug("====>>>> 初始化成功")
+ uploadSDKVersion()
+ }else {
+ canConnect = .ready
+ MLog.debug("====>>>> 初始化失败")
+ connectLpState?(false, ["msg":"初始化失败"])
+ sendMqttStateToDevice(state: MqttState_C.authenticationFailure)
+ }
+ }
+
+ public func onDeinit(successed: Bool, error: Error?) {
+ canConnect = .ready
+ disconnRes?(true, ["msg" : "反初始化"])
+ MLog.debug("====>>>> iot 反初始化 \(successed ? "成功" : "失败")")
+ }
+
+ public func onIotDisconnected() {
+ MLog.debug("====>>>> iot连接断开")
+// connectLpState?(true, ["msg":"连接断开 LP request timestamp:\(sendDataTime!)"])
+
+ /// 如果不是 主动断开 或者 已连接 ,需要对齐
+ if canConnect != .disconnecting &&
+ canConnect != .connected
+ {
+ MLog.debug("====>>>> [新流程] LP 对齐: \(canConnect.description)")
+ }
+ canConnect = .disconnected
+ AliConnectMananger_C.shared.iotStateDelegate?.onIotDisconeted()
+ }
+
+ public func onIotConnected() {
+
+ MLog.debug("====>>>> iot连接成功+并上报版本号")
+ connectLpState?(true, ["msg":"已连接 tLP request imestamp:\(sendDataTime!)"])
+ sendMqttStateToDevice(state: MqttState_C.onlineSuccess)
+
+ /// 虽然已连接上,但 如果不是 主动连接中 和 已断开
+ if canConnect != .connecting &&
+ canConnect != .disconnected {
+ MLog.debug("====>>>> [新流程] LP 对齐: \(canConnect.description)")
+ }
+
+ canConnect = .connected
+ AliConnectMananger_C.shared.iotStateDelegate?.onIotConnected()
+
+ }
+
+ public func onIotConnecting() {
+ MLog.debug("====>>>> iot连接中...")
+ AliConnectMananger_C.shared.iotStateDelegate?.onIotConnecting()
+ }
+
+ public func onMessageArrived(topic: String, message: Any?) {
+ //"接收到消息"
+ MLog.debug("接收到2 topic: \(topic) 的消息")
+ }
+
+ public func onIotMessageArrived(data: String, message: String, code: Int32) {
+ //"接收到IOT消息"
+ MLog.debug("接收到IOT消息 topic: \(message) 的消息")
+ }
+
+ public func onBusCodeMessageArrived(data: String, message: String, code: Int32) {
+// "接收到乘车码消息"
+ MLog.debug("接收到乘车码消息 topic: \(message) 的消息")
+ }
+
+}
--- /dev/null
+//
+// MqttFileManager.swift
+// AliIotConnectKit
+//
+// Created by incar on 2023/12/4.
+//
+
+import Foundation
+import AliIotConnectKit_Private.OC
+
+
+class MqttFileManager: BleBaseSendService_C {
+ static let init_topic = "file/upload/mqtt/init"
+ static let init_reply_topic = "file/upload/mqtt/init_reply"
+
+ static let send_topic = "file/upload/mqtt/send"
+ static let send_reply_topic = "file/upload/mqtt/send_reply"
+
+ static let cancel_topic = "file/upload/mqtt/cancel"
+ static let cancel_reply_topic = "file/upload/mqtt/cancel_reply"
+
+ static let shared = MqttFileManager()
+ static var msgId = 0/// 消息id,唯一
+
+ var uploadId = ""/// 任务id,初始化上传文件时云端返回
+ var didUploadSize = 0///已上传的大小
+ var fileName = ""///文件名
+ var fileData = Data()//上传的数据
+
+
+ func subscribeTopic(){
+ showLog("====>>>> mqtt订阅文件上传topic")
+ do {
+ try IotMqttManager_C.shared.subscribeTopic(topic: ThingCommunicateService_C.addTopicPrefix(topic: MqttFileManager.init_reply_topic), qos: 0)
+ } catch {
+ showLog("====>>>> 订阅init_reply文件失败")
+ }
+
+ do {
+ try IotMqttManager_C.shared.subscribeTopic(topic: ThingCommunicateService_C.addTopicPrefix(topic: MqttFileManager.send_reply_topic), qos: 0)
+ } catch {
+ showLog("====>>>> 订阅send_reply文件失败")
+ }
+ do {
+ try IotMqttManager_C.shared.subscribeTopic(topic: ThingCommunicateService_C.addTopicPrefix(topic: MqttFileManager.cancel_reply_topic), qos: 0)
+ } catch {
+ showLog("====>>>> 订阅cancel_reply文件失败")
+ }
+ }
+
+ func sendFile(_ fileName:String,_ content:Data){
+ self.uploadId = ""
+ self.didUploadSize = 0
+
+ self.fileData = content
+ self.fileName = fileName
+
+// let content_bytes = [UInt8](repeating: 0xB6, count: 10)
+// self.fileData = Data(bytes: content_bytes, count: content_bytes.count)
+
+ self.sendFileInfo()
+// self.uploadData()
+ }
+
+ private func sendFileInfo(){
+// let aa = NSMutableData()
+// aa.oss_crc64
+ MqttFileManager.msgId += 1
+ let topic = ThingCommunicateService_C.addTopicPrefix(topic:MqttFileManager.init_topic );
+ let replyJson = [
+ "id" : "\(MqttFileManager.msgId)",
+ "params" : [
+ "fileName":self.fileName,//上传文件的名称
+ "fileSize":fileData.count,//文件大小,单位字节
+ "conflictStrategy":"overwrite",//上传同名文件的处理策略
+ // "ficMode":"crc64",//文件的完整性校验模式,目前可取值crc64。非必传参数。
+ // "ficValue":"0205d7***",//非必传参数。若传入,与ficMode同时传入。
+ "initUid":"\(arc4random() + 100000000)",//任务唯一ID
+ // "extraParams":[//非必须参数
+ // "ossOwnerType":"device-user",//自己的OSS存储空间中
+ // "serviceId":"device1.bucket",//文件上传相关的业务ID。该ID需要在物联网平台控制台预先定义
+ // "fileTag":[//文件保存到OSS存储空间携带的标签,最多包含5个
+ // "tagKey1":"tagValu1",
+ // "tagKey2":"tagValue2"
+ // ]
+ // ]
+ ]
+ ] as [String : Any]
+ showLog("Mqtt文件上传初始化\(replyJson)")
+ do {
+ try IotMqttManager_C.shared.publishMessage(topic: topic, json: replyJson)
+ } catch {
+ outputInitFailure()
+ }
+ }
+ private func uploadData(){
+ var content_data = Data()
+// let maxBlockSize = 131072
+ let maxBlockSize = 3000//最大上传块大小
+ var blockSize = 0//上传块大小
+
+ if didUploadSize + maxBlockSize > fileData.count{
+ blockSize = fileData.count - didUploadSize
+ }
+ else{
+ blockSize = maxBlockSize
+ }
+ content_data = fileData.subdata(in: Range(NSMakeRange(didUploadSize, blockSize))!)
+
+
+ MqttFileManager.msgId += 1
+ let topic = ThingCommunicateService_C.addTopicPrefix(topic: MqttFileManager.send_topic);
+ let headJson = [
+ "id":"\(MqttFileManager.msgId)",
+ "params":[
+ "uploadId":uploadId,//设备请求上传文件时返回的文件上传任务标识ID。
+ "offset":didUploadSize,//已上传文件分片的总大小,单位为字节。
+ "bSize":content_data.count,//当前上传文件分片的大小,单位为字节。
+ "isComplete":false//仅当设备请求上传文件中fileSize为-1,即文件大小未知时,该参数有效,表示当前分片是否是文件的最后一个分片。
+ ]
+ ] as [String : Any]
+ showLog("Mqtt文件传输 \(headJson)")
+
+ guard let head_data = try? JSONSerialization.data(withJSONObject: headJson) else {
+ showLog("headJson 数据格式化异常")
+ return
+ }
+
+ let head_len:Int16 = Int16(head_data.count)
+ let head_len_data = Data.int16ToData(num: head_len,isLittle: false)
+ var crc_data = MqttManager_C.crc16Data(content_data)!
+
+
+ var send_data = Data()
+ send_data.append(head_len_data)
+ send_data.append(head_data)
+ send_data.append(content_data)
+ send_data.append(crc_data)
+// print(head_len_data.hexEncodedString())
+// print(head_data.hexEncodedString())
+// print(content_data.hexEncodedString())
+// print(crc_data.hexEncodedString())
+// print(send_data.hexEncodedString())
+
+ do {
+ try IotMqttManager_C.shared.publishMessage(topic: topic, data: send_data)
+ } catch {
+ showLog("mqtt文件上传失败")
+ }
+ }
+}
+extension MqttFileManager{
+ func onMessageArrival(topic: String, payload: Any?) {
+ let dic = payloadToJson(payload)
+ let str = "mqtt收到消息 " + "top = \(topic)" + "payload = \(payload ?? "")"
+// showLog(str)
+ showLog("mqtt收到消息 " + "top = \(topic)" + "\(dic)")
+
+ if topic.hasSuffix(MqttFileManager.init_reply_topic) {
+ if let code = dic["code"] as? Int,code == 200, let data = dic["data"] as? [String:Any],let uid = data["uploadId"] as? String{
+ self.uploadId = uid
+ outputProgress()
+ uploadData()
+ }
+ else{
+ outputInitFailure()
+ }
+ }
+ else if topic.hasSuffix(MqttFileManager.send_reply_topic) {
+ if let code = dic["code"] as? Int,code == 200,let data = dic["data"] as? [String:Any],let offset = data["offset"] as? Int,let bSize = data["bSize"] as? Int{
+ self.didUploadSize = offset + bSize
+ outputProgress()
+ if self.didUploadSize >= fileData.count{
+ outputUploadSuccess()
+ }
+ else{
+ uploadData()
+ }
+ }
+ else{
+ outputUploadFailure()
+ }
+ }
+ else if topic.hasSuffix(MqttFileManager.cancel_reply_topic) {
+
+
+ }
+ }
+}
+extension MqttFileManager{
+ private func payloadToJson(_ payload:Any?)-> [String: Any]{
+ guard let str:String = payload as? String else {
+ showLog("payLoad异常")
+ return [:]
+ }
+ let data = str.data(using: .utf8)
+
+ guard let dic = try? JSON(data: data!).dictionaryObject else {
+ showLog("payLoad异常")
+ return [:]
+ }
+ return dic
+ }
+ private func showLog(_ log:String){
+ AliConnectMananger_C.shared.iotStateDelegate?.onLog(log: log)
+ }
+}
+extension MqttFileManager{
+ private func outputProgress(){
+ let progress = Int(100.0*(Float(self.didUploadSize)/Float(fileData.count)))
+ showLog("上传进度\(self.didUploadSize)/\(fileData.count) = " + String(format: "%d",progress) + "%")
+ let pro = UInt8(progress)
+ var data = Data()
+ data.append(contentsOf: [0x42,0x11,0x00,pro])
+ sendBleData(data: data)
+ }
+ private func outputInitFailure(){
+ showLog("Mqtt文件上行初始化失败")
+ let progress = Int(100.0*(Float(self.didUploadSize)/Float(fileData.count)))
+ let pro = UInt8(progress)
+ var data = Data()
+ data.append(contentsOf: [0x42,0x11,0x01,pro])
+ sendBleData(data: data)
+ }
+ private func outputUploadFailure(){
+ showLog("Mqtt文件上行失败")
+ let progress = Int(100.0*(Float(self.didUploadSize)/Float(fileData.count)))
+ let pro = UInt8(progress)
+ var data = Data()
+ data.append(contentsOf: [0x42,0x11,0x02,pro])
+ sendBleData(data: data)
+ }
+ private func outputUploadSuccess(){
+ showLog("Mqtt文件传输完成")
+ let progress = Int(100.0*(Float(self.didUploadSize)/Float(fileData.count)))
+ let pro = UInt8(progress)
+ var data = Data()
+ data.append(contentsOf: [0x42,0x11,0xff,pro])
+ sendBleData(data: data)
+ }
+}
+
+
--- /dev/null
+//\r
+// RideYardService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+class RideYardService_C: BleBaseSendService_C {\r
+ static let shared = RideYardService_C()\r
+ \r
+ var sdkService: SdkMqttService_C? {\r
+ didSet {\r
+ guard let sdkService = sdkService else {\r
+ return\r
+ }\r
+\r
+ ThingCommunicateService_C.shared.setupSdkService(sdkService: sdkService)\r
+ }\r
+ }\r
+ \r
+ //收到乘车码mqtt数据,转发给蓝牙\r
+ func didReceiveMqttData(model:WearCommonCommand_C) -> Void {\r
+ let encoder = JSONEncoder()\r
+ let data: Data = try! encoder.encode(model)\r
+ guard let modelJson:[String:Any] = try? JSONSerialization.jsonObject(with: data) as? [String : Any] else {\r
+ MLog.debug("====>>>> 乘车码模型转json失败 ")\r
+ return }\r
+ \r
+ let json = ["sid":BleSIdentifier_C.gtd.rawValue,\r
+ "data":bleDataToString(json: modelJson)]\r
+ MLog.debug("====>>>> 响应乘车码数据,发送给蓝牙: \(json) ")\r
+ let bleData = try! JSONSerialization.data(withJSONObject: json)\r
+ sendBleData(data: bleData)\r
+ }\r
+ \r
+ /// 上报乘车码数据\r
+ /// - Parameters:\r
+ /// - eventContent: 时间内容\r
+ /// - trackId: id\r
+ public func uploadData(eventContent: String, trackId: String?) throws {\r
+ //NOTE: trackId 判空处理?\r
+ let event = WearCommonEvent_C(eventIdentifier: EventIdentifier_C.alipayRideyard.rawValue, eventContent: eventContent, trackId: trackId)\r
+ try ThingCommunicateService_C.shared.wearCommonEventUpload(event: event)\r
+ }\r
+ \r
+}\r
+\r
+\r
+extension RideYardService_C {\r
+ //接收来着蓝牙数据\r
+ func didReceiveBleData(dataStr:String) {\r
+ MLog.debug("====>>>> 请求乘车码数据")\r
+ ThingCommunicateService_C.shared.setupSdkService(sdkService: IotMqttManager_C.shared)\r
+\r
+ do {\r
+ try uploadData(eventContent: dataStr, trackId: "")\r
+ } catch {\r
+ MLog.debug("====>>>> 请求乘车码数据执行异常")\r
+ }\r
+ \r
+// ThingCommunicateService.shared.setupSdkService(sdkService: IotMqttManager.shared)\r
+// let topic = "event/WearCommonEvent/post"\r
+// let sendJson = [\r
+// "id":IDGenerater.shared.sequenceId,\r
+// "method":ThingCommunicateService.getMethod(topic: topic),\r
+// "version":"1.0",\r
+// "params":dataStr\r
+// ] as [String : Any]\r
+// MLog.debug("====>>>> 开始发送CCM 数据 \(sendJson)")\r
+//\r
+// do {\r
+// try IotMqttManager.shared.publishMessage(topic: ThingCommunicateService.addTopicPrefix(topic: topic), json: sendJson)\r
+// } catch {\r
+// MLog.debug("====>>>> 向后台发送CCM数据失败")\r
+// }\r
+ }\r
+}\r
--- /dev/null
+//\r
+// CommandIdentifier.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+//iot命令标识\r
+enum CommandIdentifier_C: String {\r
+ //乘车码命令\r
+ case alipayRideyard = "AlipayRideyard"\r
+ //系统命令\r
+ case systemCommand = "SystemCommand"\r
+ //iot\r
+ case iot = "iot"\r
+}\r
--- /dev/null
+//\r
+// EventIdentifier.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+enum EventIdentifier_C: String {\r
+ //乘车码\r
+ case alipayRideyard = "AlipayRideyard"\r
+ case iot = "iot"\r
+}\r
--- /dev/null
+//
+// FGSMsgType.swift
+// AliCloudIotKitAdvance
+//
+// Created by IYCDev on 2022/11/26.
+//
+
+import Foundation
+
+enum FGSMsgType_C: String {
+
+ case startReq = "FGS_MSG_TYPE_START_FGS_REQ"//飞鸽书请求
+ case startResp = "FGS_MSG_TYPE_START_FGS_RESP"//飞鸽书返回
+
+ case authReq = "FGS_MSG_TYPE_START_LP_AUTH_REQ"//三元组信息授权
+ case authResp = "FGS_MSG_TYPE_START_LP_AUTH_RESP"//三元组信息返回
+
+ case downloadReq = "FGS_MSG_TYPE_DS_DOWNLOAD_REQ"//三元组数据写入
+ case downloadResp = "FGS_MSG_TYPE_DS_DOWNLOAD_RESP"////三元组数据写入返回
+
+}
+
--- /dev/null
+//\r
+// ReplyPack.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+public struct RelpyPack_C: Codable {\r
+ var method: String?\r
+ let version: String\r
+ let id: String\r
+ \r
+ /// 状态代码\r
+ let code: Int32\r
+ \r
+ /// 状态描述\r
+ let message: String\r
+ \r
+ /// 数据\r
+ let data: String\r
+}\r
+\r
--- /dev/null
+//\r
+// ThingPack.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/1.\r
+//\r
+\r
+import Foundation\r
+\r
+/**\r
+ 物模型包装\r
+ */\r
+struct ThingPack_C<T: Codable>: Codable {\r
+ \r
+ /// topic方法名\r
+ let method: String?\r
+ \r
+ /// 消息id\r
+ let id: Int64?\r
+ \r
+ /// version\r
+ let version: String?\r
+ \r
+ /// params\r
+ let params: T?\r
+}\r
--- /dev/null
+//\r
+// ThingEventType.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+enum ThingType_C {\r
+ case wearEventUpload(thingTopicPrefix: String)\r
+ case wearCommandReply(thingTopicPrefix: String)\r
+ \r
+ var topic: String {\r
+ switch self {\r
+ case .wearEventUpload(let thingTopicPrefix):\r
+ return thingTopicPrefix + "/event/WearCommonEvent/post"\r
+ case .wearCommandReply(let thingTopicPrefix):\r
+ return thingTopicPrefix + "/service/WearCommonCommand_reply"\r
+ }\r
+ }\r
+ \r
+ var method: String {\r
+ switch self {\r
+ case .wearEventUpload(_):\r
+ return "thing.event.WearCommonEvent.post"\r
+ case .wearCommandReply(_):\r
+ return "thing.service.WearCommonCommand"\r
+ }\r
+ }\r
+}\r
--- /dev/null
+//\r
+// WearCommonCommand.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+\r
+public struct WearCommonCommand_C: Codable {\r
+ \r
+ /// 命令标识\r
+ let cmdIdentifier: String\r
+ \r
+ /// 错误码\r
+ let code: Int32\r
+ \r
+ /// 跟踪ID\r
+ let trackId: String\r
+ \r
+ /// 错误消息内容\r
+ let message: String\r
+ \r
+ /// 业务数据\r
+ let data: String\r
+}\r
--- /dev/null
+//\r
+// WearCommonEvent.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/1.\r
+//\r
+\r
+import Foundation\r
+\r
+/**\r
+ 穿戴通用事件物模型\r
+ */\r
+public struct WearCommonEvent_C: Codable {\r
+ \r
+ /// 事件标识id\r
+ let eventIdentifier: String?\r
+ \r
+ /// 事件内容\r
+ let eventContent: String?\r
+ \r
+ /// 跟踪id\r
+ let trackId: String?\r
+}\r
--- /dev/null
+//\r
+// ThingCommunicateService.swift\r
+// AliCloudIotKit\r
+//\r
+// Created by tonyin on 2022/9/2.\r
+//\r
+\r
+import Foundation\r
+/// import SwiftyBeaver\r
+/// import SwiftyJSON\r
+\r
+/// 物模型通信\r
+public class ThingCommunicateService_C {\r
+ static let shared = ThingCommunicateService_C()\r
+ \r
+ private var sdkService: SdkMqttService_C?\r
+ \r
+ func setupSdkService(sdkService: SdkMqttService_C) {\r
+ MLog.debug("====>>>> 设置mqtt")\r
+ self.sdkService = sdkService\r
+ }\r
+ \r
+ /// 物模型topic前缀\r
+ var thingTopicPrefix: String? {\r
+ guard let config = sdkService?.config else {\r
+ return nil\r
+ }\r
+ //\r
+ return "/sys/\(config.productKey)/\(config.deviceName)/thing"\r
+ }\r
+ \r
+ \r
+ /// 穿戴通用事件上传到云端\r
+ /// - Parameter event: 事件\r
+ public func wearCommonEventUpload(event: WearCommonEvent_C) throws {\r
+ guard let thingTopicPrefix = thingTopicPrefix else {\r
+ throw AliCloudIotError_C.sdkNotInit\r
+ }\r
+ \r
+ let type = ThingType_C.wearEventUpload(thingTopicPrefix: thingTopicPrefix)\r
+ let thingPack = ThingPack_C<WearCommonEvent_C>(method: type.method, id: IDGenerater_C.shared.sequenceId, version: "1.0", params: event)\r
+ \r
+ \r
+ try publishMessage(topic: type.topic, obj: thingPack)\r
+ }\r
+ \r
+ \r
+ /// 穿戴通用服务调用同步响应\r
+ /// - Parameter replyPack: 响应包\r
+ public func wearCommonCommandReply(replyPack: RelpyPack_C) throws {\r
+ guard let thingTopicPrefix = thingTopicPrefix else {\r
+ throw AliCloudIotError_C.sdkNotInit\r
+ }\r
+ let type = ThingType_C.wearCommandReply(thingTopicPrefix: thingTopicPrefix)\r
+ var pack = replyPack\r
+ pack.method = type.method\r
+ \r
+ try publishMessage(topic: type.topic, obj: pack)\r
+ }\r
+ \r
+ func publishMessage<T: Codable>(topic: String, obj: T) throws {\r
+ \r
+ guard let data = try? JSONEncoder().encode(obj), let jsonStr = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else {\r
+ MLog.error("数据格式化异常")\r
+ return\r
+ }\r
+ MLog.debug("====>>>> 发送mqtt数据 \(jsonStr)")\r
+ \r
+ try sdkService?.publishMessage(topic: topic, data: data)\r
+ }\r
+ \r
+ func publishJsonMessage(topic: String, obj: [String: Any]) throws {\r
+ guard let data = try? JSONSerialization.data(withJSONObject: obj), let jsonStr = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else {\r
+ MLog.error("数据格式化异常")\r
+ return\r
+ }\r
+ \r
+ MLog.debug("====>>>> 发送mqtt数据2 \(jsonStr)")\r
+\r
+ try sdkService?.publishMessage(topic: topic, data: data)\r
+ }\r
+ \r
+ public func tryResolve(topic: String, messageData: Any?) -> WearCommonCommand_C? {\r
+ guard let thingTopicPrefix = thingTopicPrefix, topic == thingTopicPrefix + "/service/WearCommonCommand", let messageData = messageData else {\r
+ return nil\r
+ }\r
+ \r
+ //属于字符串格式\r
+ var data: Data? = nil\r
+ \r
+ if let jsonStr = messageData as? String, let strData = jsonStr.data(using: .utf8) {\r
+ data = strData\r
+ } else if let dict = messageData as? NSDictionary, let dictData = try? JSONSerialization.data(withJSONObject: dict, options: []) {\r
+ data = dictData\r
+ }\r
+ \r
+ guard let data = data, let model = try? JSONDecoder().decode(ThingPack_C<WearCommonCommand_C>.self, from: data) else {\r
+ return nil\r
+ }\r
+ return model.params\r
+ }\r
+ \r
+ func tryId2Resolve(messageData: Any?) -> ThingPack_C<String>? {\r
+ \r
+ //属于字符串格式\r
+ var data: Data? = nil\r
+ \r
+ if let jsonStr = messageData as? String, let strData = jsonStr.data(using: .utf8) {\r
+ data = strData\r
+ } else if let dict = messageData as? NSDictionary, let dictData = try? JSONSerialization.data(withJSONObject: dict, options: []) {\r
+ data = dictData\r
+ }\r
+ \r
+ guard let data = data, let model = try? JSONDecoder().decode(ThingPack_C<String>.self, from: data) else {\r
+ return nil\r
+ }\r
+ return model\r
+ }\r
+}\r
+\r
+extension ThingCommunicateService_C {\r
+ class func getMethod(topic:String) -> String {\r
+ let str = topic.replacingOccurrences(of: "/", with: ".")\r
+ return "thing." + str\r
+ }\r
+ \r
+ class func addTopicPrefix(topic:String) -> String {\r
+ MLog.debug("====>>>> 开始设置topic \(topic)")\r
+ if (ThingCommunicateService_C.shared.thingTopicPrefix != nil) {\r
+ let topicPrefix = ThingCommunicateService_C.shared.thingTopicPrefix! + "/" + topic\r
+ MLog.debug("====>>>> 设置好后的topic \(topicPrefix)")\r
+ return topicPrefix\r
+ }\r
+ MLog.debug("====>>>> 开始设置topic 没有配置config")\r
+ return topic\r
+ }\r
+}\r
--- /dev/null
+// AFCompatibilityMacros.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef AFCompatibilityMacros_h
+#define AFCompatibilityMacros_h
+
+#ifdef API_AVAILABLE
+ #define AF_API_AVAILABLE(...) API_AVAILABLE(__VA_ARGS__)
+#else
+ #define AF_API_AVAILABLE(...)
+#endif // API_AVAILABLE
+
+#ifdef API_UNAVAILABLE
+ #define AF_API_UNAVAILABLE(...) API_UNAVAILABLE(__VA_ARGS__)
+#else
+ #define AF_API_UNAVAILABLE(...)
+#endif // API_UNAVAILABLE
+
+#if __has_warning("-Wunguarded-availability-new")
+ #define AF_CAN_USE_AT_AVAILABLE 1
+#else
+ #define AF_CAN_USE_AT_AVAILABLE 0
+#endif
+
+#if ((__IPHONE_OS_VERSION_MAX_ALLOWED && __IPHONE_OS_VERSION_MAX_ALLOWED < 100000) || (__MAC_OS_VERSION_MAX_ALLOWED && __MAC_OS_VERSION_MAX_ALLOWED < 101200) ||(__WATCH_OS_MAX_VERSION_ALLOWED && __WATCH_OS_MAX_VERSION_ALLOWED < 30000) ||(__TV_OS_MAX_VERSION_ALLOWED && __TV_OS_MAX_VERSION_ALLOWED < 100000))
+ #define AF_CAN_INCLUDE_SESSION_TASK_METRICS 0
+#else
+ #define AF_CAN_INCLUDE_SESSION_TASK_METRICS 1
+#endif
+
+#endif /* AFCompatibilityMacros_h */
--- /dev/null
+// AFHTTPSessionManager.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+#if !TARGET_OS_WATCH
+#import <SystemConfiguration/SystemConfiguration.h>
+#endif
+#import <TargetConditionals.h>
+
+#import "AFURLSessionManager.h"
+
+/**
+ `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths.
+
+ ## Subclassing Notes
+
+ Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
+
+ ## Methods to Override
+
+ To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:`.
+
+ ## Serialization
+
+ Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to `<AFURLRequestSerialization>`.
+
+ Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `<AFURLResponseSerialization>`
+
+ ## URL Construction Using Relative Paths
+
+ For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`.
+
+ Below are a few examples of how `baseURL` and relative paths interact:
+
+ NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
+ [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
+ [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
+ [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
+ [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
+ [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
+ [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
+
+ Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash.
+
+ @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance.
+ */
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
+
+/**
+ The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
+ */
+@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
+
+/**
+ Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
+
+ @warning `requestSerializer` must not be `nil`.
+ */
+@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
+
+/**
+ Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
+
+ @warning `responseSerializer` must not be `nil`.
+ */
+@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
+
+///-------------------------------
+/// @name Managing Security Policy
+///-------------------------------
+
+/**
+ The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. A security policy configured with `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate` can only be applied on a session manager initialized with a secure base URL (i.e. https). Applying a security policy with pinning enabled on an insecure session manager throws an `Invalid Security Policy` exception.
+ */
+@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
+
+///---------------------
+/// @name Initialization
+///---------------------
+
+/**
+ Creates and returns an `AFHTTPSessionManager` object.
+ */
++ (instancetype)manager;
+
+/**
+ Initializes an `AFHTTPSessionManager` object with the specified base URL.
+
+ @param url The base URL for the HTTP client.
+
+ @return The newly-initialized HTTP client
+ */
+- (instancetype)initWithBaseURL:(nullable NSURL *)url;
+
+/**
+ Initializes an `AFHTTPSessionManager` object with the specified base URL.
+
+ This is the designated initializer.
+
+ @param url The base URL for the HTTP client.
+ @param configuration The configuration used to create the managed session.
+
+ @return The newly-initialized HTTP client
+ */
+- (instancetype)initWithBaseURL:(nullable NSURL *)url
+ sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
+
+///---------------------------
+/// @name Making HTTP Requests
+///---------------------------
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `GET` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `POST` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
+ @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+/**
+ Creates an `NSURLSessionDataTask` with a custom `HTTPMethod` request.
+
+ @param method The HTTPMethod string used to create the request.
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
+ @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
+ @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
+ */
+- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+// AFHTTPSessionManager.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFHTTPSessionManager.h"
+
+#import "AFURLRequestSerialization.h"
+#import "AFURLResponseSerialization.h"
+
+#import <Availability.h>
+#import <TargetConditionals.h>
+#import <Security/Security.h>
+
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+
+#if TARGET_OS_IOS || TARGET_OS_TV
+#import <UIKit/UIKit.h>
+#elif TARGET_OS_WATCH
+#import <WatchKit/WatchKit.h>
+#endif
+
+@interface AFHTTPSessionManager ()
+@property (readwrite, nonatomic, strong) NSURL *baseURL;
+@end
+
+@implementation AFHTTPSessionManager
+@dynamic responseSerializer;
+
++ (instancetype)manager {
+ return [[[self class] alloc] initWithBaseURL:nil];
+}
+
+- (instancetype)init {
+ return [self initWithBaseURL:nil];
+}
+
+- (instancetype)initWithBaseURL:(NSURL *)url {
+ return [self initWithBaseURL:url sessionConfiguration:nil];
+}
+
+- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
+ return [self initWithBaseURL:nil sessionConfiguration:configuration];
+}
+
+- (instancetype)initWithBaseURL:(NSURL *)url
+ sessionConfiguration:(NSURLSessionConfiguration *)configuration
+{
+ self = [super initWithSessionConfiguration:configuration];
+ if (!self) {
+ return nil;
+ }
+
+ // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
+ if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
+ url = [url URLByAppendingPathComponent:@""];
+ }
+
+ self.baseURL = url;
+
+ self.requestSerializer = [AFHTTPRequestSerializer serializer];
+ self.responseSerializer = [AFJSONResponseSerializer serializer];
+
+ return self;
+}
+
+#pragma mark -
+
+- (void)setRequestSerializer:(AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
+ NSParameterAssert(requestSerializer);
+
+ _requestSerializer = requestSerializer;
+}
+
+- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
+ NSParameterAssert(responseSerializer);
+
+ [super setResponseSerializer:responseSerializer];
+}
+
+@dynamic securityPolicy;
+
+- (void)setSecurityPolicy:(AFSecurityPolicy *)securityPolicy {
+ if (securityPolicy.SSLPinningMode != AFSSLPinningModeNone && ![self.baseURL.scheme isEqualToString:@"https"]) {
+ NSString *pinningMode = @"Unknown Pinning Mode";
+ switch (securityPolicy.SSLPinningMode) {
+ case AFSSLPinningModeNone: pinningMode = @"AFSSLPinningModeNone"; break;
+ case AFSSLPinningModeCertificate: pinningMode = @"AFSSLPinningModeCertificate"; break;
+ case AFSSLPinningModePublicKey: pinningMode = @"AFSSLPinningModePublicKey"; break;
+ }
+ NSString *reason = [NSString stringWithFormat:@"A security policy configured with `%@` can only be applied on a manager with a secure base URL (i.e. https)", pinningMode];
+ @throw [NSException exceptionWithName:@"Invalid Security Policy" reason:reason userInfo:nil];
+ }
+
+ [super setSecurityPolicy:securityPolicy];
+}
+
+#pragma mark -
+
+- (NSURLSessionDataTask *)GET:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
+ URLString:URLString
+ parameters:parameters
+ headers:headers
+ uploadProgress:nil
+ downloadProgress:downloadProgress
+ success:success
+ failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary<NSString *,NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
+ if (success) {
+ success(task);
+ }
+ } failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
+{
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+- (NSURLSessionDataTask *)POST:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary<NSString *,NSString *> *)headers
+ constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
+ progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+{
+ NSError *serializationError = nil;
+ NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
+ for (NSString *headerField in headers.keyEnumerator) {
+ [request setValue:headers[headerField] forHTTPHeaderField:headerField];
+ }
+ if (serializationError) {
+ if (failure) {
+ dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
+ failure(nil, serializationError);
+ });
+ }
+
+ return nil;
+ }
+
+ __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
+ if (error) {
+ if (failure) {
+ failure(task, error);
+ }
+ } else {
+ if (success) {
+ success(task, responseObject);
+ }
+ }
+ }];
+
+ [task resume];
+
+ return task;
+}
+
+- (NSURLSessionDataTask *)PUT:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary<NSString *,NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
+{
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary<NSString *,NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
+{
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary<NSString *,NSString *> *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
+{
+ NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
+
+ [dataTask resume];
+
+ return dataTask;
+}
+
+
+- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary <NSString *, NSString *> *)headers
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
+{
+ NSError *serializationError = nil;
+ NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
+ for (NSString *headerField in headers.keyEnumerator) {
+ [request setValue:headers[headerField] forHTTPHeaderField:headerField];
+ }
+ if (serializationError) {
+ if (failure) {
+ dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
+ failure(nil, serializationError);
+ });
+ }
+
+ return nil;
+ }
+
+ __block NSURLSessionDataTask *dataTask = nil;
+ dataTask = [self dataTaskWithRequest:request
+ uploadProgress:uploadProgress
+ downloadProgress:downloadProgress
+ completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
+ if (error) {
+ if (failure) {
+ failure(dataTask, error);
+ }
+ } else {
+ if (success) {
+ success(dataTask, responseObject);
+ }
+ }
+ }];
+
+ return dataTask;
+}
+
+#pragma mark - NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue];
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))];
+ NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
+ if (!configuration) {
+ NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"];
+ if (configurationIdentifier) {
+ configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier];
+ }
+ }
+
+ self = [self initWithBaseURL:baseURL sessionConfiguration:configuration];
+ if (!self) {
+ return nil;
+ }
+
+ self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))];
+ self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))];
+ AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))];
+ if (decodedPolicy) {
+ self.securityPolicy = decodedPolicy;
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))];
+ if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) {
+ [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
+ } else {
+ [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"];
+ }
+ [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))];
+ [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
+ [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration];
+
+ HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone];
+ HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone];
+ HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone];
+ return HTTPClient;
+}
+
+@end
--- /dev/null
+// AFNetworkReachabilityManager.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+
+#if !TARGET_OS_WATCH
+#import <SystemConfiguration/SystemConfiguration.h>
+
+typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
+ AFNetworkReachabilityStatusUnknown = -1,
+ AFNetworkReachabilityStatusNotReachable = 0,
+ AFNetworkReachabilityStatusReachableViaWWAN = 1,
+ AFNetworkReachabilityStatusReachableViaWiFi = 2,
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
+
+ Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability.
+
+ See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ )
+
+ @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
+ */
+@interface AFNetworkReachabilityManager : NSObject
+
+/**
+ The current network reachability status.
+ */
+@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
+
+/**
+ Whether or not the network is currently reachable.
+ */
+@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
+
+/**
+ Whether or not the network is currently reachable via WWAN.
+ */
+@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
+
+/**
+ Whether or not the network is currently reachable via WiFi.
+ */
+@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
+
+///---------------------
+/// @name Initialization
+///---------------------
+
+/**
+ Returns the shared network reachability manager.
+ */
++ (instancetype)sharedManager;
+
+/**
+ Creates and returns a network reachability manager with the default socket address.
+
+ @return An initialized network reachability manager, actively monitoring the default socket address.
+ */
++ (instancetype)manager;
+
+/**
+ Creates and returns a network reachability manager for the specified domain.
+
+ @param domain The domain used to evaluate network reachability.
+
+ @return An initialized network reachability manager, actively monitoring the specified domain.
+ */
++ (instancetype)managerForDomain:(NSString *)domain;
+
+/**
+ Creates and returns a network reachability manager for the socket address.
+
+ @param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
+
+ @return An initialized network reachability manager, actively monitoring the specified socket address.
+ */
++ (instancetype)managerForAddress:(const void *)address;
+
+/**
+ Initializes an instance of a network reachability manager from the specified reachability object.
+
+ @param reachability The reachability object to monitor.
+
+ @return An initialized network reachability manager, actively monitoring the specified reachability.
+ */
+- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Unavailable initializer
+ */
++ (instancetype)new NS_UNAVAILABLE;
+
+/**
+ * Unavailable initializer
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+///--------------------------------------------------
+/// @name Starting & Stopping Reachability Monitoring
+///--------------------------------------------------
+
+/**
+ Starts monitoring for changes in network reachability status.
+ */
+- (void)startMonitoring;
+
+/**
+ Stops monitoring for changes in network reachability status.
+ */
+- (void)stopMonitoring;
+
+///-------------------------------------------------
+/// @name Getting Localized Reachability Description
+///-------------------------------------------------
+
+/**
+ Returns a localized string representation of the current network reachability status.
+ */
+- (NSString *)localizedNetworkReachabilityStatusString;
+
+///---------------------------------------------------
+/// @name Setting Network Reachability Change Callback
+///---------------------------------------------------
+
+/**
+ Sets a callback to be executed when the network availability of the `baseURL` host changes.
+
+ @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
+ */
+- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
+
+@end
+
+///----------------
+/// @name Constants
+///----------------
+
+/**
+ ## Network Reachability
+
+ The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses.
+
+ enum {
+ AFNetworkReachabilityStatusUnknown,
+ AFNetworkReachabilityStatusNotReachable,
+ AFNetworkReachabilityStatusReachableViaWWAN,
+ AFNetworkReachabilityStatusReachableViaWiFi,
+ }
+
+ `AFNetworkReachabilityStatusUnknown`
+ The `baseURL` host reachability is not known.
+
+ `AFNetworkReachabilityStatusNotReachable`
+ The `baseURL` host cannot be reached.
+
+ `AFNetworkReachabilityStatusReachableViaWWAN`
+ The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS.
+
+ `AFNetworkReachabilityStatusReachableViaWiFi`
+ The `baseURL` host can be reached via a Wi-Fi connection.
+
+ ### Keys for Notification UserInfo Dictionary
+
+ Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification.
+
+ `AFNetworkingReachabilityNotificationStatusItem`
+ A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
+ The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
+ */
+
+///--------------------
+/// @name Notifications
+///--------------------
+
+/**
+ Posted when network reachability changes.
+ This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
+
+ @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
+FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
+
+///--------------------
+/// @name Functions
+///--------------------
+
+/**
+ Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
+ */
+FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
+
+NS_ASSUME_NONNULL_END
+#endif
--- /dev/null
+// AFNetworkReachabilityManager.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFNetworkReachabilityManager.h"
+#if !TARGET_OS_WATCH
+
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+
+NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
+NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
+
+typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
+typedef AFNetworkReachabilityManager * (^AFNetworkReachabilityStatusCallback)(AFNetworkReachabilityStatus status);
+
+NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
+ switch (status) {
+ case AFNetworkReachabilityStatusNotReachable:
+ return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
+ case AFNetworkReachabilityStatusReachableViaWWAN:
+ return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
+ case AFNetworkReachabilityStatusReachableViaWiFi:
+ return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
+ case AFNetworkReachabilityStatusUnknown:
+ default:
+ return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
+ }
+}
+
+static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
+ BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
+ BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
+ BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
+ BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
+ BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
+
+ AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
+ if (isNetworkReachable == NO) {
+ status = AFNetworkReachabilityStatusNotReachable;
+ }
+#if TARGET_OS_IPHONE
+ else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
+ status = AFNetworkReachabilityStatusReachableViaWWAN;
+ }
+#endif
+ else {
+ status = AFNetworkReachabilityStatusReachableViaWiFi;
+ }
+
+ return status;
+}
+
+/**
+ * Queue a status change notification for the main thread.
+ *
+ * This is done to ensure that the notifications are received in the same order
+ * as they are sent. If notifications are sent directly, it is possible that
+ * a queued notification (for an earlier status condition) is processed after
+ * the later update, resulting in the listener being left in the wrong state.
+ */
+static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusCallback block) {
+ AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ AFNetworkReachabilityManager *manager = nil;
+ if (block) {
+ manager = block(status);
+ }
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
+ [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:manager userInfo:userInfo];
+ });
+}
+
+static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
+ AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusCallback)info);
+}
+
+
+static const void * AFNetworkReachabilityRetainCallback(const void *info) {
+ return Block_copy(info);
+}
+
+static void AFNetworkReachabilityReleaseCallback(const void *info) {
+ if (info) {
+ Block_release(info);
+ }
+}
+
+@interface AFNetworkReachabilityManager ()
+@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
+@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
+@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
+@end
+
+@implementation AFNetworkReachabilityManager
+
++ (instancetype)sharedManager {
+ static AFNetworkReachabilityManager *_sharedManager = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _sharedManager = [self manager];
+ });
+
+ return _sharedManager;
+}
+
++ (instancetype)managerForDomain:(NSString *)domain {
+ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
+
+ AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
+
+ CFRelease(reachability);
+
+ return manager;
+}
+
++ (instancetype)managerForAddress:(const void *)address {
+ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
+ AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
+
+ CFRelease(reachability);
+
+ return manager;
+}
+
++ (instancetype)manager
+{
+#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
+ struct sockaddr_in6 address;
+ bzero(&address, sizeof(address));
+ address.sin6_len = sizeof(address);
+ address.sin6_family = AF_INET6;
+#else
+ struct sockaddr_in address;
+ bzero(&address, sizeof(address));
+ address.sin_len = sizeof(address);
+ address.sin_family = AF_INET;
+#endif
+ return [self managerForAddress:&address];
+}
+
+- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _networkReachability = CFRetain(reachability);
+ self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
+
+ return self;
+}
+
+- (instancetype)init
+{
+ @throw [NSException exceptionWithName:NSGenericException
+ reason:@"`-init` unavailable. Use `-initWithReachability:` instead"
+ userInfo:nil];
+ return nil;
+}
+
+- (void)dealloc {
+ [self stopMonitoring];
+
+ if (_networkReachability != NULL) {
+ CFRelease(_networkReachability);
+ }
+}
+
+#pragma mark -
+
+- (BOOL)isReachable {
+ return [self isReachableViaWWAN] || [self isReachableViaWiFi];
+}
+
+- (BOOL)isReachableViaWWAN {
+ return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
+}
+
+- (BOOL)isReachableViaWiFi {
+ return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
+}
+
+#pragma mark -
+
+- (void)startMonitoring {
+ [self stopMonitoring];
+
+ if (!self.networkReachability) {
+ return;
+ }
+
+ __weak __typeof(self)weakSelf = self;
+ AFNetworkReachabilityStatusCallback callback = ^(AFNetworkReachabilityStatus status) {
+ __strong __typeof(weakSelf)strongSelf = weakSelf;
+
+ strongSelf.networkReachabilityStatus = status;
+ if (strongSelf.networkReachabilityStatusBlock) {
+ strongSelf.networkReachabilityStatusBlock(status);
+ }
+
+ return strongSelf;
+ };
+
+ SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
+ SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
+ SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
+ SCNetworkReachabilityFlags flags;
+ if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
+ AFPostReachabilityStatusChange(flags, callback);
+ }
+ });
+}
+
+- (void)stopMonitoring {
+ if (!self.networkReachability) {
+ return;
+ }
+
+ SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+}
+
+#pragma mark -
+
+- (NSString *)localizedNetworkReachabilityStatusString {
+ return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
+}
+
+#pragma mark -
+
+- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
+ self.networkReachabilityStatusBlock = block;
+}
+
+#pragma mark - NSKeyValueObserving
+
++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
+ if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
+ return [NSSet setWithObject:@"networkReachabilityStatus"];
+ }
+
+ return [super keyPathsForValuesAffectingValueForKey:key];
+}
+
+@end
+#endif
--- /dev/null
+// AFNetworking.h
+//
+// Copyright (c) 2013 AFNetworking (http://afnetworking.com/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+#import <Availability.h>
+#import <TargetConditionals.h>
+
+#ifndef _AFNETWORKING_
+ #define _AFNETWORKING_
+
+ #import "AFURLRequestSerialization.h"
+ #import "AFURLResponseSerialization.h"
+ #import "AFSecurityPolicy.h"
+
+#if !TARGET_OS_WATCH
+ #import "AFNetworkReachabilityManager.h"
+#endif
+
+ #import "AFURLSessionManager.h"
+ #import "AFHTTPSessionManager.h"
+
+#endif /* _AFNETWORKING_ */
--- /dev/null
+// AFSecurityPolicy.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
+ AFSSLPinningModeNone,
+ AFSSLPinningModePublicKey,
+ AFSSLPinningModeCertificate,
+};
+
+/**
+ `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections.
+
+ Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled.
+ */
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
+
+/**
+ The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`.
+ */
+@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
+
+/**
+ The certificates used to evaluate server trust according to the SSL pinning mode.
+
+ Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
+
+ @see policyWithPinningMode:withPinnedCertificates:
+ */
+@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
+
+/**
+ Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
+ */
+@property (nonatomic, assign) BOOL allowInvalidCertificates;
+
+/**
+ Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
+ */
+@property (nonatomic, assign) BOOL validatesDomainName;
+
+///-----------------------------------------
+/// @name Getting Certificates from the Bundle
+///-----------------------------------------
+
+/**
+ Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`.
+
+ @return The certificates included in the given bundle.
+ */
++ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;
+
+///-----------------------------------------
+/// @name Getting Specific Security Policies
+///-----------------------------------------
+
+/**
+ Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys.
+
+ @return The default security policy.
+ */
++ (instancetype)defaultPolicy;
+
+///---------------------
+/// @name Initialization
+///---------------------
+
+/**
+ Creates and returns a security policy with the specified pinning mode.
+
+ Certificates with the `.cer` extension found in the main bundle will be pinned. If you want more control over which certificates are pinned, please use `policyWithPinningMode:withPinnedCertificates:` instead.
+
+ @param pinningMode The SSL pinning mode.
+
+ @return A new security policy.
+
+ @see -policyWithPinningMode:withPinnedCertificates:
+ */
++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
+
+/**
+ Creates and returns a security policy with the specified pinning mode.
+
+ @param pinningMode The SSL pinning mode.
+ @param pinnedCertificates The certificates to pin against.
+
+ @return A new security policy.
+
+ @see +certificatesInBundle:
+ @see -pinnedCertificates
+*/
++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
+
+///------------------------------
+/// @name Evaluating Server Trust
+///------------------------------
+
+/**
+ Whether or not the specified server trust should be accepted, based on the security policy.
+
+ This method should be used when responding to an authentication challenge from a server.
+
+ @param serverTrust The X.509 certificate trust of the server.
+ @param domain The domain of serverTrust. If `nil`, the domain will not be validated.
+
+ @return Whether or not to trust the server.
+ */
+- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
+ forDomain:(nullable NSString *)domain;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+///----------------
+/// @name Constants
+///----------------
+
+/**
+ ## SSL Pinning Modes
+
+ The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes.
+
+ enum {
+ AFSSLPinningModeNone,
+ AFSSLPinningModePublicKey,
+ AFSSLPinningModeCertificate,
+ }
+
+ `AFSSLPinningModeNone`
+ Do not used pinned certificates to validate servers.
+
+ `AFSSLPinningModePublicKey`
+ Validate host certificates against public keys of pinned certificates.
+
+ `AFSSLPinningModeCertificate`
+ Validate host certificates against pinned certificates.
+*/
--- /dev/null
+// AFSecurityPolicy.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFSecurityPolicy.h"
+
+#import <AssertMacros.h>
+
+#if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV
+static NSData * AFSecKeyGetData(SecKeyRef key) {
+ CFDataRef data = NULL;
+
+ __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);
+
+ return (__bridge_transfer NSData *)data;
+
+_out:
+ if (data) {
+ CFRelease(data);
+ }
+
+ return nil;
+}
+#endif
+
+static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
+#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
+ return [(__bridge id)key1 isEqual:(__bridge id)key2];
+#else
+ return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
+#endif
+}
+
+static id AFPublicKeyForCertificate(NSData *certificate) {
+ id allowedPublicKey = nil;
+ SecCertificateRef allowedCertificate;
+ SecPolicyRef policy = nil;
+ SecTrustRef allowedTrust = nil;
+ SecTrustResultType result;
+
+ allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
+ __Require_Quiet(allowedCertificate != NULL, _out);
+
+ policy = SecPolicyCreateBasicX509();
+ __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
+#pragma clang diagnostic pop
+
+ allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
+
+_out:
+ if (allowedTrust) {
+ CFRelease(allowedTrust);
+ }
+
+ if (policy) {
+ CFRelease(policy);
+ }
+
+ if (allowedCertificate) {
+ CFRelease(allowedCertificate);
+ }
+
+ return allowedPublicKey;
+}
+
+static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
+ BOOL isValid = NO;
+ SecTrustResultType result;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
+#pragma clang diagnostic pop
+
+ isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
+
+_out:
+ return isValid;
+}
+
+static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
+ CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
+ NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
+
+ for (CFIndex i = 0; i < certificateCount; i++) {
+ SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
+ [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
+ }
+
+ return [NSArray arrayWithArray:trustChain];
+}
+
+static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
+ SecPolicyRef policy = SecPolicyCreateBasicX509();
+ CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
+ NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
+ for (CFIndex i = 0; i < certificateCount; i++) {
+ SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
+
+ SecCertificateRef someCertificates[] = {certificate};
+ CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
+
+ SecTrustRef trust;
+ __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
+ SecTrustResultType result;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
+#pragma clang diagnostic pop
+ [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
+
+ _out:
+ if (trust) {
+ CFRelease(trust);
+ }
+
+ if (certificates) {
+ CFRelease(certificates);
+ }
+
+ continue;
+ }
+ CFRelease(policy);
+
+ return [NSArray arrayWithArray:trustChain];
+}
+
+#pragma mark -
+
+@interface AFSecurityPolicy()
+@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
+@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
+@end
+
+@implementation AFSecurityPolicy
+
++ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
+ NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
+
+ NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
+ for (NSString *path in paths) {
+ NSData *certificateData = [NSData dataWithContentsOfFile:path];
+ [certificates addObject:certificateData];
+ }
+
+ return [NSSet setWithSet:certificates];
+}
+
++ (instancetype)defaultPolicy {
+ AFSecurityPolicy *securityPolicy = [[self alloc] init];
+ securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
+
+ return securityPolicy;
+}
+
++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
+ NSSet <NSData *> *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]];
+ return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates];
+}
+
++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
+ AFSecurityPolicy *securityPolicy = [[self alloc] init];
+ securityPolicy.SSLPinningMode = pinningMode;
+
+ [securityPolicy setPinnedCertificates:pinnedCertificates];
+
+ return securityPolicy;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.validatesDomainName = YES;
+
+ return self;
+}
+
+- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
+ _pinnedCertificates = pinnedCertificates;
+
+ if (self.pinnedCertificates) {
+ NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
+ for (NSData *certificate in self.pinnedCertificates) {
+ id publicKey = AFPublicKeyForCertificate(certificate);
+ if (!publicKey) {
+ continue;
+ }
+ [mutablePinnedPublicKeys addObject:publicKey];
+ }
+ self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
+ } else {
+ self.pinnedPublicKeys = nil;
+ }
+}
+
+#pragma mark -
+
+- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
+ forDomain:(NSString *)domain
+{
+ if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
+ // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
+ // According to the docs, you should only trust your provided certs for evaluation.
+ // Pinned certificates are added to the trust. Without pinned certificates,
+ // there is nothing to evaluate against.
+ //
+ // From Apple Docs:
+ // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
+ // Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
+ NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
+ return NO;
+ }
+
+ NSMutableArray *policies = [NSMutableArray array];
+ if (self.validatesDomainName) {
+ [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
+ } else {
+ [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
+ }
+
+ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
+
+ if (self.SSLPinningMode == AFSSLPinningModeNone) {
+ return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
+ } else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) {
+ return NO;
+ }
+
+ switch (self.SSLPinningMode) {
+ case AFSSLPinningModeCertificate: {
+ NSMutableArray *pinnedCertificates = [NSMutableArray array];
+ for (NSData *certificateData in self.pinnedCertificates) {
+ [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
+ }
+ SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
+
+ if (!AFServerTrustIsValid(serverTrust)) {
+ return NO;
+ }
+
+ // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
+ NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
+
+ for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
+ if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
+ return YES;
+ }
+ }
+
+ return NO;
+ }
+ case AFSSLPinningModePublicKey: {
+ NSUInteger trustedPublicKeyCount = 0;
+ NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
+
+ for (id trustChainPublicKey in publicKeys) {
+ for (id pinnedPublicKey in self.pinnedPublicKeys) {
+ if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
+ trustedPublicKeyCount += 1;
+ }
+ }
+ }
+ return trustedPublicKeyCount > 0;
+ }
+
+ default:
+ return NO;
+ }
+
+ return NO;
+}
+
+#pragma mark - NSKeyValueObserving
+
++ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
+ return [NSSet setWithObject:@"pinnedCertificates"];
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+
+ self = [self init];
+ if (!self) {
+ return nil;
+ }
+
+ self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
+ self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
+ self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
+ self.pinnedCertificates = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))];
+ [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
+ [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))];
+ [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
+ securityPolicy.SSLPinningMode = self.SSLPinningMode;
+ securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
+ securityPolicy.validatesDomainName = self.validatesDomainName;
+ securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];
+
+ return securityPolicy;
+}
+
+@end
--- /dev/null
+// AFURLRequestSerialization.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+#import <TargetConditionals.h>
+
+#if TARGET_OS_IOS || TARGET_OS_TV
+#import <UIKit/UIKit.h>
+#elif TARGET_OS_WATCH
+#import <WatchKit/WatchKit.h>
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Returns a percent-escaped string following RFC 3986 for a query string key or value.
+ RFC 3986 states that the following characters are "reserved" characters.
+ - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
+ - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
+
+ In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
+ query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
+ should be percent-escaped in the query string.
+
+ @param string The string to be percent-escaped.
+
+ @return The percent-escaped string.
+ */
+FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
+
+/**
+ A helper method to generate encoded url query parameters for appending to the end of a URL.
+
+ @param parameters A dictionary of key/values to be encoded.
+
+ @return A url encoded query string
+ */
+FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);
+
+/**
+ The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary.
+
+ For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`.
+ */
+@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
+
+/**
+ Returns a request with the specified parameters encoded into a copy of the original request.
+
+ @param request The original request.
+ @param parameters The parameters to be encoded.
+ @param error The error that occurred while attempting to encode the request parameters.
+
+ @return A serialized request.
+ */
+- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
+ withParameters:(nullable id)parameters
+ error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
+
+@end
+
+#pragma mark -
+
+/**
+
+ */
+typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
+ AFHTTPRequestQueryStringDefaultStyle = 0,
+};
+
+@protocol AFMultipartFormData;
+
+/**
+ `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
+
+ Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior.
+ */
+@interface AFHTTPRequestSerializer : NSObject <AFURLRequestSerialization>
+
+/**
+ The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default.
+ */
+@property (nonatomic, assign) NSStringEncoding stringEncoding;
+
+/**
+ Whether created requests can use the device’s cellular radio (if present). `YES` by default.
+
+ @see NSMutableURLRequest -setAllowsCellularAccess:
+ */
+@property (nonatomic, assign) BOOL allowsCellularAccess;
+
+/**
+ The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default.
+
+ @see NSMutableURLRequest -setCachePolicy:
+ */
+@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
+
+/**
+ Whether created requests should use the default cookie handling. `YES` by default.
+
+ @see NSMutableURLRequest -setHTTPShouldHandleCookies:
+ */
+@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
+
+/**
+ Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default
+
+ @see NSMutableURLRequest -setHTTPShouldUsePipelining:
+ */
+@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
+
+/**
+ The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default.
+
+ @see NSMutableURLRequest -setNetworkServiceType:
+ */
+@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
+
+/**
+ The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds.
+
+ @see NSMutableURLRequest -setTimeoutInterval:
+ */
+@property (nonatomic, assign) NSTimeInterval timeoutInterval;
+
+///---------------------------------------
+/// @name Configuring HTTP Request Headers
+///---------------------------------------
+
+/**
+ Default HTTP header field values to be applied to serialized requests. By default, these include the following:
+
+ - `Accept-Language` with the contents of `NSLocale +preferredLanguages`
+ - `User-Agent` with the contents of various bundle identifiers and OS designations
+
+ @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`.
+ */
+@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
+
+/**
+ Creates and returns a serializer with default configuration.
+ */
++ (instancetype)serializer;
+
+/**
+ Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header.
+
+ @param field The HTTP header to set a default value for
+ @param value The value set as default for the specified header, or `nil`
+ */
+- (void)setValue:(nullable NSString *)value
+forHTTPHeaderField:(NSString *)field;
+
+/**
+ Returns the value for the HTTP headers set in the request serializer.
+
+ @param field The HTTP header to retrieve the default value for
+
+ @return The value set as default for the specified header, or `nil`
+ */
+- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
+
+/**
+ Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header.
+
+ @param username The HTTP basic auth username
+ @param password The HTTP basic auth password
+ */
+- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
+ password:(NSString *)password;
+
+/**
+ Clears any existing value for the "Authorization" HTTP header.
+ */
+- (void)clearAuthorizationHeader;
+
+///-------------------------------------------------------
+/// @name Configuring Query String Parameter Serialization
+///-------------------------------------------------------
+
+/**
+ HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default.
+ */
+@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
+
+/**
+ Set the method of query string serialization according to one of the pre-defined styles.
+
+ @param style The serialization style.
+
+ @see AFHTTPRequestQueryStringSerializationStyle
+ */
+- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;
+
+/**
+ Set the a custom method of query string serialization according to the specified block.
+
+ @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request.
+ */
+- (void)setQueryStringSerializationWithBlock:(nullable NSString * _Nullable (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
+
+///-------------------------------
+/// @name Creating Request Objects
+///-------------------------------
+
+/**
+ Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string.
+
+ If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body.
+
+ @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`.
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body.
+ @param error The error that occurred while constructing the request.
+
+ @return An `NSMutableURLRequest` object.
+ */
+- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable id)parameters
+ error:(NSError * _Nullable __autoreleasing *)error;
+
+/**
+ Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
+
+ Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream.
+
+ @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`.
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded and set in the request HTTP body.
+ @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
+ @param error The error that occurred while constructing the request.
+
+ @return An `NSMutableURLRequest` object
+ */
+- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable NSDictionary <NSString *, id> *)parameters
+ constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
+ error:(NSError * _Nullable __autoreleasing *)error;
+
+/**
+ Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished.
+
+ @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`.
+ @param fileURL The file URL to write multipart form contents to.
+ @param handler A handler block to execute.
+
+ @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request.
+
+ @see https://github.com/AFNetworking/AFNetworking/issues/1398
+ */
+- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
+ writingStreamContentsToFile:(NSURL *)fileURL
+ completionHandler:(nullable void (^)(NSError * _Nullable error))handler;
+
+@end
+
+#pragma mark -
+
+/**
+ The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`.
+ */
+@protocol AFMultipartFormData
+
+/**
+ Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary.
+
+ The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively.
+
+ @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
+ @param name The name to be associated with the specified data. This parameter must not be `nil`.
+ @param error If an error occurs, upon return contains an `NSError` object that describes the problem.
+
+ @return `YES` if the file data was successfully appended, otherwise `NO`.
+ */
+- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
+ name:(NSString *)name
+ error:(NSError * _Nullable __autoreleasing *)error;
+
+/**
+ Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
+
+ @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
+ @param name The name to be associated with the specified data. This parameter must not be `nil`.
+ @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`.
+ @param mimeType The declared MIME type of the file data. This parameter must not be `nil`.
+ @param error If an error occurs, upon return contains an `NSError` object that describes the problem.
+
+ @return `YES` if the file data was successfully appended otherwise `NO`.
+ */
+- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ mimeType:(NSString *)mimeType
+ error:(NSError * _Nullable __autoreleasing *)error;
+
+/**
+ Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary.
+
+ @param inputStream The input stream to be appended to the form data
+ @param name The name to be associated with the specified input stream. This parameter must not be `nil`.
+ @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`.
+ @param length The length of the specified input stream in bytes.
+ @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
+ */
+- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ length:(int64_t)length
+ mimeType:(NSString *)mimeType;
+
+/**
+ Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
+
+ @param data The data to be encoded and appended to the form data.
+ @param name The name to be associated with the specified data. This parameter must not be `nil`.
+ @param fileName The filename to be associated with the specified data. This parameter must not be `nil`.
+ @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
+ */
+- (void)appendPartWithFileData:(NSData *)data
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ mimeType:(NSString *)mimeType;
+
+/**
+ Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary.
+
+ @param data The data to be encoded and appended to the form data.
+ @param name The name to be associated with the specified data. This parameter must not be `nil`.
+ */
+
+- (void)appendPartWithFormData:(NSData *)data
+ name:(NSString *)name;
+
+
+/**
+ Appends HTTP headers, followed by the encoded data and the multipart form boundary.
+
+ @param headers The HTTP headers to be appended to the form data.
+ @param body The data to be encoded and appended to the form data. This parameter must not be `nil`.
+ */
+- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers
+ body:(NSData *)body;
+
+/**
+ Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream.
+
+ When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
+
+ @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb.
+ @param delay Duration of delay each time a packet is read. By default, no delay is set.
+ */
+- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
+ delay:(NSTimeInterval)delay;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`.
+ */
+@interface AFJSONRequestSerializer : AFHTTPRequestSerializer
+
+/**
+ Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default.
+ */
+@property (nonatomic, assign) NSJSONWritingOptions writingOptions;
+
+/**
+ Creates and returns a JSON serializer with specified reading and writing options.
+
+ @param writingOptions The specified JSON writing options.
+ */
++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`.
+ */
+@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer
+
+/**
+ The property list format. Possible values are described in "NSPropertyListFormat".
+ */
+@property (nonatomic, assign) NSPropertyListFormat format;
+
+/**
+ @warning The `writeOptions` property is currently unused.
+ */
+@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions;
+
+/**
+ Creates and returns a property list serializer with a specified format, read options, and write options.
+
+ @param format The property list format.
+ @param writeOptions The property list write options.
+
+ @warning The `writeOptions` property is currently unused.
+ */
++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
+ writeOptions:(NSPropertyListWriteOptions)writeOptions;
+
+@end
+
+#pragma mark -
+
+///----------------
+/// @name Constants
+///----------------
+
+/**
+ ## Error Domains
+
+ The following error domain is predefined.
+
+ - `NSString * const AFURLRequestSerializationErrorDomain`
+
+ ### Constants
+
+ `AFURLRequestSerializationErrorDomain`
+ AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
+ */
+FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain;
+
+/**
+ ## User info dictionary keys
+
+ These keys may exist in the user info dictionary, in addition to those defined for NSError.
+
+ - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey`
+
+ ### Constants
+
+ `AFNetworkingOperationFailingURLRequestErrorKey`
+ The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey;
+
+/**
+ ## Throttling Bandwidth for HTTP Request Input Streams
+
+ @see -throttleBandwidthWithPacketSize:delay:
+
+ ### Constants
+
+ `kAFUploadStream3GSuggestedPacketSize`
+ Maximum packet size, in number of bytes. Equal to 16kb.
+
+ `kAFUploadStream3GSuggestedDelay`
+ Duration of delay each time a packet is read. Equal to 0.2 seconds.
+ */
+FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize;
+FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay;
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+// AFURLRequestSerialization.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFURLRequestSerialization.h"
+
+#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
+#import <MobileCoreServices/MobileCoreServices.h>
+#else
+#import <CoreServices/CoreServices.h>
+#endif
+
+NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request";
+NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response";
+
+typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error);
+
+/**
+ Returns a percent-escaped string following RFC 3986 for a query string key or value.
+ RFC 3986 states that the following characters are "reserved" characters.
+ - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
+ - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
+
+ In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
+ query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
+ should be percent-escaped in the query string.
+ - parameter string: The string to be percent-escaped.
+ - returns: The percent-escaped string.
+ */
+NSString * AFPercentEscapedStringFromString(NSString *string) {
+ static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
+ static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
+
+ NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
+ [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
+
+ // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
+ // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
+
+ static NSUInteger const batchSize = 50;
+
+ NSUInteger index = 0;
+ NSMutableString *escaped = @"".mutableCopy;
+
+ while (index < string.length) {
+ NSUInteger length = MIN(string.length - index, batchSize);
+ NSRange range = NSMakeRange(index, length);
+
+ // To avoid breaking up character sequences such as 👴🏻👮🏽
+ range = [string rangeOfComposedCharacterSequencesForRange:range];
+
+ NSString *substring = [string substringWithRange:range];
+ NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
+ [escaped appendString:encoded];
+
+ index += range.length;
+ }
+
+ return escaped;
+}
+
+#pragma mark -
+
+@interface AFQueryStringPair : NSObject
+@property (readwrite, nonatomic, strong) id field;
+@property (readwrite, nonatomic, strong) id value;
+
+- (instancetype)initWithField:(id)field value:(id)value;
+
+- (NSString *)URLEncodedStringValue;
+@end
+
+@implementation AFQueryStringPair
+
+- (instancetype)initWithField:(id)field value:(id)value {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.field = field;
+ self.value = value;
+
+ return self;
+}
+
+- (NSString *)URLEncodedStringValue {
+ if (!self.value || [self.value isEqual:[NSNull null]]) {
+ return AFPercentEscapedStringFromString([self.field description]);
+ } else {
+ return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
+ }
+}
+
+@end
+
+#pragma mark -
+
+FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary);
+FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value);
+
+NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
+ NSMutableArray *mutablePairs = [NSMutableArray array];
+ for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
+ [mutablePairs addObject:[pair URLEncodedStringValue]];
+ }
+
+ return [mutablePairs componentsJoinedByString:@"&"];
+}
+
+NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
+ return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
+}
+
+NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
+ NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
+
+ NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
+
+ if ([value isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *dictionary = value;
+ // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
+ for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
+ id nestedValue = dictionary[nestedKey];
+ if (nestedValue) {
+ [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
+ }
+ }
+ } else if ([value isKindOfClass:[NSArray class]]) {
+ NSArray *array = value;
+ for (id nestedValue in array) {
+ [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
+ }
+ } else if ([value isKindOfClass:[NSSet class]]) {
+ NSSet *set = value;
+ for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
+ [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
+ }
+ } else {
+ [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
+ }
+
+ return mutableQueryStringComponents;
+}
+
+#pragma mark -
+
+@interface AFStreamingMultipartFormData : NSObject <AFMultipartFormData>
+- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
+ stringEncoding:(NSStringEncoding)encoding;
+
+- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;
+@end
+
+#pragma mark -
+
+static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
+ static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
+ });
+
+ return _AFHTTPRequestSerializerObservedKeyPaths;
+}
+
+static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext;
+
+@interface AFHTTPRequestSerializer ()
+@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
+@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
+@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;
+@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
+@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
+@end
+
+@implementation AFHTTPRequestSerializer
+
++ (instancetype)serializer {
+ return [[self alloc] init];
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.stringEncoding = NSUTF8StringEncoding;
+
+ self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
+ self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
+
+ // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
+ NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
+ [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ float q = 1.0f - (idx * 0.1f);
+ [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
+ *stop = q <= 0.5f;
+ }];
+ [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
+
+ // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
+ NSString *userAgent = nil;
+#if TARGET_OS_IOS
+ userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
+#elif TARGET_OS_TV
+ userAgent = [NSString stringWithFormat:@"%@/%@ (%@; tvOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
+#elif TARGET_OS_WATCH
+ userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
+#endif
+ if (userAgent) {
+ if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
+ NSMutableString *mutableUserAgent = [userAgent mutableCopy];
+ if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
+ userAgent = mutableUserAgent;
+ }
+ }
+ [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
+ }
+
+ // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+ self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
+
+ self.mutableObservedChangedKeyPaths = [NSMutableSet set];
+ for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
+ if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
+ [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
+ }
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
+ if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
+ [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext];
+ }
+ }
+}
+
+#pragma mark -
+
+// Workarounds for crashing behavior using Key-Value Observing with XCTest
+// See https://github.com/AFNetworking/AFNetworking/issues/2523
+
+- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
+ _allowsCellularAccess = allowsCellularAccess;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
+}
+
+- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
+ _cachePolicy = cachePolicy;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
+}
+
+- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))];
+ _HTTPShouldHandleCookies = HTTPShouldHandleCookies;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))];
+}
+
+- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))];
+ _HTTPShouldUsePipelining = HTTPShouldUsePipelining;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))];
+}
+
+- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))];
+ _networkServiceType = networkServiceType;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))];
+}
+
+- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval {
+ [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))];
+ _timeoutInterval = timeoutInterval;
+ [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))];
+}
+
+#pragma mark -
+
+- (NSDictionary *)HTTPRequestHeaders {
+ NSDictionary __block *value;
+ dispatch_sync(self.requestHeaderModificationQueue, ^{
+ value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
+ });
+ return value;
+}
+
+- (void)setValue:(NSString *)value
+forHTTPHeaderField:(NSString *)field
+{
+ dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
+ [self.mutableHTTPRequestHeaders setValue:value forKey:field];
+ });
+}
+
+- (NSString *)valueForHTTPHeaderField:(NSString *)field {
+ NSString __block *value;
+ dispatch_sync(self.requestHeaderModificationQueue, ^{
+ value = [self.mutableHTTPRequestHeaders valueForKey:field];
+ });
+ return value;
+}
+
+- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
+ password:(NSString *)password
+{
+ NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
+ NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
+ [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
+}
+
+- (void)clearAuthorizationHeader {
+ dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
+ [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
+ });
+}
+
+#pragma mark -
+
+- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {
+ self.queryStringSerializationStyle = style;
+ self.queryStringSerialization = nil;
+}
+
+- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block {
+ self.queryStringSerialization = block;
+}
+
+#pragma mark -
+
+- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(id)parameters
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(method);
+ NSParameterAssert(URLString);
+
+ NSURL *url = [NSURL URLWithString:URLString];
+
+ NSParameterAssert(url);
+
+ NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
+ mutableRequest.HTTPMethod = method;
+
+ for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
+ if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
+ [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
+ }
+ }
+
+ mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
+
+ return mutableRequest;
+}
+
+- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(NSDictionary *)parameters
+ constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(method);
+ NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
+
+ NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
+
+ __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
+
+ if (parameters) {
+ for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
+ NSData *data = nil;
+ if ([pair.value isKindOfClass:[NSData class]]) {
+ data = pair.value;
+ } else if ([pair.value isEqual:[NSNull null]]) {
+ data = [NSData data];
+ } else {
+ data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
+ }
+
+ if (data) {
+ [formData appendPartWithFormData:data name:[pair.field description]];
+ }
+ }
+ }
+
+ if (block) {
+ block(formData);
+ }
+
+ return [formData requestByFinalizingMultipartFormData];
+}
+
+- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
+ writingStreamContentsToFile:(NSURL *)fileURL
+ completionHandler:(void (^)(NSError *error))handler
+{
+ NSParameterAssert(request.HTTPBodyStream);
+ NSParameterAssert([fileURL isFileURL]);
+
+ NSInputStream *inputStream = request.HTTPBodyStream;
+ NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO];
+ __block NSError *error = nil;
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+
+ [inputStream open];
+ [outputStream open];
+
+ while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {
+ uint8_t buffer[1024];
+
+ NSInteger bytesRead = [inputStream read:buffer maxLength:1024];
+ if (inputStream.streamError || bytesRead < 0) {
+ error = inputStream.streamError;
+ break;
+ }
+
+ NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];
+ if (outputStream.streamError || bytesWritten < 0) {
+ error = outputStream.streamError;
+ break;
+ }
+
+ if (bytesRead == 0 && bytesWritten == 0) {
+ break;
+ }
+ }
+
+ [outputStream close];
+ [inputStream close];
+
+ if (handler) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ handler(error);
+ });
+ }
+ });
+
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
+ mutableRequest.HTTPBodyStream = nil;
+
+ return mutableRequest;
+}
+
+#pragma mark - AFURLRequestSerialization
+
+- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
+ withParameters:(id)parameters
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(request);
+
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
+
+ [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
+ if (![request valueForHTTPHeaderField:field]) {
+ [mutableRequest setValue:value forHTTPHeaderField:field];
+ }
+ }];
+
+ NSString *query = nil;
+ if (parameters) {
+ if (self.queryStringSerialization) {
+ NSError *serializationError;
+ query = self.queryStringSerialization(request, parameters, &serializationError);
+
+ if (serializationError) {
+ if (error) {
+ *error = serializationError;
+ }
+
+ return nil;
+ }
+ } else {
+ switch (self.queryStringSerializationStyle) {
+ case AFHTTPRequestQueryStringDefaultStyle:
+ query = AFQueryStringFromParameters(parameters);
+ break;
+ }
+ }
+ }
+
+ if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
+ if (query && query.length > 0) {
+ mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
+ }
+ } else {
+ // #2864: an empty string is a valid x-www-form-urlencoded payload
+ if (!query) {
+ query = @"";
+ }
+ if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
+ [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
+ }
+ [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
+ }
+
+ return mutableRequest;
+}
+
+#pragma mark - NSKeyValueObserving
+
++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
+ if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
+ return NO;
+ }
+
+ return [super automaticallyNotifiesObserversForKey:key];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(__unused id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if (context == AFHTTPRequestSerializerObserverContext) {
+ if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
+ [self.mutableObservedChangedKeyPaths removeObject:keyPath];
+ } else {
+ [self.mutableObservedChangedKeyPaths addObject:keyPath];
+ }
+ }
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [self init];
+ if (!self) {
+ return nil;
+ }
+
+ self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy];
+ self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ dispatch_sync(self.requestHeaderModificationQueue, ^{
+ [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
+ });
+ [coder encodeObject:@(self.queryStringSerializationStyle) forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
+ dispatch_sync(self.requestHeaderModificationQueue, ^{
+ serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
+ });
+ serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
+ serializer.queryStringSerialization = self.queryStringSerialization;
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+static NSString * AFCreateMultipartFormBoundary() {
+ return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
+}
+
+static NSString * const kAFMultipartFormCRLF = @"\r\n";
+
+static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {
+ return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];
+}
+
+static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) {
+ return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
+}
+
+static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) {
+ return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
+}
+
+static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
+ NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
+ NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
+ if (!contentType) {
+ return @"application/octet-stream";
+ } else {
+ return contentType;
+ }
+}
+
+NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16;
+NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
+
+@interface AFHTTPBodyPart : NSObject
+@property (nonatomic, assign) NSStringEncoding stringEncoding;
+@property (nonatomic, strong) NSDictionary *headers;
+@property (nonatomic, copy) NSString *boundary;
+@property (nonatomic, strong) id body;
+@property (nonatomic, assign) unsigned long long bodyContentLength;
+@property (nonatomic, strong) NSInputStream *inputStream;
+
+@property (nonatomic, assign) BOOL hasInitialBoundary;
+@property (nonatomic, assign) BOOL hasFinalBoundary;
+
+@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable;
+@property (readonly, nonatomic, assign) unsigned long long contentLength;
+
+- (NSInteger)read:(uint8_t *)buffer
+ maxLength:(NSUInteger)length;
+@end
+
+@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
+@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
+@property (nonatomic, assign) NSTimeInterval delay;
+@property (nonatomic, strong) NSInputStream *inputStream;
+@property (readonly, nonatomic, assign) unsigned long long contentLength;
+@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;
+
+- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
+- (void)setInitialAndFinalBoundaries;
+- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
+@end
+
+#pragma mark -
+
+@interface AFStreamingMultipartFormData ()
+@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
+@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
+@property (readwrite, nonatomic, copy) NSString *boundary;
+@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
+@end
+
+@implementation AFStreamingMultipartFormData
+
+- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
+ stringEncoding:(NSStringEncoding)encoding
+{
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.request = urlRequest;
+ self.stringEncoding = encoding;
+ self.boundary = AFCreateMultipartFormBoundary();
+ self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
+
+ return self;
+}
+
+- (void)setRequest:(NSMutableURLRequest *)request
+{
+ _request = [request mutableCopy];
+}
+
+- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
+ name:(NSString *)name
+ error:(NSError * __autoreleasing *)error
+{
+ NSParameterAssert(fileURL);
+ NSParameterAssert(name);
+
+ NSString *fileName = [fileURL lastPathComponent];
+ NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]);
+
+ return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error];
+}
+
+- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ mimeType:(NSString *)mimeType
+ error:(NSError * __autoreleasing *)error
+{
+ NSParameterAssert(fileURL);
+ NSParameterAssert(name);
+ NSParameterAssert(fileName);
+ NSParameterAssert(mimeType);
+
+ if (![fileURL isFileURL]) {
+ NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};
+ if (error) {
+ *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
+ }
+
+ return NO;
+ } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
+ NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};
+ if (error) {
+ *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
+ }
+
+ return NO;
+ }
+
+ NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
+ if (!fileAttributes) {
+ return NO;
+ }
+
+ NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
+ [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
+ [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
+
+ AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
+ bodyPart.stringEncoding = self.stringEncoding;
+ bodyPart.headers = mutableHeaders;
+ bodyPart.boundary = self.boundary;
+ bodyPart.body = fileURL;
+ bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue];
+ [self.bodyStream appendHTTPBodyPart:bodyPart];
+
+ return YES;
+}
+
+- (void)appendPartWithInputStream:(NSInputStream *)inputStream
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ length:(int64_t)length
+ mimeType:(NSString *)mimeType
+{
+ NSParameterAssert(name);
+ NSParameterAssert(fileName);
+ NSParameterAssert(mimeType);
+
+ NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
+ [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
+ [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
+
+ AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
+ bodyPart.stringEncoding = self.stringEncoding;
+ bodyPart.headers = mutableHeaders;
+ bodyPart.boundary = self.boundary;
+ bodyPart.body = inputStream;
+
+ bodyPart.bodyContentLength = (unsigned long long)length;
+
+ [self.bodyStream appendHTTPBodyPart:bodyPart];
+}
+
+- (void)appendPartWithFileData:(NSData *)data
+ name:(NSString *)name
+ fileName:(NSString *)fileName
+ mimeType:(NSString *)mimeType
+{
+ NSParameterAssert(name);
+ NSParameterAssert(fileName);
+ NSParameterAssert(mimeType);
+
+ NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
+ [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
+ [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
+
+ [self appendPartWithHeaders:mutableHeaders body:data];
+}
+
+- (void)appendPartWithFormData:(NSData *)data
+ name:(NSString *)name
+{
+ NSParameterAssert(name);
+
+ NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
+ [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];
+
+ [self appendPartWithHeaders:mutableHeaders body:data];
+}
+
+- (void)appendPartWithHeaders:(NSDictionary *)headers
+ body:(NSData *)body
+{
+ NSParameterAssert(body);
+
+ AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
+ bodyPart.stringEncoding = self.stringEncoding;
+ bodyPart.headers = headers;
+ bodyPart.boundary = self.boundary;
+ bodyPart.bodyContentLength = [body length];
+ bodyPart.body = body;
+
+ [self.bodyStream appendHTTPBodyPart:bodyPart];
+}
+
+- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
+ delay:(NSTimeInterval)delay
+{
+ self.bodyStream.numberOfBytesInPacket = numberOfBytes;
+ self.bodyStream.delay = delay;
+}
+
+- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
+ if ([self.bodyStream isEmpty]) {
+ return self.request;
+ }
+
+ // Reset the initial and final boundaries to ensure correct Content-Length
+ [self.bodyStream setInitialAndFinalBoundaries];
+ [self.request setHTTPBodyStream:self.bodyStream];
+
+ [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
+ [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
+
+ return self.request;
+}
+
+@end
+
+#pragma mark -
+
+@interface NSStream ()
+@property (readwrite) NSStreamStatus streamStatus;
+@property (readwrite, copy) NSError *streamError;
+@end
+
+@interface AFMultipartBodyStream () <NSCopying>
+@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
+@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
+@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
+@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
+@property (readwrite, nonatomic, strong) NSOutputStream *outputStream;
+@property (readwrite, nonatomic, strong) NSMutableData *buffer;
+@end
+
+@implementation AFMultipartBodyStream
+#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100)
+@synthesize delegate;
+#endif
+@synthesize streamStatus;
+@synthesize streamError;
+
+- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.stringEncoding = encoding;
+ self.HTTPBodyParts = [NSMutableArray array];
+ self.numberOfBytesInPacket = NSIntegerMax;
+
+ return self;
+}
+
+- (void)setInitialAndFinalBoundaries {
+ if ([self.HTTPBodyParts count] > 0) {
+ for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
+ bodyPart.hasInitialBoundary = NO;
+ bodyPart.hasFinalBoundary = NO;
+ }
+
+ [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
+ [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
+ }
+}
+
+- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
+ [self.HTTPBodyParts addObject:bodyPart];
+}
+
+- (BOOL)isEmpty {
+ return [self.HTTPBodyParts count] == 0;
+}
+
+#pragma mark - NSInputStream
+
+- (NSInteger)read:(uint8_t *)buffer
+ maxLength:(NSUInteger)length
+{
+ if ([self streamStatus] == NSStreamStatusClosed) {
+ return 0;
+ }
+
+ NSInteger totalNumberOfBytesRead = 0;
+
+ while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
+ if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
+ if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
+ break;
+ }
+ } else {
+ NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
+ NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
+ if (numberOfBytesRead == -1) {
+ self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
+ break;
+ } else {
+ totalNumberOfBytesRead += numberOfBytesRead;
+
+ if (self.delay > 0.0f) {
+ [NSThread sleepForTimeInterval:self.delay];
+ }
+ }
+ }
+ }
+
+ return totalNumberOfBytesRead;
+}
+
+- (BOOL)getBuffer:(__unused uint8_t **)buffer
+ length:(__unused NSUInteger *)len
+{
+ return NO;
+}
+
+- (BOOL)hasBytesAvailable {
+ return [self streamStatus] == NSStreamStatusOpen;
+}
+
+#pragma mark - NSStream
+
+- (void)open {
+ if (self.streamStatus == NSStreamStatusOpen) {
+ return;
+ }
+
+ self.streamStatus = NSStreamStatusOpen;
+
+ [self setInitialAndFinalBoundaries];
+ self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
+}
+
+- (void)close {
+ self.streamStatus = NSStreamStatusClosed;
+}
+
+- (id)propertyForKey:(__unused NSString *)key {
+ return nil;
+}
+
+- (BOOL)setProperty:(__unused id)property
+ forKey:(__unused NSString *)key
+{
+ return NO;
+}
+
+- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
+ forMode:(__unused NSString *)mode
+{}
+
+- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
+ forMode:(__unused NSString *)mode
+{}
+
+- (unsigned long long)contentLength {
+ unsigned long long length = 0;
+ for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
+ length += [bodyPart contentLength];
+ }
+
+ return length;
+}
+
+#pragma mark - Undocumented CFReadStream Bridged Methods
+
+- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
+ forMode:(__unused CFStringRef)aMode
+{}
+
+- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
+ forMode:(__unused CFStringRef)aMode
+{}
+
+- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
+ callback:(__unused CFReadStreamClientCallBack)inCallback
+ context:(__unused CFStreamClientContext *)inContext {
+ return NO;
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
+
+ for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
+ [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
+ }
+
+ [bodyStreamCopy setInitialAndFinalBoundaries];
+
+ return bodyStreamCopy;
+}
+
+@end
+
+#pragma mark -
+
+typedef enum {
+ AFEncapsulationBoundaryPhase = 1,
+ AFHeaderPhase = 2,
+ AFBodyPhase = 3,
+ AFFinalBoundaryPhase = 4,
+} AFHTTPBodyPartReadPhase;
+
+@interface AFHTTPBodyPart () <NSCopying> {
+ AFHTTPBodyPartReadPhase _phase;
+ NSInputStream *_inputStream;
+ unsigned long long _phaseReadOffset;
+}
+
+- (BOOL)transitionToNextPhase;
+- (NSInteger)readData:(NSData *)data
+ intoBuffer:(uint8_t *)buffer
+ maxLength:(NSUInteger)length;
+@end
+
+@implementation AFHTTPBodyPart
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ [self transitionToNextPhase];
+
+ return self;
+}
+
+- (void)dealloc {
+ if (_inputStream) {
+ [_inputStream close];
+ _inputStream = nil;
+ }
+}
+
+- (NSInputStream *)inputStream {
+ if (!_inputStream) {
+ if ([self.body isKindOfClass:[NSData class]]) {
+ _inputStream = [NSInputStream inputStreamWithData:self.body];
+ } else if ([self.body isKindOfClass:[NSURL class]]) {
+ _inputStream = [NSInputStream inputStreamWithURL:self.body];
+ } else if ([self.body isKindOfClass:[NSInputStream class]]) {
+ _inputStream = self.body;
+ } else {
+ _inputStream = [NSInputStream inputStreamWithData:[NSData data]];
+ }
+ }
+
+ return _inputStream;
+}
+
+- (NSString *)stringForHeaders {
+ NSMutableString *headerString = [NSMutableString string];
+ for (NSString *field in [self.headers allKeys]) {
+ [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]];
+ }
+ [headerString appendString:kAFMultipartFormCRLF];
+
+ return [NSString stringWithString:headerString];
+}
+
+- (unsigned long long)contentLength {
+ unsigned long long length = 0;
+
+ NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
+ length += [encapsulationBoundaryData length];
+
+ NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
+ length += [headersData length];
+
+ length += _bodyContentLength;
+
+ NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
+ length += [closingBoundaryData length];
+
+ return length;
+}
+
+- (BOOL)hasBytesAvailable {
+ // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer
+ if (_phase == AFFinalBoundaryPhase) {
+ return YES;
+ }
+
+ switch (self.inputStream.streamStatus) {
+ case NSStreamStatusNotOpen:
+ case NSStreamStatusOpening:
+ case NSStreamStatusOpen:
+ case NSStreamStatusReading:
+ case NSStreamStatusWriting:
+ return YES;
+ case NSStreamStatusAtEnd:
+ case NSStreamStatusClosed:
+ case NSStreamStatusError:
+ default:
+ return NO;
+ }
+}
+
+- (NSInteger)read:(uint8_t *)buffer
+ maxLength:(NSUInteger)length
+{
+ NSInteger totalNumberOfBytesRead = 0;
+
+ if (_phase == AFEncapsulationBoundaryPhase) {
+ NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
+ totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
+ }
+
+ if (_phase == AFHeaderPhase) {
+ NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
+ totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
+ }
+
+ if (_phase == AFBodyPhase) {
+ NSInteger numberOfBytesRead = 0;
+
+ numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
+ if (numberOfBytesRead == -1) {
+ return -1;
+ } else {
+ totalNumberOfBytesRead += numberOfBytesRead;
+
+ if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
+ [self transitionToNextPhase];
+ }
+ }
+ }
+
+ if (_phase == AFFinalBoundaryPhase) {
+ NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
+ totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
+ }
+
+ return totalNumberOfBytesRead;
+}
+
+- (NSInteger)readData:(NSData *)data
+ intoBuffer:(uint8_t *)buffer
+ maxLength:(NSUInteger)length
+{
+ NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
+ [data getBytes:buffer range:range];
+
+ _phaseReadOffset += range.length;
+
+ if (((NSUInteger)_phaseReadOffset) >= [data length]) {
+ [self transitionToNextPhase];
+ }
+
+ return (NSInteger)range.length;
+}
+
+- (BOOL)transitionToNextPhase {
+ if (![[NSThread currentThread] isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ [self transitionToNextPhase];
+ });
+ return YES;
+ }
+
+ switch (_phase) {
+ case AFEncapsulationBoundaryPhase:
+ _phase = AFHeaderPhase;
+ break;
+ case AFHeaderPhase:
+ [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+ [self.inputStream open];
+ _phase = AFBodyPhase;
+ break;
+ case AFBodyPhase:
+ [self.inputStream close];
+ _phase = AFFinalBoundaryPhase;
+ break;
+ case AFFinalBoundaryPhase:
+ default:
+ _phase = AFEncapsulationBoundaryPhase;
+ break;
+ }
+ _phaseReadOffset = 0;
+
+ return YES;
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
+
+ bodyPart.stringEncoding = self.stringEncoding;
+ bodyPart.headers = self.headers;
+ bodyPart.bodyContentLength = self.bodyContentLength;
+ bodyPart.body = self.body;
+ bodyPart.boundary = self.boundary;
+
+ return bodyPart;
+}
+
+@end
+
+#pragma mark -
+
+@implementation AFJSONRequestSerializer
+
++ (instancetype)serializer {
+ return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
+}
+
++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
+{
+ AFJSONRequestSerializer *serializer = [[self alloc] init];
+ serializer.writingOptions = writingOptions;
+
+ return serializer;
+}
+
+#pragma mark - AFURLRequestSerialization
+
+- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
+ withParameters:(id)parameters
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(request);
+
+ if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
+ return [super requestBySerializingRequest:request withParameters:parameters error:error];
+ }
+
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
+
+ [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
+ if (![request valueForHTTPHeaderField:field]) {
+ [mutableRequest setValue:value forHTTPHeaderField:field];
+ }
+ }];
+
+ if (parameters) {
+ if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
+ [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+ }
+
+ if (![NSJSONSerialization isValidJSONObject:parameters]) {
+ if (error) {
+ NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
+ *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
+ }
+ return nil;
+ }
+
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
+
+ if (!jsonData) {
+ return nil;
+ }
+
+ [mutableRequest setHTTPBody:jsonData];
+ }
+
+ return mutableRequest;
+}
+
+#pragma mark - NSSecureCoding
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:@(self.writingOptions) forKey:NSStringFromSelector(@selector(writingOptions))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFJSONRequestSerializer *serializer = [super copyWithZone:zone];
+ serializer.writingOptions = self.writingOptions;
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+@implementation AFPropertyListRequestSerializer
+
++ (instancetype)serializer {
+ return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0];
+}
+
++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
+ writeOptions:(NSPropertyListWriteOptions)writeOptions
+{
+ AFPropertyListRequestSerializer *serializer = [[self alloc] init];
+ serializer.format = format;
+ serializer.writeOptions = writeOptions;
+
+ return serializer;
+}
+
+#pragma mark - AFURLRequestSerializer
+
+- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
+ withParameters:(id)parameters
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(request);
+
+ if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
+ return [super requestBySerializingRequest:request withParameters:parameters error:error];
+ }
+
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
+
+ [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
+ if (![request valueForHTTPHeaderField:field]) {
+ [mutableRequest setValue:value forHTTPHeaderField:field];
+ }
+ }];
+
+ if (parameters) {
+ if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
+ [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
+ }
+
+ NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error];
+
+ if (!plistData) {
+ return nil;
+ }
+
+ [mutableRequest setHTTPBody:plistData];
+ }
+
+ return mutableRequest;
+}
+
+#pragma mark - NSSecureCoding
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue];
+ self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))];
+ [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone];
+ serializer.format = self.format;
+ serializer.writeOptions = self.writeOptions;
+
+ return serializer;
+}
+
+@end
--- /dev/null
+// AFURLResponseSerialization.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Recursively removes `NSNull` values from a JSON object.
+*/
+FOUNDATION_EXPORT id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions);
+
+/**
+ The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
+
+ For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object.
+ */
+@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
+
+/**
+ The response object decoded from the data associated with a specified response.
+
+ @param response The response to be processed.
+ @param data The response data to be decoded.
+ @param error The error that occurred while attempting to decode the response data.
+
+ @return The object decoded from the specified response data.
+ */
+- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
+ data:(nullable NSData *)data
+ error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
+
+ Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior.
+ */
+@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
+
+- (instancetype)init;
+
+/**
+ Creates and returns a serializer with default configuration.
+ */
++ (instancetype)serializer;
+
+///-----------------------------------------
+/// @name Configuring Response Serialization
+///-----------------------------------------
+
+/**
+ The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation.
+
+ See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ */
+@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
+
+/**
+ The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation.
+ */
+@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;
+
+/**
+ Validates the specified response and data.
+
+ In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks.
+
+ @param response The response to be validated.
+ @param data The data associated with the response.
+ @param error The error that occurred while attempting to validate the response.
+
+ @return `YES` if the response is valid, otherwise `NO`.
+ */
+- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
+ data:(nullable NSData *)data
+ error:(NSError * _Nullable __autoreleasing *)error;
+
+@end
+
+#pragma mark -
+
+
+/**
+ `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses.
+
+ By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types:
+
+ - `application/json`
+ - `text/json`
+ - `text/javascript`
+
+ In RFC 7159 - Section 8.1, it states that JSON text is required to be encoded in UTF-8, UTF-16, or UTF-32, and the default encoding is UTF-8. NSJSONSerialization provides support for all the encodings listed in the specification, and recommends UTF-8 for efficiency. Using an unsupported encoding will result in serialization error. See the `NSJSONSerialization` documentation for more details.
+ */
+@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
+
+- (instancetype)init;
+
+/**
+ Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
+ */
+@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
+
+/**
+ Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`.
+ */
+@property (nonatomic, assign) BOOL removesKeysWithNullValues;
+
+/**
+ Creates and returns a JSON serializer with specified reading and writing options.
+
+ @param readingOptions The specified JSON reading options.
+ */
++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects.
+
+ By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
+
+ - `application/xml`
+ - `text/xml`
+ */
+@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
+
+@end
+
+#pragma mark -
+
+#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+
+/**
+ `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
+
+ By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
+
+ - `application/xml`
+ - `text/xml`
+ */
+@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer
+
+- (instancetype)init;
+
+/**
+ Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSXMLDocument` documentation section "Input and Output Options". `0` by default.
+ */
+@property (nonatomic, assign) NSUInteger options;
+
+/**
+ Creates and returns an XML document serializer with the specified options.
+
+ @param mask The XML document options.
+ */
++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask;
+
+@end
+
+#endif
+
+#pragma mark -
+
+/**
+ `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
+
+ By default, `AFPropertyListResponseSerializer` accepts the following MIME types:
+
+ - `application/x-plist`
+ */
+@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
+
+- (instancetype)init;
+
+/**
+ The property list format. Possible values are described in "NSPropertyListFormat".
+ */
+@property (nonatomic, assign) NSPropertyListFormat format;
+
+/**
+ The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions."
+ */
+@property (nonatomic, assign) NSPropertyListReadOptions readOptions;
+
+/**
+ Creates and returns a property list serializer with a specified format, read options, and write options.
+
+ @param format The property list format.
+ @param readOptions The property list reading options.
+ */
++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
+ readOptions:(NSPropertyListReadOptions)readOptions;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses.
+
+ By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
+
+ - `image/tiff`
+ - `image/jpeg`
+ - `image/gif`
+ - `image/png`
+ - `image/ico`
+ - `image/x-icon`
+ - `image/bmp`
+ - `image/x-bmp`
+ - `image/x-xbitmap`
+ - `image/x-win-bitmap`
+ */
+@interface AFImageResponseSerializer : AFHTTPResponseSerializer
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+/**
+ The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance.
+ */
+@property (nonatomic, assign) CGFloat imageScale;
+
+/**
+ Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default.
+ */
+@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
+#endif
+
+@end
+
+#pragma mark -
+
+/**
+ `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
+ */
+@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
+
+/**
+ The component response serializers.
+ */
+@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
+
+/**
+ Creates and returns a compound serializer comprised of the specified response serializers.
+
+ @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`.
+ */
++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;
+
+@end
+
+///----------------
+/// @name Constants
+///----------------
+
+/**
+ ## Error Domains
+
+ The following error domain is predefined.
+
+ - `NSString * const AFURLResponseSerializationErrorDomain`
+
+ ### Constants
+
+ `AFURLResponseSerializationErrorDomain`
+ AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
+ */
+FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain;
+
+/**
+ ## User info dictionary keys
+
+ These keys may exist in the user info dictionary, in addition to those defined for NSError.
+
+ - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey`
+ - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey`
+
+ ### Constants
+
+ `AFNetworkingOperationFailingURLResponseErrorKey`
+ The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`.
+
+ `AFNetworkingOperationFailingURLResponseDataErrorKey`
+ The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey;
+
+FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey;
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+// AFURLResponseSerialization.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFURLResponseSerialization.h"
+
+#import <TargetConditionals.h>
+
+#if TARGET_OS_IOS
+#import <UIKit/UIKit.h>
+#elif TARGET_OS_WATCH
+#import <WatchKit/WatchKit.h>
+#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#import <Cocoa/Cocoa.h>
+#endif
+
+NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response";
+NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response";
+NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data";
+
+static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
+ if (!error) {
+ return underlyingError;
+ }
+
+ if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
+ return error;
+ }
+
+ NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
+ mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
+
+ return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
+}
+
+static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
+ if ([error.domain isEqualToString:domain] && error.code == code) {
+ return YES;
+ } else if (error.userInfo[NSUnderlyingErrorKey]) {
+ return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
+ }
+
+ return NO;
+}
+
+id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
+ if ([JSONObject isKindOfClass:[NSArray class]]) {
+ NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
+ for (id value in (NSArray *)JSONObject) {
+ if (![value isEqual:[NSNull null]]) {
+ [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
+ }
+ }
+
+ return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
+ } else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
+ for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
+ id value = (NSDictionary *)JSONObject[key];
+ if (!value || [value isEqual:[NSNull null]]) {
+ [mutableDictionary removeObjectForKey:key];
+ } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
+ mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
+ }
+ }
+
+ return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
+ }
+
+ return JSONObject;
+}
+
+@implementation AFHTTPResponseSerializer
+
++ (instancetype)serializer {
+ return [[self alloc] init];
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
+ self.acceptableContentTypes = nil;
+
+ return self;
+}
+
+#pragma mark -
+
+- (BOOL)validateResponse:(NSHTTPURLResponse *)response
+ data:(NSData *)data
+ error:(NSError * __autoreleasing *)error
+{
+ BOOL responseIsValid = YES;
+ NSError *validationError = nil;
+
+ if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+ if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
+ !([response MIMEType] == nil && [data length] == 0)) {
+
+ if ([data length] > 0 && [response URL]) {
+ NSMutableDictionary *mutableUserInfo = [@{
+ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
+ NSURLErrorFailingURLErrorKey:[response URL],
+ AFNetworkingOperationFailingURLResponseErrorKey: response,
+ } mutableCopy];
+ if (data) {
+ mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
+ }
+
+ validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
+ }
+
+ responseIsValid = NO;
+ }
+
+ if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
+ NSMutableDictionary *mutableUserInfo = [@{
+ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
+ NSURLErrorFailingURLErrorKey:[response URL],
+ AFNetworkingOperationFailingURLResponseErrorKey: response,
+ } mutableCopy];
+
+ if (data) {
+ mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
+ }
+
+ validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
+
+ responseIsValid = NO;
+ }
+ }
+
+ if (error && !responseIsValid) {
+ *error = validationError;
+ }
+
+ return responseIsValid;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
+
+ return data;
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [self init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
+ self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
+ [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
+ serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone];
+ serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone];
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+@implementation AFJSONResponseSerializer
+
++ (instancetype)serializer {
+ return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
+}
+
++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
+ AFJSONResponseSerializer *serializer = [[self alloc] init];
+ serializer.readingOptions = readingOptions;
+
+ return serializer;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
+
+ return self;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+ // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
+ // See https://github.com/rails/rails/issues/1742
+ BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
+
+ if (data.length == 0 || isSpace) {
+ return nil;
+ }
+
+ NSError *serializationError = nil;
+
+ id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
+
+ if (!responseObject)
+ {
+ if (error) {
+ *error = AFErrorWithUnderlyingError(serializationError, *error);
+ }
+ return nil;
+ }
+
+ if (self.removesKeysWithNullValues) {
+ return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
+ }
+
+ return responseObject;
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue];
+ self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))];
+ [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFJSONResponseSerializer *serializer = [super copyWithZone:zone];
+ serializer.readingOptions = self.readingOptions;
+ serializer.removesKeysWithNullValues = self.removesKeysWithNullValues;
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+@implementation AFXMLParserResponseSerializer
+
++ (instancetype)serializer {
+ AFXMLParserResponseSerializer *serializer = [[self alloc] init];
+
+ return serializer;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];
+
+ return self;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+ return [[NSXMLParser alloc] initWithData:data];
+}
+
+@end
+
+#pragma mark -
+
+#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+
+@implementation AFXMLDocumentResponseSerializer
+
++ (instancetype)serializer {
+ return [self serializerWithXMLDocumentOptions:0];
+}
+
++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask {
+ AFXMLDocumentResponseSerializer *serializer = [[self alloc] init];
+ serializer.options = mask;
+
+ return serializer;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];
+
+ return self;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+ NSError *serializationError = nil;
+ NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
+
+ if (!document)
+ {
+ if (error) {
+ *error = AFErrorWithUnderlyingError(serializationError, *error);
+ }
+ return nil;
+ }
+
+ return document;
+}
+
+#pragma mark - NSSecureCoding
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFXMLDocumentResponseSerializer *serializer = [super copyWithZone:zone];
+ serializer.options = self.options;
+
+ return serializer;
+}
+
+@end
+
+#endif
+
+#pragma mark -
+
+@implementation AFPropertyListResponseSerializer
+
++ (instancetype)serializer {
+ return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0];
+}
+
++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
+ readOptions:(NSPropertyListReadOptions)readOptions
+{
+ AFPropertyListResponseSerializer *serializer = [[self alloc] init];
+ serializer.format = format;
+ serializer.readOptions = readOptions;
+
+ return serializer;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil];
+
+ return self;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+ if (!data) {
+ return nil;
+ }
+
+ NSError *serializationError = nil;
+
+ id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
+
+ if (!responseObject)
+ {
+ if (error) {
+ *error = AFErrorWithUnderlyingError(serializationError, *error);
+ }
+ return nil;
+ }
+
+ return responseObject;
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue];
+ self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))];
+ [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFPropertyListResponseSerializer *serializer = [super copyWithZone:zone];
+ serializer.format = self.format;
+ serializer.readOptions = self.readOptions;
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+#import <CoreGraphics/CoreGraphics.h>
+#import <UIKit/UIKit.h>
+
+@interface UIImage (AFNetworkingSafeImageLoading)
++ (UIImage *)af_safeImageWithData:(NSData *)data;
+@end
+
+static NSLock* imageLock = nil;
+
+@implementation UIImage (AFNetworkingSafeImageLoading)
+
++ (UIImage *)af_safeImageWithData:(NSData *)data {
+ UIImage* image = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ imageLock = [[NSLock alloc] init];
+ });
+
+ [imageLock lock];
+ image = [UIImage imageWithData:data];
+ [imageLock unlock];
+ return image;
+}
+
+@end
+
+static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
+ UIImage *image = [UIImage af_safeImageWithData:data];
+ if (image.images) {
+ return image;
+ }
+
+ return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
+}
+
+static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
+ if (!data || [data length] == 0) {
+ return nil;
+ }
+
+ CGImageRef imageRef = NULL;
+ CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
+
+ if ([response.MIMEType isEqualToString:@"image/png"]) {
+ imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
+ } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
+ imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
+
+ if (imageRef) {
+ CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
+ CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);
+
+ // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale
+ if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
+ CGImageRelease(imageRef);
+ imageRef = NULL;
+ }
+ }
+ }
+
+ CGDataProviderRelease(dataProvider);
+
+ UIImage *image = AFImageWithDataAtScale(data, scale);
+ if (!imageRef) {
+ if (image.images || !image) {
+ return image;
+ }
+
+ imageRef = CGImageCreateCopy([image CGImage]);
+ if (!imageRef) {
+ return nil;
+ }
+ }
+
+ size_t width = CGImageGetWidth(imageRef);
+ size_t height = CGImageGetHeight(imageRef);
+ size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
+
+ if (width * height > 1024 * 1024 || bitsPerComponent > 8) {
+ CGImageRelease(imageRef);
+
+ return image;
+ }
+
+ // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate
+ size_t bytesPerRow = 0;
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
+ CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
+
+ if (colorSpaceModel == kCGColorSpaceModelRGB) {
+ uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wassign-enum"
+ if (alpha == kCGImageAlphaNone) {
+ bitmapInfo &= ~kCGBitmapAlphaInfoMask;
+ bitmapInfo |= kCGImageAlphaNoneSkipFirst;
+ } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
+ bitmapInfo &= ~kCGBitmapAlphaInfoMask;
+ bitmapInfo |= kCGImageAlphaPremultipliedFirst;
+ }
+#pragma clang diagnostic pop
+ }
+
+ CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
+
+ CGColorSpaceRelease(colorSpace);
+
+ if (!context) {
+ CGImageRelease(imageRef);
+
+ return image;
+ }
+
+ CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);
+ CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
+
+ CGContextRelease(context);
+
+ UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
+
+ CGImageRelease(inflatedImageRef);
+ CGImageRelease(imageRef);
+
+ return inflatedImage;
+}
+#endif
+
+
+@implementation AFImageResponseSerializer
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];
+
+#if TARGET_OS_IOS || TARGET_OS_TV
+ self.imageScale = [[UIScreen mainScreen] scale];
+ self.automaticallyInflatesResponseImage = YES;
+#elif TARGET_OS_WATCH
+ self.imageScale = [[WKInterfaceDevice currentDevice] screenScale];
+ self.automaticallyInflatesResponseImage = YES;
+#endif
+
+ return self;
+}
+
+#pragma mark - AFURLResponseSerializer
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+ if (self.automaticallyInflatesResponseImage) {
+ return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
+ } else {
+ return AFImageWithDataAtScale(data, self.imageScale);
+ }
+#else
+ // Ensure that the image is set to it's correct pixel width and height
+ NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
+ NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
+ [image addRepresentation:bitimage];
+
+ return image;
+#endif
+
+ return nil;
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+ NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))];
+#if CGFLOAT_IS_DOUBLE
+ self.imageScale = [imageScale doubleValue];
+#else
+ self.imageScale = [imageScale floatValue];
+#endif
+
+ self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))];
+#endif
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+ [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))];
+ [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))];
+#endif
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFImageResponseSerializer *serializer = [super copyWithZone:zone];
+
+#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
+ serializer.imageScale = self.imageScale;
+ serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage;
+#endif
+
+ return serializer;
+}
+
+@end
+
+#pragma mark -
+
+@interface AFCompoundResponseSerializer ()
+@property (readwrite, nonatomic, copy) NSArray *responseSerializers;
+@end
+
+@implementation AFCompoundResponseSerializer
+
++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers {
+ AFCompoundResponseSerializer *serializer = [[self alloc] init];
+ serializer.responseSerializers = responseSerializers;
+
+ return serializer;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
+ if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
+ continue;
+ }
+
+ NSError *serializerError = nil;
+ id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
+ if (responseObject) {
+ if (error) {
+ *error = AFErrorWithUnderlyingError(serializerError, *error);
+ }
+
+ return responseObject;
+ }
+ }
+
+ return [super responseObjectForResponse:response data:data error:error];
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ NSSet *classes = [NSSet setWithArray:@[[NSArray class], [AFHTTPResponseSerializer <AFURLResponseSerialization> class]]];
+ self.responseSerializers = [decoder decodeObjectOfClasses:classes forKey:NSStringFromSelector(@selector(responseSerializers))];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+
+ [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ AFCompoundResponseSerializer *serializer = [super copyWithZone:zone];
+ serializer.responseSerializers = self.responseSerializers;
+
+ return serializer;
+}
+
+@end
--- /dev/null
+// AFURLSessionManager.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+#import <Foundation/Foundation.h>
+
+#import "AFURLResponseSerialization.h"
+#import "AFURLRequestSerialization.h"
+#import "AFSecurityPolicy.h"
+#import "AFCompatibilityMacros.h"
+#if !TARGET_OS_WATCH
+#import "AFNetworkReachabilityManager.h"
+#endif
+
+/**
+ `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to `<NSURLSessionTaskDelegate>`, `<NSURLSessionDataDelegate>`, `<NSURLSessionDownloadDelegate>`, and `<NSURLSessionDelegate>`.
+
+ ## Subclassing Notes
+
+ This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead.
+
+ ## NSURLSession & NSURLSessionTask Delegate Methods
+
+ `AFURLSessionManager` implements the following delegate methods:
+
+ ### `NSURLSessionDelegate`
+
+ - `URLSession:didBecomeInvalidWithError:`
+ - `URLSession:didReceiveChallenge:completionHandler:`
+ - `URLSessionDidFinishEventsForBackgroundURLSession:`
+
+ ### `NSURLSessionTaskDelegate`
+
+ - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`
+ - `URLSession:task:didReceiveChallenge:completionHandler:`
+ - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
+ - `URLSession:task:needNewBodyStream:`
+ - `URLSession:task:didCompleteWithError:`
+
+ ### `NSURLSessionDataDelegate`
+
+ - `URLSession:dataTask:didReceiveResponse:completionHandler:`
+ - `URLSession:dataTask:didBecomeDownloadTask:`
+ - `URLSession:dataTask:didReceiveData:`
+ - `URLSession:dataTask:willCacheResponse:completionHandler:`
+
+ ### `NSURLSessionDownloadDelegate`
+
+ - `URLSession:downloadTask:didFinishDownloadingToURL:`
+ - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`
+ - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
+
+ If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
+
+ ## Network Reachability Monitoring
+
+ Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details.
+
+ ## NSCoding Caveats
+
+ - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`.
+
+ ## NSCopying Caveats
+
+ - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original.
+ - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied.
+
+ @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance.
+ */
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
+
+/**
+ The managed session.
+ */
+@property (readonly, nonatomic, strong) NSURLSession *session;
+
+/**
+ The operation queue on which delegate callbacks are run.
+ */
+@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
+
+/**
+ Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
+
+ @warning `responseSerializer` must not be `nil`.
+ */
+@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
+
+///-------------------------------
+/// @name Managing Security Policy
+///-------------------------------
+
+/**
+ The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
+ */
+@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
+
+#if !TARGET_OS_WATCH
+///--------------------------------------
+/// @name Monitoring Network Reachability
+///--------------------------------------
+
+/**
+ The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default.
+ */
+@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
+#endif
+
+///----------------------------
+/// @name Getting Session Tasks
+///----------------------------
+
+/**
+ The data, upload, and download tasks currently run by the managed session.
+ */
+@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
+
+/**
+ The data tasks currently run by the managed session.
+ */
+@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
+
+/**
+ The upload tasks currently run by the managed session.
+ */
+@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
+
+/**
+ The download tasks currently run by the managed session.
+ */
+@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
+
+///-------------------------------
+/// @name Managing Callback Queues
+///-------------------------------
+
+/**
+ The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
+ */
+@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
+
+/**
+ The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
+ */
+@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
+
+///---------------------
+/// @name Initialization
+///---------------------
+
+/**
+ Creates and returns a manager for a session created with the specified configuration. This is the designated initializer.
+
+ @param configuration The configuration used to create the managed session.
+
+ @return A manager for a newly-created session.
+ */
+- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
+
+/**
+ Invalidates the managed session, optionally canceling pending tasks and optionally resets given session.
+
+ @param cancelPendingTasks Whether or not to cancel pending tasks.
+ @param resetSession Whether or not to reset the session of the manager.
+ */
+- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession;
+
+///-------------------------
+/// @name Running Data Tasks
+///-------------------------
+
+/**
+ Creates an `NSURLSessionDataTask` with the specified request.
+
+ @param request The HTTP request for the request.
+ @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
+ */
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
+ completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
+
+///---------------------------
+/// @name Running Upload Tasks
+///---------------------------
+
+/**
+ Creates an `NSURLSessionUploadTask` with the specified request for a local file.
+
+ @param request The HTTP request for the request.
+ @param fileURL A URL to the local file to be uploaded.
+ @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
+
+ @see `attemptsToRecreateUploadTasksForBackgroundSessions`
+ */
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromFile:(NSURL *)fileURL
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
+ completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
+
+/**
+ Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body.
+
+ @param request The HTTP request for the request.
+ @param bodyData A data object containing the HTTP body to be uploaded.
+ @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
+ */
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromData:(nullable NSData *)bodyData
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
+ completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
+
+/**
+ Creates an `NSURLSessionUploadTask` with the specified streaming request.
+
+ @param request The HTTP request for the request.
+ @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
+ */
+- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
+ progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
+ completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
+
+///-----------------------------
+/// @name Running Download Tasks
+///-----------------------------
+
+/**
+ Creates an `NSURLSessionDownloadTask` with the specified request.
+
+ @param request The HTTP request for the request.
+ @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
+ @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
+
+ @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method.
+ */
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
+ progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
+ destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
+ completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
+
+/**
+ Creates an `NSURLSessionDownloadTask` with the specified resume data.
+
+ @param resumeData The data used to resume downloading.
+ @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
+ @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
+ @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
+ */
+- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
+ progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
+ destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
+ completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
+
+///---------------------------------
+/// @name Getting Progress for Tasks
+///---------------------------------
+
+/**
+ Returns the upload progress of the specified task.
+
+ @param task The session task. Must not be `nil`.
+
+ @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable.
+ */
+- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
+
+/**
+ Returns the download progress of the specified task.
+
+ @param task The session task. Must not be `nil`.
+
+ @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable.
+ */
+- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
+
+///-----------------------------------------
+/// @name Setting Session Delegate Callbacks
+///-----------------------------------------
+
+/**
+ Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`.
+
+ @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation.
+ */
+- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
+
+/**
+ Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`.
+
+ @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
+
+ @warning Implementing a session authentication challenge handler yourself totally bypasses AFNetworking's security policy defined in `AFSecurityPolicy`. Make sure you fully understand the implications before implementing a custom session authentication challenge handler. If you do not want to bypass AFNetworking's security policy, use `setTaskDidReceiveAuthenticationChallengeBlock:` instead.
+
+ @see -securityPolicy
+ @see -setTaskDidReceiveAuthenticationChallengeBlock:
+ */
+- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
+
+///--------------------------------------
+/// @name Setting Task Delegate Callbacks
+///--------------------------------------
+
+/**
+ Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`.
+
+ @param block A block object to be executed when a task requires a new request body stream.
+ */
+- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
+
+/**
+ Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`.
+
+ @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response.
+ */
+- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
+
+/**
+ Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`.
+
+ @param authenticationChallengeHandler A block object to be executed when a session task has received a request specific authentication challenge.
+
+ When implementing an authentication challenge handler, you should check the authentication method first (`challenge.protectionSpace.authenticationMethod `) to decide if you want to handle the authentication challenge yourself or if you want AFNetworking to handle it. If you want AFNetworking to handle the authentication challenge, just return `@(NSURLSessionAuthChallengePerformDefaultHandling)`. For example, you certainly want AFNetworking to handle certificate validation (i.e. authentication method == `NSURLAuthenticationMethodServerTrust`) as defined by the security policy. If you want to handle the challenge yourself, you have four options:
+
+ 1. Return `nil` from the authentication challenge handler. You **MUST** call the completion handler with a disposition and credentials yourself. Use this if you need to present a user interface to let the user enter their credentials.
+ 2. Return an `NSError` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSError `. The returned error will be reported in the completion handler of the task. Use this if you need to abort an authentication challenge with a specific error.
+ 3. Return an `NSURLCredential` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSURLCredential`. The returned credentials will be used to fulfil the challenge. Use this when you can get credentials without presenting a user interface.
+ 4. Return an `NSNumber` object wrapping an `NSURLSessionAuthChallengeDisposition`. Supported values are `@(NSURLSessionAuthChallengePerformDefaultHandling)`, `@(NSURLSessionAuthChallengeCancelAuthenticationChallenge)` and `@(NSURLSessionAuthChallengeRejectProtectionSpace)`. You **MUST NOT** call the completion handler when returning an `NSNumber`.
+
+ If you return anything else from the authentication challenge handler, an exception will be thrown.
+
+ For more information about how URL sessions handle the different types of authentication challenges, see [NSURLSession](https://developer.apple.com/reference/foundation/nsurlsession?language=objc) and [URL Session Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html).
+
+ @see -securityPolicy
+ */
+- (void)setAuthenticationChallengeHandler:(id (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition , NSURLCredential * _Nullable)))authenticationChallengeHandler;
+
+/**
+ Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
+
+ @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
+ */
+- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
+
+/**
+ Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`.
+
+ @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task.
+ */
+- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
+
+/**
+ Sets a block to be executed when metrics are finalized related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didFinishCollectingMetrics:`.
+
+ @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any metrics that were collected in the process of executing the task.
+ */
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+- (void)setTaskDidFinishCollectingMetricsBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * _Nullable metrics))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
+#endif
+///-------------------------------------------
+/// @name Setting Data Task Delegate Callbacks
+///-------------------------------------------
+
+/**
+ Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
+
+ @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response.
+ */
+- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
+
+/**
+ Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`.
+
+ @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become.
+ */
+- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
+
+/**
+ Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`.
+
+ @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue.
+ */
+- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
+
+/**
+ Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`.
+
+ @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response.
+ */
+- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
+
+/**
+ Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`.
+
+ @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session.
+ */
+- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block AF_API_UNAVAILABLE(macos);
+
+///-----------------------------------------------
+/// @name Setting Download Task Delegate Callbacks
+///-----------------------------------------------
+
+/**
+ Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`.
+
+ @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
+ */
+- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
+
+/**
+ Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
+
+ @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue.
+ */
+- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
+
+/**
+ Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
+
+ @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded.
+ */
+- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
+
+@end
+
+///--------------------
+/// @name Notifications
+///--------------------
+
+/**
+ Posted when a task resumes.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;
+
+/**
+ Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification;
+
+/**
+ Posted when a task suspends its execution.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;
+
+/**
+ Posted when a session is invalidated.
+ */
+FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
+
+/**
+ Posted when a session download task finished moving the temporary download file to a specified destination successfully.
+ */
+FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification;
+
+/**
+ Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
+ */
+FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;
+
+/**
+ The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;
+
+/**
+ The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;
+
+/**
+ The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;
+
+/**
+ The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
+
+/**
+ Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists.
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
+
+/**
+ The session task metrics taken from the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteSessionTaskMetrics`
+ */
+FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics;
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+// AFURLSessionManager.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFURLSessionManager.h"
+#import <objc/runtime.h>
+
+static dispatch_queue_t url_session_manager_processing_queue() {
+ static dispatch_queue_t af_url_session_manager_processing_queue;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
+ });
+
+ return af_url_session_manager_processing_queue;
+}
+
+static dispatch_group_t url_session_manager_completion_group() {
+ static dispatch_group_t af_url_session_manager_completion_group;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ af_url_session_manager_completion_group = dispatch_group_create();
+ });
+
+ return af_url_session_manager_completion_group;
+}
+
+NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
+NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
+NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
+NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
+NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification = @"com.alamofire.networking.session.download.file-manager-succeed";
+NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
+
+NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
+NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
+NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
+NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
+NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
+NSString * const AFNetworkingTaskDidCompleteSessionTaskMetrics = @"com.alamofire.networking.complete.sessiontaskmetrics";
+
+static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
+
+typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
+typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
+
+typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
+typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
+typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential));
+typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
+
+typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
+typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
+typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics) AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
+#endif
+
+typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
+typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
+typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
+typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);
+
+typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
+typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
+typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
+typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
+
+typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
+
+#pragma mark -
+
+@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
+- (instancetype)initWithTask:(NSURLSessionTask *)task;
+@property (nonatomic, weak) AFURLSessionManager *manager;
+@property (nonatomic, strong) NSMutableData *mutableData;
+@property (nonatomic, strong) NSProgress *uploadProgress;
+@property (nonatomic, strong) NSProgress *downloadProgress;
+@property (nonatomic, copy) NSURL *downloadFileURL;
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
+#endif
+@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
+@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
+@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
+@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
+@end
+
+@implementation AFURLSessionManagerTaskDelegate
+
+- (instancetype)initWithTask:(NSURLSessionTask *)task {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _mutableData = [NSMutableData data];
+ _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
+ _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
+
+ __weak __typeof__(task) weakTask = task;
+ for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
+ {
+ progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
+ progress.cancellable = YES;
+ progress.cancellationHandler = ^{
+ [weakTask cancel];
+ };
+ progress.pausable = YES;
+ progress.pausingHandler = ^{
+ [weakTask suspend];
+ };
+#if AF_CAN_USE_AT_AVAILABLE
+ if (@available(macOS 10.11, *))
+#else
+ if ([progress respondsToSelector:@selector(setResumingHandler:)])
+#endif
+ {
+ progress.resumingHandler = ^{
+ [weakTask resume];
+ };
+ }
+
+ [progress addObserver:self
+ forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
+ options:NSKeyValueObservingOptionNew
+ context:NULL];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
+ [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
+}
+
+#pragma mark - NSProgress Tracking
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
+ if ([object isEqual:self.downloadProgress]) {
+ if (self.downloadProgressBlock) {
+ self.downloadProgressBlock(object);
+ }
+ }
+ else if ([object isEqual:self.uploadProgress]) {
+ if (self.uploadProgressBlock) {
+ self.uploadProgressBlock(object);
+ }
+ }
+}
+
+static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey;
+
+#pragma mark - NSURLSessionTaskDelegate
+
+- (void)URLSession:(__unused NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didCompleteWithError:(NSError *)error
+{
+ error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
+ __strong AFURLSessionManager *manager = self.manager;
+
+ __block id responseObject = nil;
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
+
+ //Performance Improvement from #2672
+ NSData *data = nil;
+ if (self.mutableData) {
+ data = [self.mutableData copy];
+ //We no longer need the reference, so nil it out to gain back some memory.
+ self.mutableData = nil;
+ }
+
+#if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
+ if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
+ if (self.sessionTaskMetrics) {
+ userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
+ }
+ }
+#endif
+
+ if (self.downloadFileURL) {
+ userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
+ } else if (data) {
+ userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
+ }
+
+ if (error) {
+ userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
+
+ dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
+ if (self.completionHandler) {
+ self.completionHandler(task.response, responseObject, error);
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
+ });
+ });
+ } else {
+ dispatch_async(url_session_manager_processing_queue(), ^{
+ NSError *serializationError = nil;
+ responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
+
+ if (self.downloadFileURL) {
+ responseObject = self.downloadFileURL;
+ }
+
+ if (responseObject) {
+ userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
+ }
+
+ if (serializationError) {
+ userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
+ }
+
+ dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
+ if (self.completionHandler) {
+ self.completionHandler(task.response, responseObject, serializationError);
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
+ });
+ });
+ });
+ }
+}
+
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
+ self.sessionTaskMetrics = metrics;
+}
+#endif
+
+#pragma mark - NSURLSessionDataDelegate
+
+- (void)URLSession:(__unused NSURLSession *)session
+ dataTask:(__unused NSURLSessionDataTask *)dataTask
+ didReceiveData:(NSData *)data
+{
+ self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
+ self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
+
+ [self.mutableData appendData:data];
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
+ didSendBodyData:(int64_t)bytesSent
+ totalBytesSent:(int64_t)totalBytesSent
+totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
+
+ self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
+ self.uploadProgress.completedUnitCount = task.countOfBytesSent;
+}
+
+#pragma mark - NSURLSessionDownloadDelegate
+
+- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
+ didWriteData:(int64_t)bytesWritten
+ totalBytesWritten:(int64_t)totalBytesWritten
+totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
+
+ self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
+ self.downloadProgress.completedUnitCount = totalBytesWritten;
+}
+
+- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
+ didResumeAtOffset:(int64_t)fileOffset
+expectedTotalBytes:(int64_t)expectedTotalBytes{
+
+ self.downloadProgress.totalUnitCount = expectedTotalBytes;
+ self.downloadProgress.completedUnitCount = fileOffset;
+}
+
+- (void)URLSession:(NSURLSession *)session
+ downloadTask:(NSURLSessionDownloadTask *)downloadTask
+didFinishDownloadingToURL:(NSURL *)location
+{
+ self.downloadFileURL = nil;
+
+ if (self.downloadTaskDidFinishDownloading) {
+ self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
+ if (self.downloadFileURL) {
+ NSError *fileManagerError = nil;
+
+ if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
+ } else {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
+ }
+ }
+ }
+}
+
+@end
+
+#pragma mark -
+
+/**
+ * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`.
+ *
+ * See:
+ * - https://github.com/AFNetworking/AFNetworking/issues/1477
+ * - https://github.com/AFNetworking/AFNetworking/issues/2638
+ * - https://github.com/AFNetworking/AFNetworking/pull/2702
+ */
+
+static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
+ Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
+ Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
+ method_exchangeImplementations(originalMethod, swizzledMethod);
+}
+
+static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
+ return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
+}
+
+static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume";
+static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
+
+@interface _AFURLSessionTaskSwizzling : NSObject
+
+@end
+
+@implementation _AFURLSessionTaskSwizzling
+
++ (void)load {
+ /**
+ WARNING: Trouble Ahead
+ https://github.com/AFNetworking/AFNetworking/pull/2702
+ */
+
+ if (NSClassFromString(@"NSURLSessionTask")) {
+ /**
+ iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky.
+ Many Unit Tests have been built to validate as much of this behavior has possible.
+ Here is what we know:
+ - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back.
+ - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there.
+ - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`.
+ - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`.
+ - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled.
+ - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled.
+ - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.
+
+ Some Assumptions:
+ - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
+ - No background task classes override `resume` or `suspend`
+
+ The current solution:
+ 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
+ 2) Grab a pointer to the original implementation of `af_resume`
+ 3) Check to see if the current class has an implementation of resume. If so, continue to step 4.
+ 4) Grab the super class of the current class.
+ 5) Grab a pointer for the current class to the current implementation of `resume`.
+ 6) Grab a pointer for the super class to the current implementation of `resume`.
+ 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods
+ 8) Set the current class to the super class, and repeat steps 3-8
+ */
+ NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
+ NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnonnull"
+ NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
+#pragma clang diagnostic pop
+ IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
+ Class currentClass = [localDataTask class];
+
+ while (class_getInstanceMethod(currentClass, @selector(resume))) {
+ Class superClass = [currentClass superclass];
+ IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
+ IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
+ if (classResumeIMP != superclassResumeIMP &&
+ originalAFResumeIMP != classResumeIMP) {
+ [self swizzleResumeAndSuspendMethodForClass:currentClass];
+ }
+ currentClass = [currentClass superclass];
+ }
+
+ [localDataTask cancel];
+ [session finishTasksAndInvalidate];
+ }
+}
+
++ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
+ Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
+ Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
+
+ if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
+ af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
+ }
+
+ if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
+ af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
+ }
+}
+
+- (NSURLSessionTaskState)state {
+ NSAssert(NO, @"State method should never be called in the actual dummy class");
+ return NSURLSessionTaskStateCanceling;
+}
+
+- (void)af_resume {
+ NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
+ NSURLSessionTaskState state = [self state];
+ [self af_resume];
+
+ if (state != NSURLSessionTaskStateRunning) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
+ }
+}
+
+- (void)af_suspend {
+ NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
+ NSURLSessionTaskState state = [self state];
+ [self af_suspend];
+
+ if (state != NSURLSessionTaskStateSuspended) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
+ }
+}
+@end
+
+#pragma mark -
+
+@interface AFURLSessionManager ()
+@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
+@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
+@property (readwrite, nonatomic, strong) NSURLSession *session;
+@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
+@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
+@property (readwrite, nonatomic, strong) NSLock *lock;
+@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
+@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
+@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
+@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
+#endif
+@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
+@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
+@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
+@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
+@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
+@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
+@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
+@end
+
+@implementation AFURLSessionManager
+
+- (instancetype)init {
+ return [self initWithSessionConfiguration:nil];
+}
+
+- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ if (!configuration) {
+ configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
+ }
+
+ self.sessionConfiguration = configuration;
+
+ self.operationQueue = [[NSOperationQueue alloc] init];
+ self.operationQueue.maxConcurrentOperationCount = 1;
+
+ self.responseSerializer = [AFJSONResponseSerializer serializer];
+
+ self.securityPolicy = [AFSecurityPolicy defaultPolicy];
+
+#if !TARGET_OS_WATCH
+ self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
+#endif
+
+ self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
+
+ self.lock = [[NSLock alloc] init];
+ self.lock.name = AFURLSessionManagerLockName;
+
+ [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
+ for (NSURLSessionDataTask *task in dataTasks) {
+ [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
+ }
+
+ for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
+ [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
+ }
+
+ for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
+ [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
+ }
+ }];
+
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark -
+
+- (NSURLSession *)session {
+
+ @synchronized (self) {
+ if (!_session) {
+ _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
+ }
+ }
+ return _session;
+}
+
+#pragma mark -
+
+
+- (NSString *)taskDescriptionForSessionTasks {
+ return [NSString stringWithFormat:@"%p", self];
+}
+
+- (void)taskDidResume:(NSNotification *)notification {
+ NSURLSessionTask *task = notification.object;
+ if ([task respondsToSelector:@selector(taskDescription)]) {
+ if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
+ });
+ }
+ }
+}
+
+- (void)taskDidSuspend:(NSNotification *)notification {
+ NSURLSessionTask *task = notification.object;
+ if ([task respondsToSelector:@selector(taskDescription)]) {
+ if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
+ });
+ }
+ }
+}
+
+#pragma mark -
+
+- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
+ NSParameterAssert(task);
+
+ AFURLSessionManagerTaskDelegate *delegate = nil;
+ [self.lock lock];
+ delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
+ [self.lock unlock];
+
+ return delegate;
+}
+
+- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
+ forTask:(NSURLSessionTask *)task
+{
+ NSParameterAssert(task);
+ NSParameterAssert(delegate);
+
+ [self.lock lock];
+ self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
+ [self addNotificationObserverForTask:task];
+ [self.lock unlock];
+}
+
+- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+ completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
+{
+ AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
+ delegate.manager = self;
+ delegate.completionHandler = completionHandler;
+
+ dataTask.taskDescription = self.taskDescriptionForSessionTasks;
+ [self setDelegate:delegate forTask:dataTask];
+
+ delegate.uploadProgressBlock = uploadProgressBlock;
+ delegate.downloadProgressBlock = downloadProgressBlock;
+}
+
+- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
+ progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
+{
+ AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
+ delegate.manager = self;
+ delegate.completionHandler = completionHandler;
+
+ uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
+
+ [self setDelegate:delegate forTask:uploadTask];
+
+ delegate.uploadProgressBlock = uploadProgressBlock;
+}
+
+- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
+ progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+ destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
+ completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
+{
+ AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
+ delegate.manager = self;
+ delegate.completionHandler = completionHandler;
+
+ if (destination) {
+ delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
+ return destination(location, task.response);
+ };
+ }
+
+ downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
+
+ [self setDelegate:delegate forTask:downloadTask];
+
+ delegate.downloadProgressBlock = downloadProgressBlock;
+}
+
+- (void)removeDelegateForTask:(NSURLSessionTask *)task {
+ NSParameterAssert(task);
+
+ [self.lock lock];
+ [self removeNotificationObserverForTask:task];
+ [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
+ [self.lock unlock];
+}
+
+#pragma mark -
+
+- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
+ __block NSArray *tasks = nil;
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
+ if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
+ tasks = dataTasks;
+ } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
+ tasks = uploadTasks;
+ } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
+ tasks = downloadTasks;
+ } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
+ tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
+ }
+
+ dispatch_semaphore_signal(semaphore);
+ }];
+
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+
+ return tasks;
+}
+
+- (NSArray *)tasks {
+ return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
+}
+
+- (NSArray *)dataTasks {
+ return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
+}
+
+- (NSArray *)uploadTasks {
+ return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
+}
+
+- (NSArray *)downloadTasks {
+ return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
+}
+
+#pragma mark -
+
+- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession {
+ if (cancelPendingTasks) {
+ [self.session invalidateAndCancel];
+ } else {
+ [self.session finishTasksAndInvalidate];
+ }
+ if (resetSession) {
+ self.session = nil;
+ }
+}
+
+#pragma mark -
+
+- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
+ NSParameterAssert(responseSerializer);
+
+ _responseSerializer = responseSerializer;
+}
+
+#pragma mark -
+- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
+}
+
+- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
+}
+
+#pragma mark -
+
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+ completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
+
+ NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
+
+ [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
+
+ return dataTask;
+}
+
+#pragma mark -
+
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromFile:(NSURL *)fileURL
+ progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
+{
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
+
+ if (uploadTask) {
+ [self addDelegateForUploadTask:uploadTask
+ progress:uploadProgressBlock
+ completionHandler:completionHandler];
+ }
+
+ return uploadTask;
+}
+
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromData:(NSData *)bodyData
+ progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
+{
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
+
+ [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
+
+ return uploadTask;
+}
+
+- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
+ progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
+ completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
+{
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
+
+ [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
+
+ return uploadTask;
+}
+
+#pragma mark -
+
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
+ progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+ destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
+ completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
+{
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
+
+ [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
+
+ return downloadTask;
+}
+
+- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
+ progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
+ destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
+ completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
+{
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
+
+ [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
+
+ return downloadTask;
+}
+
+#pragma mark -
+- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
+ return [[self delegateForTask:task] uploadProgress];
+}
+
+- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
+ return [[self delegateForTask:task] downloadProgress];
+}
+
+#pragma mark -
+
+- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
+ self.sessionDidBecomeInvalid = block;
+}
+
+- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
+ self.sessionDidReceiveAuthenticationChallenge = block;
+}
+
+#if !TARGET_OS_OSX
+- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
+ self.didFinishEventsForBackgroundURLSession = block;
+}
+#endif
+
+#pragma mark -
+
+- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
+ self.taskNeedNewBodyStream = block;
+}
+
+- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
+ self.taskWillPerformHTTPRedirection = block;
+}
+
+- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
+ self.taskDidSendBodyData = block;
+}
+
+- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
+ self.taskDidComplete = block;
+}
+
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+- (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
+ self.taskDidFinishCollectingMetrics = block;
+}
+#endif
+
+#pragma mark -
+
+- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
+ self.dataTaskDidReceiveResponse = block;
+}
+
+- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
+ self.dataTaskDidBecomeDownloadTask = block;
+}
+
+- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
+ self.dataTaskDidReceiveData = block;
+}
+
+- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
+ self.dataTaskWillCacheResponse = block;
+}
+
+#pragma mark -
+
+- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
+ self.downloadTaskDidFinishDownloading = block;
+}
+
+- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
+ self.downloadTaskDidWriteData = block;
+}
+
+- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
+ self.downloadTaskDidResume = block;
+}
+
+#pragma mark - NSObject
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
+}
+
+- (BOOL)respondsToSelector:(SEL)selector {
+ if (selector == @selector(URLSession:didReceiveChallenge:completionHandler:)) {
+ return self.sessionDidReceiveAuthenticationChallenge != nil;
+ } else if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
+ return self.taskWillPerformHTTPRedirection != nil;
+ } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
+ return self.dataTaskDidReceiveResponse != nil;
+ } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
+ return self.dataTaskWillCacheResponse != nil;
+ }
+#if !TARGET_OS_OSX
+ else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
+ return self.didFinishEventsForBackgroundURLSession != nil;
+ }
+#endif
+
+ return [[self class] instancesRespondToSelector:selector];
+}
+
+#pragma mark - NSURLSessionDelegate
+
+- (void)URLSession:(NSURLSession *)session
+didBecomeInvalidWithError:(NSError *)error
+{
+ if (self.sessionDidBecomeInvalid) {
+ self.sessionDidBecomeInvalid(session, error);
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
+}
+
+- (void)URLSession:(NSURLSession *)session
+didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
+{
+ NSAssert(self.sessionDidReceiveAuthenticationChallenge != nil, @"`respondsToSelector:` implementation forces `URLSession:didReceiveChallenge:completionHandler:` to be called only if `self.sessionDidReceiveAuthenticationChallenge` is not nil");
+
+ NSURLCredential *credential = nil;
+ NSURLSessionAuthChallengeDisposition disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
+
+ if (completionHandler) {
+ completionHandler(disposition, credential);
+ }
+}
+
+#pragma mark - NSURLSessionTaskDelegate
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+willPerformHTTPRedirection:(NSHTTPURLResponse *)response
+ newRequest:(NSURLRequest *)request
+ completionHandler:(void (^)(NSURLRequest *))completionHandler
+{
+ NSURLRequest *redirectRequest = request;
+
+ if (self.taskWillPerformHTTPRedirection) {
+ redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
+ }
+
+ if (completionHandler) {
+ completionHandler(redirectRequest);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
+{
+ BOOL evaluateServerTrust = NO;
+ NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
+ NSURLCredential *credential = nil;
+
+ if (self.authenticationChallengeHandler) {
+ id result = self.authenticationChallengeHandler(session, task, challenge, completionHandler);
+ if (result == nil) {
+ return;
+ } else if ([result isKindOfClass:NSError.class]) {
+ objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN);
+ disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
+ } else if ([result isKindOfClass:NSURLCredential.class]) {
+ credential = result;
+ disposition = NSURLSessionAuthChallengeUseCredential;
+ } else if ([result isKindOfClass:NSNumber.class]) {
+ disposition = [result integerValue];
+ NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @"");
+ evaluateServerTrust = disposition == NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
+ } else {
+ @throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil];
+ }
+ } else {
+ evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
+ }
+
+ if (evaluateServerTrust) {
+ if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
+ disposition = NSURLSessionAuthChallengeUseCredential;
+ credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ } else {
+ objc_setAssociatedObject(task, AuthenticationChallengeErrorKey,
+ [self serverTrustErrorForServerTrust:challenge.protectionSpace.serverTrust url:task.currentRequest.URL],
+ OBJC_ASSOCIATION_RETAIN);
+ disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
+ }
+ }
+
+ if (completionHandler) {
+ completionHandler(disposition, credential);
+ }
+}
+
+- (nonnull NSError *)serverTrustErrorForServerTrust:(nullable SecTrustRef)serverTrust url:(nullable NSURL *)url
+{
+ NSBundle *CFNetworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.CFNetwork"];
+ NSString *defaultValue = @"The certificate for this server is invalid. You might be connecting to a server that is pretending to be “%@” which could put your confidential information at risk.";
+ NSString *descriptionFormat = NSLocalizedStringWithDefaultValue(@"Err-1202.w", nil, CFNetworkBundle, defaultValue, @"") ?: defaultValue;
+ NSString *localizedDescription = [descriptionFormat componentsSeparatedByString:@"%@"].count <= 2 ? [NSString localizedStringWithFormat:descriptionFormat, url.host] : descriptionFormat;
+ NSMutableDictionary *userInfo = [@{
+ NSLocalizedDescriptionKey: localizedDescription
+ } mutableCopy];
+
+ if (serverTrust) {
+ userInfo[NSURLErrorFailingURLPeerTrustErrorKey] = (__bridge id)serverTrust;
+ }
+
+ if (url) {
+ userInfo[NSURLErrorFailingURLErrorKey] = url;
+
+ if (url.absoluteString) {
+ userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString;
+ }
+ }
+
+ return [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorServerCertificateUntrusted userInfo:userInfo];
+}
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+ needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
+{
+ NSInputStream *inputStream = nil;
+
+ if (self.taskNeedNewBodyStream) {
+ inputStream = self.taskNeedNewBodyStream(session, task);
+ } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
+ inputStream = [task.originalRequest.HTTPBodyStream copy];
+ }
+
+ if (completionHandler) {
+ completionHandler(inputStream);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+ didSendBodyData:(int64_t)bytesSent
+ totalBytesSent:(int64_t)totalBytesSent
+totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
+{
+
+ int64_t totalUnitCount = totalBytesExpectedToSend;
+ if (totalUnitCount == NSURLSessionTransferSizeUnknown) {
+ NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
+ if (contentLength) {
+ totalUnitCount = (int64_t) [contentLength longLongValue];
+ }
+ }
+
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
+
+ if (delegate) {
+ [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
+ }
+
+ if (self.taskDidSendBodyData) {
+ self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didCompleteWithError:(NSError *)error
+{
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
+
+ // delegate may be nil when completing a task in the background
+ if (delegate) {
+ [delegate URLSession:session task:task didCompleteWithError:error];
+
+ [self removeDelegateForTask:task];
+ }
+
+ if (self.taskDidComplete) {
+ self.taskDidComplete(session, task, error);
+ }
+}
+
+#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10))
+{
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
+ // Metrics may fire after URLSession:task:didCompleteWithError: is called, delegate may be nil
+ if (delegate) {
+ [delegate URLSession:session task:task didFinishCollectingMetrics:metrics];
+ }
+
+ if (self.taskDidFinishCollectingMetrics) {
+ self.taskDidFinishCollectingMetrics(session, task, metrics);
+ }
+}
+#endif
+
+#pragma mark - NSURLSessionDataDelegate
+
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+didReceiveResponse:(NSURLResponse *)response
+ completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
+{
+ NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
+
+ if (self.dataTaskDidReceiveResponse) {
+ disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
+ }
+
+ if (completionHandler) {
+ completionHandler(disposition);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
+{
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
+ if (delegate) {
+ [self removeDelegateForTask:dataTask];
+ [self setDelegate:delegate forTask:downloadTask];
+ }
+
+ if (self.dataTaskDidBecomeDownloadTask) {
+ self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+ didReceiveData:(NSData *)data
+{
+
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
+ [delegate URLSession:session dataTask:dataTask didReceiveData:data];
+
+ if (self.dataTaskDidReceiveData) {
+ self.dataTaskDidReceiveData(session, dataTask, data);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+ willCacheResponse:(NSCachedURLResponse *)proposedResponse
+ completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
+{
+ NSCachedURLResponse *cachedResponse = proposedResponse;
+
+ if (self.dataTaskWillCacheResponse) {
+ cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
+ }
+
+ if (completionHandler) {
+ completionHandler(cachedResponse);
+ }
+}
+
+#if !TARGET_OS_OSX
+- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
+ if (self.didFinishEventsForBackgroundURLSession) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.didFinishEventsForBackgroundURLSession(session);
+ });
+ }
+}
+#endif
+
+#pragma mark - NSURLSessionDownloadDelegate
+
+- (void)URLSession:(NSURLSession *)session
+ downloadTask:(NSURLSessionDownloadTask *)downloadTask
+didFinishDownloadingToURL:(NSURL *)location
+{
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
+ if (self.downloadTaskDidFinishDownloading) {
+ NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
+ if (fileURL) {
+ delegate.downloadFileURL = fileURL;
+ NSError *error = nil;
+
+ if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
+ } else {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
+ }
+
+ return;
+ }
+ }
+
+ if (delegate) {
+ [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ downloadTask:(NSURLSessionDownloadTask *)downloadTask
+ didWriteData:(int64_t)bytesWritten
+ totalBytesWritten:(int64_t)totalBytesWritten
+totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
+{
+
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
+
+ if (delegate) {
+ [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
+ }
+
+ if (self.downloadTaskDidWriteData) {
+ self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ downloadTask:(NSURLSessionDownloadTask *)downloadTask
+ didResumeAtOffset:(int64_t)fileOffset
+expectedTotalBytes:(int64_t)expectedTotalBytes
+{
+
+ AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
+
+ if (delegate) {
+ [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
+ }
+
+ if (self.downloadTaskDidResume) {
+ self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
+ }
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
+
+ self = [self initWithSessionConfiguration:configuration];
+ if (!self) {
+ return nil;
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
+}
+
+#pragma mark - NSCopying
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+ return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
+}
+
+@end
--- /dev/null
+//
+// AEAD.swift
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+//
+
+// https://www.iana.org/assignments/aead-parameters/aead-parameters.xhtml
+
+/// Authenticated Encryption with Associated Data (AEAD)
+public protocol AEAD {
+ static var kLen: Int { get } // key length
+ static var ivRange: Range<Int> { get } // nonce length
+}
+
+extension AEAD {
+ static func calculateAuthenticationTag(authenticator: Authenticator, cipherText: Array<UInt8>, authenticationHeader: Array<UInt8>) throws -> Array<UInt8> {
+ let headerPadding = ((16 - (authenticationHeader.count & 0xf)) & 0xf)
+ let cipherPadding = ((16 - (cipherText.count & 0xf)) & 0xf)
+
+ var mac = authenticationHeader
+ mac += Array<UInt8>(repeating: 0, count: headerPadding)
+ mac += cipherText
+ mac += Array<UInt8>(repeating: 0, count: cipherPadding)
+ mac += UInt64(bigEndian: UInt64(authenticationHeader.count)).bytes()
+ mac += UInt64(bigEndian: UInt64(cipherText.count)).bytes()
+
+ return try authenticator.authenticate(mac)
+ }
+}
--- /dev/null
+//
+// ChaCha20Poly1305.swift
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+//
+// https://tools.ietf.org/html/rfc7539#section-2.8.1
+
+/// AEAD_CHACHA20_POLY1305
+public final class AEADChaCha20Poly1305: AEAD {
+ public static let kLen = 32 // key length
+ public static var ivRange = Range<Int>(12...12)
+
+ /// Authenticated encryption
+ public static func encrypt(_ plainText: Array<UInt8>, key: Array<UInt8>, iv: Array<UInt8>, authenticationHeader: Array<UInt8>) throws -> (cipherText: Array<UInt8>, authenticationTag: Array<UInt8>) {
+ let cipher = try ChaCha20(key: key, iv: iv)
+
+ var polykey = Array<UInt8>(repeating: 0, count: kLen)
+ var toEncrypt = polykey
+ polykey = try cipher.encrypt(polykey)
+ toEncrypt += polykey
+ toEncrypt += plainText
+
+ let fullCipherText = try cipher.encrypt(toEncrypt)
+ let cipherText = Array(fullCipherText.dropFirst(64))
+
+ let tag = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader)
+ return (cipherText, tag)
+ }
+
+ /// Authenticated decryption
+ public static func decrypt(_ cipherText: Array<UInt8>, key: Array<UInt8>, iv: Array<UInt8>, authenticationHeader: Array<UInt8>, authenticationTag: Array<UInt8>) throws -> (plainText: Array<UInt8>, success: Bool) {
+ let chacha = try ChaCha20(key: key, iv: iv)
+
+ let polykey = try chacha.encrypt(Array<UInt8>(repeating: 0, count: self.kLen))
+ let mac = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader)
+ guard mac == authenticationTag else {
+ return (cipherText, false)
+ }
+
+ var toDecrypt = Array<UInt8>(reserveCapacity: cipherText.count + 64)
+ toDecrypt += polykey
+ toDecrypt += polykey
+ toDecrypt += cipherText
+ let fullPlainText = try chacha.decrypt(toDecrypt)
+ let plainText = Array(fullPlainText.dropFirst(64))
+ return (plainText, true)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension AES {
+ /// Initialize with CBC block mode.
+ ///
+ /// - Parameters:
+ /// - key: CookiesKey as a String.
+ /// - iv: IV as a String.
+ /// - padding: Padding
+ /// - Throws: Error
+ ///
+ /// The input is a String, that is treat as sequence of bytes made directly out of String.
+ /// If input is Base64 encoded data (which is a String technically) it is not decoded automatically for you.
+ public convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws {
+ try self.init(key: key.bytes, blockMode: CBC(iv: iv.bytes), padding: padding)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// MARK: Cryptors
+
+extension AES: Cryptors {
+ @inlinable
+ public func makeEncryptor() throws -> Cryptor & Updatable {
+ let blockSize = blockMode.customBlockSize ?? AES.blockSize
+ let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: encrypt, encryptionOperation: encrypt)
+ if worker is StreamModeWorker {
+ return try StreamEncryptor(blockSize: blockSize, padding: padding, worker)
+ }
+ return try BlockEncryptor(blockSize: blockSize, padding: padding, worker)
+ }
+
+ @inlinable
+ public func makeDecryptor() throws -> Cryptor & Updatable {
+ let blockSize = blockMode.customBlockSize ?? AES.blockSize
+ let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt
+ let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: cipherOperation, encryptionOperation: encrypt)
+ if worker is StreamModeWorker {
+ return try StreamDecryptor(blockSize: blockSize, padding: padding, worker)
+ }
+ return try BlockDecryptor(blockSize: blockSize, padding: padding, worker)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Implementation of Gladman algorithm http://www.gladman.me.uk/AES
+//
+
+/// The Advanced Encryption Standard (AES)
+public final class AES: BlockCipher {
+ public enum Error: Swift.Error {
+ /// Invalid key
+ case invalidKeySize
+ /// Data padding is required
+ case dataPaddingRequired
+ /// Invalid Data
+ case invalidData
+ }
+
+ public enum Variant: Int {
+ case aes128 = 1, aes192, aes256
+
+ var Nk: Int { // Nk words
+ [4, 6, 8][self.rawValue - 1]
+ }
+
+ var Nb: Int { // Nb words
+ 4
+ }
+
+ var Nr: Int { // Nr
+ self.Nk + 6
+ }
+ }
+
+ @usableFromInline
+ internal let variantNr: Int
+
+ @usableFromInline
+ internal let variantNb: Int
+
+ @usableFromInline
+ internal let variantNk: Int
+
+ public static let blockSize: Int = 16 // 128 /8
+ public let keySize: Int
+
+ /// AES Variant
+ public let variant: Variant
+
+ // Parameters
+ let key: CookiesKey
+
+ @usableFromInline
+ let blockMode: BlockMode
+
+ @usableFromInline
+ let padding: Padding
+
+ //
+ @usableFromInline
+ internal lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
+
+ @usableFromInline
+ internal lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
+
+ private lazy var sBoxes: (sBox: Array<UInt32>, invSBox: Array<UInt32>) = self.calculateSBox()
+ private lazy var sBox: Array<UInt32> = self.sBoxes.sBox
+ private lazy var sBoxInv: Array<UInt32> = self.sBoxes.invSBox
+
+ // Parameters for Linear Congruence Generators
+ private static let Rcon: Array<UInt8> = [
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
+ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
+ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
+ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
+ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
+ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
+ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
+ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
+ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
+ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d
+ ]
+
+ @usableFromInline static let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
+ @usableFromInline static let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
+ @usableFromInline static let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
+ @usableFromInline static let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
+ @usableFromInline static let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
+ @usableFromInline static let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
+ @usableFromInline static let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
+ @usableFromInline static let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
+ @usableFromInline static let U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
+ @usableFromInline static let U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
+ @usableFromInline static let U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
+ @usableFromInline static let U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
+
+ /// Initialize AES with variant calculated out of key length:
+ /// - 16 bytes (AES-128)
+ /// - 24 bytes (AES-192)
+ /// - 32 bytes (AES-256)
+ ///
+ /// - parameter key: CookiesKey. Length of the key decides on AES variant.
+ /// - parameter iv: Initialization Vector (Optional for some blockMode values)
+ /// - parameter blockMode: Cipher mode of operation
+ /// - parameter padding: Padding method. .pkcs7, .noPadding, .zeroPadding, ...
+ ///
+ /// - throws: AES.Error
+ ///
+ /// - returns: Instance
+ public init(key: Array<UInt8>, blockMode: BlockMode, padding: Padding = .pkcs7) throws {
+ self.key = CookiesKey(bytes: key)
+ self.blockMode = blockMode
+ self.padding = padding
+ self.keySize = self.key.count
+
+ // Validate key size
+ switch self.keySize * 8 {
+ case 128:
+ self.variant = .aes128
+ case 192:
+ self.variant = .aes192
+ case 256:
+ self.variant = .aes256
+ default:
+ throw Error.invalidKeySize
+ }
+
+ self.variantNb = self.variant.Nb
+ self.variantNk = self.variant.Nk
+ self.variantNr = self.variant.Nr
+ }
+
+ @inlinable
+ internal func encrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
+ if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize {
+ return Array(block)
+ }
+
+ let rounds = self.variantNr
+ let rk = self.expandedKey
+
+ let b00 = UInt32(block[block.startIndex.advanced(by: 0)])
+ let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8
+ let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16
+ let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24
+ var b0 = b00 | b01 | b02 | b03
+
+ let b10 = UInt32(block[block.startIndex.advanced(by: 4)])
+ let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8
+ let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16
+ let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24
+ var b1 = b10 | b11 | b12 | b13
+
+ let b20 = UInt32(block[block.startIndex.advanced(by: 8)])
+ let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8
+ let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16
+ let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24
+ var b2 = b20 | b21 | b22 | b23
+
+ let b30 = UInt32(block[block.startIndex.advanced(by: 12)])
+ let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8
+ let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16
+ let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24
+ var b3 = b30 | b31 | b32 | b33
+
+ let tLength = 4
+ let t = UnsafeMutablePointer<UInt32>.allocate(capacity: tLength)
+ t.initialize(repeating: 0, count: tLength)
+ defer {
+ t.deinitialize(count: tLength)
+ t.deallocate()
+ }
+
+ for r in 0..<rounds - 1 {
+ t[0] = b0 ^ rk[r][0]
+ t[1] = b1 ^ rk[r][1]
+ t[2] = b2 ^ rk[r][2]
+ t[3] = b3 ^ rk[r][3]
+
+ let lb00 = AES.T0[Int(t[0] & 0xff)]
+ let lb01 = AES.T1[Int((t[1] >> 8) & 0xff)]
+ let lb02 = AES.T2[Int((t[2] >> 16) & 0xff)]
+ let lb03 = AES.T3[Int(t[3] >> 24)]
+ b0 = lb00 ^ lb01 ^ lb02 ^ lb03
+
+ let lb10 = AES.T0[Int(t[1] & 0xff)]
+ let lb11 = AES.T1[Int((t[2] >> 8) & 0xff)]
+ let lb12 = AES.T2[Int((t[3] >> 16) & 0xff)]
+ let lb13 = AES.T3[Int(t[0] >> 24)]
+ b1 = lb10 ^ lb11 ^ lb12 ^ lb13
+
+ let lb20 = AES.T0[Int(t[2] & 0xff)]
+ let lb21 = AES.T1[Int((t[3] >> 8) & 0xff)]
+ let lb22 = AES.T2[Int((t[0] >> 16) & 0xff)]
+ let lb23 = AES.T3[Int(t[1] >> 24)]
+ b2 = lb20 ^ lb21 ^ lb22 ^ lb23
+
+ let lb30 = AES.T0[Int(t[3] & 0xff)]
+ let lb31 = AES.T1[Int((t[0] >> 8) & 0xff)]
+ let lb32 = AES.T2[Int((t[1] >> 16) & 0xff)]
+ let lb33 = AES.T3[Int(t[2] >> 24)]
+ b3 = lb30 ^ lb31 ^ lb32 ^ lb33
+ }
+
+ // last round
+ let r = rounds - 1
+
+ t[0] = b0 ^ rk[r][0]
+ t[1] = b1 ^ rk[r][1]
+ t[2] = b2 ^ rk[r][2]
+ t[3] = b3 ^ rk[r][3]
+
+ // rounds
+ b0 = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
+ b1 = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
+ b2 = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
+ b3 = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
+
+ let encrypted: Array<UInt8> = [
+ UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff),
+ UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff),
+ UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff),
+ UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff)
+ ]
+ return encrypted
+ }
+
+ @usableFromInline
+ internal func decrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
+ if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize {
+ return Array(block)
+ }
+
+ let rounds = self.variantNr
+ let rk = self.expandedKeyInv
+
+ // Save miliseconds by not using `block.toUInt32Array()`
+ let b00 = UInt32(block[block.startIndex.advanced(by: 0)])
+ let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8
+ let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16
+ let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24
+ var b0 = b00 | b01 | b02 | b03
+
+ let b10 = UInt32(block[block.startIndex.advanced(by: 4)])
+ let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8
+ let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16
+ let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24
+ var b1 = b10 | b11 | b12 | b13
+
+ let b20 = UInt32(block[block.startIndex.advanced(by: 8)])
+ let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8
+ let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16
+ let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24
+ var b2 = b20 | b21 | b22 | b23
+
+ let b30 = UInt32(block[block.startIndex.advanced(by: 12)])
+ let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8
+ let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16
+ let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24
+ var b3 = b30 | b31 | b32 | b33
+
+ let tLength = 4
+ let t = UnsafeMutablePointer<UInt32>.allocate(capacity: tLength)
+ t.initialize(repeating: 0, count: tLength)
+ defer {
+ t.deinitialize(count: tLength)
+ t.deallocate()
+ }
+
+ for r in (2...rounds).reversed() {
+ t[0] = b0 ^ rk[r][0]
+ t[1] = b1 ^ rk[r][1]
+ t[2] = b2 ^ rk[r][2]
+ t[3] = b3 ^ rk[r][3]
+
+ let b00 = AES.T0_INV[Int(t[0] & 0xff)]
+ let b01 = AES.T1_INV[Int((t[3] >> 8) & 0xff)]
+ let b02 = AES.T2_INV[Int((t[2] >> 16) & 0xff)]
+ let b03 = AES.T3_INV[Int(t[1] >> 24)]
+ b0 = b00 ^ b01 ^ b02 ^ b03
+
+ let b10 = AES.T0_INV[Int(t[1] & 0xff)]
+ let b11 = AES.T1_INV[Int((t[0] >> 8) & 0xff)]
+ let b12 = AES.T2_INV[Int((t[3] >> 16) & 0xff)]
+ let b13 = AES.T3_INV[Int(t[2] >> 24)]
+ b1 = b10 ^ b11 ^ b12 ^ b13
+
+ let b20 = AES.T0_INV[Int(t[2] & 0xff)]
+ let b21 = AES.T1_INV[Int((t[1] >> 8) & 0xff)]
+ let b22 = AES.T2_INV[Int((t[0] >> 16) & 0xff)]
+ let b23 = AES.T3_INV[Int(t[3] >> 24)]
+ b2 = b20 ^ b21 ^ b22 ^ b23
+
+ let b30 = AES.T0_INV[Int(t[3] & 0xff)]
+ let b31 = AES.T1_INV[Int((t[2] >> 8) & 0xff)]
+ let b32 = AES.T2_INV[Int((t[1] >> 16) & 0xff)]
+ let b33 = AES.T3_INV[Int(t[0] >> 24)]
+ b3 = b30 ^ b31 ^ b32 ^ b33
+ }
+
+ // last round
+ t[0] = b0 ^ rk[1][0]
+ t[1] = b1 ^ rk[1][1]
+ t[2] = b2 ^ rk[1][2]
+ t[3] = b3 ^ rk[1][3]
+
+ // rounds
+
+ let lb00 = self.sBoxInv[Int(B0(t[0]))]
+ let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
+ let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
+ let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
+ b0 = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
+
+ let lb10 = self.sBoxInv[Int(B0(t[1]))]
+ let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
+ let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
+ let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
+ b1 = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
+
+ let lb20 = self.sBoxInv[Int(B0(t[2]))]
+ let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
+ let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
+ let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
+ b2 = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
+
+ let lb30 = self.sBoxInv[Int(B0(t[3]))]
+ let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
+ let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
+ let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
+ b3 = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
+
+ let result: Array<UInt8> = [
+ UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff),
+ UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff),
+ UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff),
+ UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff)
+ ]
+ return result
+ }
+}
+
+extension AES {
+ private func expandKeyInv(_ key: CookiesKey, variant: Variant) -> Array<Array<UInt32>> {
+ let rounds = self.variantNr
+ var rk2: Array<Array<UInt32>> = self.expandKey(key, variant: variant)
+
+ for r in 1..<rounds {
+ for i in 0..<4 {
+ let w = rk2[r][i]
+ let u1 = AES.U1[Int(B0(w))]
+ let u2 = AES.U2[Int(B1(w))]
+ let u3 = AES.U3[Int(B2(w))]
+ let u4 = AES.U4[Int(B3(w))]
+ rk2[r][i] = u1 ^ u2 ^ u3 ^ u4
+ }
+ }
+
+ return rk2
+ }
+
+ private func expandKey(_ key: CookiesKey, variant _: Variant) -> Array<Array<UInt32>> {
+ func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
+ expanded.batched(by: 4).map({ UInt32(bytes: $0.reversed()) }).batched(by: 4).map { Array($0) }
+ }
+
+ /*
+ * Function used in the CookiesKey Expansion routine that takes a four-byte
+ * input word and applies an S-box to each of the four bytes to
+ * produce an output word.
+ */
+ func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
+ precondition(word.count == 4)
+
+ var result = word
+ for i in 0..<4 {
+ result[i] = UInt8(self.sBox[Int(word[i])])
+ }
+ return result
+ }
+
+ @inline(__always)
+ func subWordInPlace(_ word: inout Array<UInt8>) {
+ precondition(word.count == 4)
+ word[0] = UInt8(self.sBox[Int(word[0])])
+ word[1] = UInt8(self.sBox[Int(word[1])])
+ word[2] = UInt8(self.sBox[Int(word[2])])
+ word[3] = UInt8(self.sBox[Int(word[3])])
+ }
+
+ let wLength = self.variantNb * (self.variantNr + 1) * 4
+ let w = UnsafeMutablePointer<UInt8>.allocate(capacity: wLength)
+ w.initialize(repeating: 0, count: wLength)
+ defer {
+ w.deinitialize(count: wLength)
+ w.deallocate()
+ }
+
+ for i in 0..<self.variantNk {
+ for wordIdx in 0..<4 {
+ w[(4 * i) + wordIdx] = key[(4 * i) + wordIdx]
+ }
+ }
+
+ var tmp: Array<UInt8>
+
+ for i in self.variantNk..<self.variantNb * (self.variantNr + 1) {
+ tmp = Array<UInt8>(repeating: 0, count: 4)
+
+ for wordIdx in 0..<4 {
+ tmp[wordIdx] = w[4 * (i - 1) + wordIdx]
+ }
+ if (i % self.variantNk) == 0 {
+ tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: 4))
+ tmp[0] = tmp.first! ^ AES.Rcon[i / variantNk]
+ } else if self.variantNk > 6 && (i % self.variantNk) == 4 {
+ subWordInPlace(&tmp)
+ }
+
+ // xor array of bytes
+ for wordIdx in 0..<4 {
+ w[4 * i + wordIdx] = w[4 * (i - variantNk) + wordIdx] ^ tmp[wordIdx]
+ }
+ }
+ return convertExpandedKey(Array(UnsafeBufferPointer(start: w, count: wLength)))
+ }
+
+ @inline(__always)
+ private func B0(_ x: UInt32) -> UInt32 {
+ x & 0xff
+ }
+
+ @inline(__always)
+ private func B1(_ x: UInt32) -> UInt32 {
+ (x >> 8) & 0xff
+ }
+
+ @inline(__always)
+ private func B2(_ x: UInt32) -> UInt32 {
+ (x >> 16) & 0xff
+ }
+
+ @inline(__always)
+ private func B3(_ x: UInt32) -> UInt32 {
+ (x >> 24) & 0xff
+ }
+
+ @inline(__always) @usableFromInline
+ internal func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
+ var result: UInt32 = 0
+ result |= UInt32(self.B1(AES.T0[Int(x0 & 255)]))
+ result |= UInt32(self.B1(AES.T0[Int((x1 >> 8) & 255)])) << 8
+ result |= UInt32(self.B1(AES.T0[Int((x2 >> 16) & 255)])) << 16
+ result |= UInt32(self.B1(AES.T0[Int(x3 >> 24)])) << 24
+ return result
+ }
+
+ private func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
+ let sboxLength = 256
+ let sbox = UnsafeMutablePointer<UInt32>.allocate(capacity: sboxLength)
+ let invsbox = UnsafeMutablePointer<UInt32>.allocate(capacity: sboxLength)
+ sbox.initialize(repeating: 0, count: sboxLength)
+ invsbox.initialize(repeating: 0, count: sboxLength)
+ defer {
+ sbox.deinitialize(count: sboxLength)
+ sbox.deallocate()
+ invsbox.deinitialize(count: sboxLength)
+ invsbox.deallocate()
+ }
+
+ sbox[0] = 0x63
+
+ var p: UInt8 = 1, q: UInt8 = 1
+
+ repeat {
+ p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1b : 0))
+ q ^= q << 1
+ q ^= q << 2
+ q ^= q << 4
+ q ^= (q & 0x80) == 0x80 ? 0x09 : 0
+
+ let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
+
+ sbox[Int(p)] = UInt32(s)
+ invsbox[Int(s)] = UInt32(p)
+ } while p != 1
+
+ return (sBox: Array(UnsafeBufferPointer(start: sbox, count: sboxLength)), invSBox: Array(UnsafeBufferPointer(start: invsbox, count: sboxLength)))
+ }
+}
+
+// MARK: Cipher
+
+extension AES: Cipher {
+ @inlinable
+ public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ let blockSize = self.blockMode.customBlockSize ?? AES.blockSize
+ let chunks = bytes.batched(by: blockSize)
+
+ var oneTimeCryptor = try makeEncryptor()
+ var out = Array<UInt8>(reserveCapacity: bytes.count)
+ for chunk in chunks {
+ out += try oneTimeCryptor.update(withBytes: chunk, isLast: false)
+ }
+ // Padding may be added at the very end
+ out += try oneTimeCryptor.finish()
+
+ if self.blockMode.options.contains(.paddingRequired) && (out.count % AES.blockSize != 0) {
+ throw Error.dataPaddingRequired
+ }
+
+ return out
+ }
+
+ @inlinable
+ public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ if self.blockMode.options.contains(.paddingRequired) && (bytes.count % AES.blockSize != 0) {
+ throw Error.dataPaddingRequired
+ }
+
+ var oneTimeCryptor = try makeDecryptor()
+ let chunks = bytes.batched(by: AES.blockSize)
+ if chunks.isEmpty {
+ throw Error.invalidData
+ }
+
+ var out = Array<UInt8>(reserveCapacity: bytes.count)
+
+ var lastIdx = chunks.startIndex
+ chunks.indices.formIndex(&lastIdx, offsetBy: chunks.count - 1)
+
+ // To properly remove padding, `isLast` has to be known when called with the last chunk of ciphertext
+ // Last chunk of ciphertext may contains padded data so next call to update(..) won't be able to remove it
+ for idx in chunks.indices {
+ out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == lastIdx)
+ }
+ return out
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA
+
+import Foundation
+
+/// A Partial ASN.1 (Abstract Syntax Notation 1) Encoder & Decoder Implementation.
+///
+/// - Note: This implementation is limited to a few core types and is not an exhaustive / complete ASN1 implementation
+/// - Warning: This implementation has been developed for encoding and decoding DER & PEM files specifically. If you're using this Encoder/Decoder on other ASN1 structures, make sure you test the expected behavior appropriately.
+enum ASN1 {
+ internal enum IDENTIFIERS: UInt8, Equatable {
+ case SEQUENCE = 0x30
+ case INTERGER = 0x02
+ case OBJECTID = 0x06
+ case NULL = 0x05
+ case BITSTRING = 0x03
+ case OCTETSTRING = 0x04
+
+ static func == (lhs: UInt8, rhs: IDENTIFIERS) -> Bool {
+ lhs == rhs.rawValue
+ }
+
+ var bytes: [UInt8] {
+ switch self {
+ case .NULL:
+ return [self.rawValue, 0x00]
+ default:
+ return [self.rawValue]
+ }
+ }
+ }
+
+ /// An ASN1 node
+ internal enum Node: CustomStringConvertible {
+ /// An array of more `ASN1.Node`s
+ case sequence(nodes: [Node])
+ /// An integer
+ /// - Note: This ASN1 Encoder makes no assumptions about the sign and bit order of the integers passed in. The conversion from Integer to Data is your responsiblity.
+ case integer(data: Data)
+ /// An objectIdentifier
+ case objectIdentifier(data: Data)
+ /// A null object
+ case null
+ /// A bitString
+ case bitString(data: Data)
+ /// An octetString
+ case octetString(data: Data)
+
+ var description: String {
+ ASN1.printNode(self, level: 0)
+ }
+ }
+
+ internal static func printNode(_ node: ASN1.Node, level: Int) -> String {
+ var str: [String] = []
+ let prefix = String(repeating: "\t", count: level)
+ switch node {
+ case .integer(let int):
+ str.append("\(prefix)Integer: \(int.toHexString())")
+ case .bitString(let bs):
+ str.append("\(prefix)BitString: \(bs.toHexString())")
+ case .null:
+ str.append("\(prefix)NULL")
+ case .objectIdentifier(let oid):
+ str.append("\(prefix)ObjectID: \(oid.toHexString())")
+ case .octetString(let os):
+ str.append("\(prefix)OctetString: \(os.toHexString())")
+ case .sequence(let nodes):
+ str.append("\(prefix)Sequence:")
+ nodes.forEach { str.append(printNode($0, level: level + 1)) }
+ }
+ return str.joined(separator: "\n")
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA
+
+import Foundation
+
+extension ASN1 {
+ /// A simple ASN1 parser that will recursively iterate over a root node and return a Node tree.
+ /// The root node can be any of the supported nodes described in `Node`. If the parser encounters a sequence
+ /// it will recursively parse its children.
+ enum Decoder {
+
+ enum DecodingError: Error {
+ case noType
+ case invalidType(value: UInt8)
+ }
+
+ /// Parses ASN1 data and returns its root node.
+ ///
+ /// - Parameter data: ASN1 data to parse
+ /// - Returns: Root ASN1 Node
+ /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered
+ static func decode(data: Data) throws -> Node {
+ let scanner = ASN1.Scanner(data: data)
+ let node = try decodeNode(scanner: scanner)
+ return node
+ }
+
+ /// Parses an ASN1 given an existing scanner.
+ /// @warning: this will modify the state (ie: position) of the provided scanner.
+ ///
+ /// - Parameter scanner: Scanner to use to consume the data
+ /// - Returns: Parsed node
+ /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered
+ private static func decodeNode(scanner: ASN1.Scanner) throws -> Node {
+
+ let firstByte = try scanner.consume(length: 1).firstByte
+
+ switch firstByte {
+ case IDENTIFIERS.SEQUENCE.rawValue:
+ let length = try scanner.consumeLength()
+ let data = try scanner.consume(length: length)
+ let nodes = try decodeSequence(data: data)
+ return .sequence(nodes: nodes)
+
+ case IDENTIFIERS.INTERGER.rawValue:
+ let length = try scanner.consumeLength()
+ let data = try scanner.consume(length: length)
+ return .integer(data: data)
+
+ case IDENTIFIERS.OBJECTID.rawValue:
+ let length = try scanner.consumeLength()
+ let data = try scanner.consume(length: length)
+ return .objectIdentifier(data: data)
+
+ case IDENTIFIERS.NULL.rawValue:
+ _ = try scanner.consume(length: 1)
+ return .null
+
+ case IDENTIFIERS.BITSTRING.rawValue:
+ let length = try scanner.consumeLength()
+
+ // There's an extra byte (0x00) after the bit string length in all the keys I've encountered.
+ // I couldn't find a specification that referenced this extra byte, but let's consume it and discard it.
+ _ = try scanner.consume(length: 1)
+
+ let data = try scanner.consume(length: length - 1)
+ return .bitString(data: data)
+
+ case IDENTIFIERS.OCTETSTRING.rawValue:
+ let length = try scanner.consumeLength()
+ let data = try scanner.consume(length: length)
+ return .octetString(data: data)
+
+ default:
+ throw DecodingError.invalidType(value: firstByte)
+ }
+ }
+
+ /// Parses an ASN1 sequence and returns its child nodes
+ ///
+ /// - Parameter data: ASN1 data
+ /// - Returns: A list of ASN1 nodes
+ /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered
+ private static func decodeSequence(data: Data) throws -> [Node] {
+ let scanner = ASN1.Scanner(data: data)
+ var nodes: [Node] = []
+ while !scanner.isComplete {
+ let node = try decodeNode(scanner: scanner)
+ nodes.append(node)
+ }
+ return nodes
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA
+
+import Foundation
+
+extension ASN1 {
+ enum Encoder {
+ /// Encodes an ASN1Node into it's byte representation
+ ///
+ /// - Parameter node: The Node to encode
+ /// - Returns: The encoded bytes as a UInt8 array
+ ///
+ /// - Warning: This ASN.1 encoder has only been tested to work on certain ASN.1 data structures such as DER and PEM files. Before using this encoder for another application, ensure you test it's behavior accordingly.
+ /// - Warning: This encoder makes no assumptions regarding Integer bit layout and signage. The proper serialization of Integers is left up to the user.
+ public static func encode(_ node: ASN1.Node) -> [UInt8] {
+ switch node {
+ case .integer(let integer):
+ return IDENTIFIERS.INTERGER.bytes + self.asn1LengthPrefixed(integer.bytes)
+ case .bitString(let bits):
+ return IDENTIFIERS.BITSTRING.bytes + self.asn1LengthPrefixed([0x00] + bits.bytes)
+ case .octetString(let octet):
+ return IDENTIFIERS.OCTETSTRING.bytes + self.asn1LengthPrefixed(octet.bytes)
+ case .null:
+ return IDENTIFIERS.NULL.bytes
+ case .objectIdentifier(let oid):
+ return IDENTIFIERS.OBJECTID.bytes + self.asn1LengthPrefixed(oid.bytes)
+ case .sequence(let nodes):
+ return IDENTIFIERS.SEQUENCE.bytes + self.asn1LengthPrefixed( nodes.reduce(into: Array<UInt8>(), { partialResult, node in
+ partialResult += encode(node)
+ }))
+ }
+ }
+
+ /// Calculates and returns the ASN.1 length Prefix for a chunk of data
+ ///
+ /// - Parameter bytes: The bytes to be length prefixed
+ /// - Returns: The ASN.1 length Prefix for this chuck of data (excluding the passed in data)
+ private static func asn1LengthPrefix(_ bytes: [UInt8]) -> [UInt8] {
+ if bytes.count >= 0x80 {
+ var lengthAsBytes = withUnsafeBytes(of: bytes.count.bigEndian, Array<UInt8>.init)
+ while lengthAsBytes.first == 0 { lengthAsBytes.removeFirst() }
+ return [0x80 + UInt8(lengthAsBytes.count)] + lengthAsBytes
+ } else {
+ return [UInt8(bytes.count)]
+ }
+ }
+
+ /// Prefixes the provided bytes with the appropriate ASN.1 length prefix
+ ///
+ /// - Parameter bytes: The bytes to be length prefixed
+ /// - Returns: The provided bytes with the appropriate ASN.1 length prefix prepended
+ private static func asn1LengthPrefixed(_ bytes: [UInt8]) -> [UInt8] {
+ self.asn1LengthPrefix(bytes) + bytes
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+// ASN1 Scanner code is from Asn1Parser.swift from SwiftyRSA
+
+import Foundation
+
+extension ASN1 {
+ /// Simple data scanner that consumes bytes from a raw data and keeps an updated position.
+ internal class Scanner {
+
+ enum ScannerError: Error {
+ case outOfBounds
+ }
+
+ let data: Data
+ var index: Int = 0
+
+ /// Returns whether there is no more data to consume
+ var isComplete: Bool {
+ return self.index >= self.data.count
+ }
+
+ /// Creates a scanner with provided data
+ ///
+ /// - Parameter data: Data to consume
+ init(data: Data) {
+ self.data = data
+ }
+
+ /// Consumes data of provided length and returns it
+ ///
+ /// - Parameter length: length of the data to consume
+ /// - Returns: data consumed
+ /// - Throws: ScannerError.outOfBounds error if asked to consume too many bytes
+ func consume(length: Int) throws -> Data {
+
+ guard length > 0 else {
+ return Data()
+ }
+
+ guard self.index + length <= self.data.count else {
+ throw ScannerError.outOfBounds
+ }
+
+ let subdata = self.data.subdata(in: self.index..<self.index + length)
+ self.index += length
+ return subdata
+ }
+
+ /// Consumes a primitive, definite ASN1 length and returns its value.
+ ///
+ /// See http://luca.ntop.org/Teaching/Appunti/asn1.html,
+ ///
+ /// - Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
+ /// - Long form. Two to 127 octets. Bit 8 of first octet has value "1" and
+ /// bits 7-1 give the number of additional length octets.
+ /// Second and following octets give the length, base 256, most significant digit first.
+ ///
+ /// - Returns: Length that was consumed
+ /// - Throws: ScannerError.outOfBounds error if asked to consume too many bytes
+ func consumeLength() throws -> Int {
+
+ let lengthByte = try consume(length: 1).firstByte
+
+ // If the first byte's value is less than 0x80, it directly contains the length
+ // so we can return it
+ guard lengthByte >= 0x80 else {
+ return Int(lengthByte)
+ }
+
+ // If the first byte's value is more than 0x80, it indicates how many following bytes
+ // will describe the length. For instance, 0x85 indicates that 0x85 - 0x80 = 0x05 = 5
+ // bytes will describe the length, so we need to read the 5 next bytes and get their integer
+ // value to determine the length.
+ let nextByteCount = lengthByte - 0x80
+ let length = try consume(length: Int(nextByteCount))
+
+ return length.integer
+ }
+ }
+}
+
+internal extension Data {
+
+ /// Returns the first byte of the current data
+ var firstByte: UInt8 {
+ var byte: UInt8 = 0
+ copyBytes(to: &byte, count: MemoryLayout<UInt8>.size)
+ return byte
+ }
+
+ /// Returns the integer value of the current data.
+ /// - Warning: This only supports data up to 4 bytes, as we can only extract 32-bit integers.
+ var integer: Int {
+
+ guard count > 0 else {
+ return 0
+ }
+
+ var int: UInt32 = 0
+ var offset: Int32 = Int32(count - 1)
+ forEach { byte in
+ let byte32 = UInt32(byte)
+ let shifted = byte32 << (UInt32(offset) * 8)
+ int = int | shifted
+ offset -= 1
+ }
+
+ return Int(int)
+ }
+}
--- /dev/null
+//
+// Addition.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ //MARK: Addition
+
+ /// Add `word` to this integer in place.
+ /// `word` is shifted `shift` words to the left before being added.
+ ///
+ /// - Complexity: O(max(count, shift))
+ internal mutating func addWord(_ word: Word, shiftedBy shift: Int = 0) {
+ precondition(shift >= 0)
+ var carry = word
+ var i = shift
+ while carry > 0 {
+ let (d, c) = self[i].addingReportingOverflow(carry)
+ self[i] = d
+ carry = (c ? 1 : 0)
+ i += 1
+ }
+ }
+
+ /// Add the digit `d` to this integer and return the result.
+ /// `d` is shifted `shift` words to the left before being added.
+ ///
+ /// - Complexity: O(max(count, shift))
+ internal func addingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt {
+ var r = self
+ r.addWord(word, shiftedBy: shift)
+ return r
+ }
+
+ /// Add `b` to this integer in place.
+ /// `b` is shifted `shift` words to the left before being added.
+ ///
+ /// - Complexity: O(max(count, b.count + shift))
+ internal mutating func add(_ b: CS.BigUInt, shiftedBy shift: Int = 0) {
+ precondition(shift >= 0)
+ var carry = false
+ var bi = 0
+ let bc = b.count
+ while bi < bc || carry {
+ let ai = shift + bi
+ let (d, c) = self[ai].addingReportingOverflow(b[bi])
+ if carry {
+ let (d2, c2) = d.addingReportingOverflow(1)
+ self[ai] = d2
+ carry = c || c2
+ }
+ else {
+ self[ai] = d
+ carry = c
+ }
+ bi += 1
+ }
+ }
+
+ /// Add `b` to this integer and return the result.
+ /// `b` is shifted `shift` words to the left before being added.
+ ///
+ /// - Complexity: O(max(count, b.count + shift))
+ internal func adding(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt {
+ var r = self
+ r.add(b, shiftedBy: shift)
+ return r
+ }
+
+ /// Increment this integer by one. If `shift` is non-zero, it selects
+ /// the word that is to be incremented.
+ ///
+ /// - Complexity: O(count + shift)
+ internal mutating func increment(shiftedBy shift: Int = 0) {
+ self.addWord(1, shiftedBy: shift)
+ }
+
+ /// Add `a` and `b` together and return the result.
+ ///
+ /// - Complexity: O(max(a.count, b.count))
+ public static func +(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt {
+ return a.adding(b)
+ }
+
+ /// Add `a` and `b` together, and store the sum in `a`.
+ ///
+ /// - Complexity: O(max(a.count, b.count))
+ public static func +=(a: inout CS.BigUInt, b: CS.BigUInt) {
+ a.add(b, shiftedBy: 0)
+ }
+}
+
+extension CS.BigInt {
+ /// Add `a` to `b` and return the result.
+ public static func +(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
+ switch (a.sign, b.sign) {
+ case (.plus, .plus):
+ return CS.BigInt(sign: .plus, magnitude: a.magnitude + b.magnitude)
+ case (.minus, .minus):
+ return CS.BigInt(sign: .minus, magnitude: a.magnitude + b.magnitude)
+ case (.plus, .minus):
+ if a.magnitude >= b.magnitude {
+ return CS.BigInt(sign: .plus, magnitude: a.magnitude - b.magnitude)
+ }
+ else {
+ return CS.BigInt(sign: .minus, magnitude: b.magnitude - a.magnitude)
+ }
+ case (.minus, .plus):
+ if b.magnitude >= a.magnitude {
+ return CS.BigInt(sign: .plus, magnitude: b.magnitude - a.magnitude)
+ }
+ else {
+ return CS.BigInt(sign: .minus, magnitude: a.magnitude - b.magnitude)
+ }
+ }
+ }
+
+ /// Add `b` to `a` in place.
+ public static func +=(a: inout CS.BigInt, b: CS.BigInt) {
+ a = a + b
+ }
+}
+
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+extension Array {
+ @inlinable
+ init(reserveCapacity: Int) {
+ self = Array<Element>()
+ self.reserveCapacity(reserveCapacity)
+ }
+
+ @inlinable
+ var slice: ArraySlice<Element> {
+ self[self.startIndex ..< self.endIndex]
+ }
+
+ @inlinable
+ subscript (safe index: Index) -> Element? {
+ return indices.contains(index) ? self[index] : nil
+ }
+}
+
+extension Array where Element == UInt8 {
+ public init(hex: String) {
+ self.init(reserveCapacity: hex.unicodeScalars.lazy.underestimatedCount)
+ var buffer: UInt8?
+ var skip = hex.hasPrefix("0x") ? 2 : 0
+ for char in hex.unicodeScalars.lazy {
+ guard skip == 0 else {
+ skip -= 1
+ continue
+ }
+ guard char.value >= 48 && char.value <= 102 else {
+ removeAll()
+ return
+ }
+ let v: UInt8
+ let c: UInt8 = UInt8(char.value)
+ switch c {
+ case let c where c <= 57:
+ v = c - 48
+ case let c where c >= 65 && c <= 70:
+ v = c - 55
+ case let c where c >= 97:
+ v = c - 87
+ default:
+ removeAll()
+ return
+ }
+ if let b = buffer {
+ append(b << 4 | v)
+ buffer = nil
+ } else {
+ buffer = v
+ }
+ }
+ if let b = buffer {
+ append(b)
+ }
+ }
+
+ public func toHexString() -> String {
+ `lazy`.reduce(into: "") {
+ var s = String($1, radix: 16)
+ if s.count == 1 {
+ s = "0" + s
+ }
+ $0 += s
+ }
+ }
+}
+
+extension Array where Element == UInt8 {
+ /// split in chunks with given chunk size
+ @available(*, deprecated)
+ public func chunks(size chunksize: Int) -> Array<Array<Element>> {
+ var words = Array<Array<Element>>()
+ words.reserveCapacity(count / chunksize)
+ for idx in stride(from: chunksize, through: count, by: chunksize) {
+ words.append(Array(self[idx - chunksize ..< idx])) // slow for large table
+ }
+ let remainder = suffix(count % chunksize)
+ if !remainder.isEmpty {
+ words.append(Array(remainder))
+ }
+ return words
+ }
+
+ public func md5() -> [Element] {
+ Digest.md5(self)
+ }
+
+ public func sha1() -> [Element] {
+ Digest.sha1(self)
+ }
+
+ public func sha224() -> [Element] {
+ Digest.sha224(self)
+ }
+
+ public func sha256() -> [Element] {
+ Digest.sha256(self)
+ }
+
+ public func sha384() -> [Element] {
+ Digest.sha384(self)
+ }
+
+ public func sha512() -> [Element] {
+ Digest.sha512(self)
+ }
+
+ public func sha2(_ variant: SHA2.Variant) -> [Element] {
+ Digest.sha2(self, variant: variant)
+ }
+
+ public func sha3(_ variant: SHA3.Variant) -> [Element] {
+ Digest.sha3(self, variant: variant)
+ }
+
+ public func crc32(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ Checksum.crc32(self, seed: seed, reflect: reflect)
+ }
+
+ public func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ Checksum.crc32c(self, seed: seed, reflect: reflect)
+ }
+
+ public func crc16(seed: UInt16? = nil) -> UInt16 {
+ Checksum.crc16(self, seed: seed)
+ }
+
+ public func encrypt(cipher: Cipher) throws -> [Element] {
+ try cipher.encrypt(self.slice)
+ }
+
+ public func decrypt(cipher: Cipher) throws -> [Element] {
+ try cipher.decrypt(self.slice)
+ }
+
+ public func authenticate<A: Authenticator>(with authenticator: A) throws -> [Element] {
+ try authenticator.authenticate(self)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+public extension Array where Element == UInt8 {
+ func toBase64(options: Data.Base64EncodingOptions = []) -> String {
+ Data(self).base64EncodedString(options: options)
+ }
+
+ init(base64: String, options: Data.Base64DecodingOptions = .ignoreUnknownCharacters) {
+ self.init()
+
+ guard let decodedData = Data(base64Encoded: base64, options: options) else {
+ return
+ }
+
+ append(contentsOf: decodedData.bytes)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// Message authentication code.
+public protocol Authenticator {
+ /// Calculate Message Authentication Code (MAC) for message.
+ func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8>
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@usableFromInline
+struct BatchedCollectionIndex<Base: Collection> {
+ let range: Range<Base.Index>
+}
+
+extension BatchedCollectionIndex: Comparable {
+ @usableFromInline
+ static func == <Base>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
+ lhs.range.lowerBound == rhs.range.lowerBound
+ }
+
+ @usableFromInline
+ static func < <Base>(lhs: BatchedCollectionIndex<Base>, rhs: BatchedCollectionIndex<Base>) -> Bool {
+ lhs.range.lowerBound < rhs.range.lowerBound
+ }
+}
+
+protocol BatchedCollectionType: Collection {
+ associatedtype Base: Collection
+}
+
+@usableFromInline
+struct BatchedCollection<Base: Collection>: Collection {
+ let base: Base
+ let size: Int
+
+ @usableFromInline
+ init(base: Base, size: Int) {
+ self.base = base
+ self.size = size
+ }
+
+ @usableFromInline
+ typealias Index = BatchedCollectionIndex<Base>
+
+ private func nextBreak(after idx: Base.Index) -> Base.Index {
+ self.base.index(idx, offsetBy: self.size, limitedBy: self.base.endIndex) ?? self.base.endIndex
+ }
+
+ @usableFromInline
+ var startIndex: Index {
+ Index(range: self.base.startIndex..<self.nextBreak(after: self.base.startIndex))
+ }
+
+ @usableFromInline
+ var endIndex: Index {
+ Index(range: self.base.endIndex..<self.base.endIndex)
+ }
+
+ @usableFromInline
+ func index(after idx: Index) -> Index {
+ Index(range: idx.range.upperBound..<self.nextBreak(after: idx.range.upperBound))
+ }
+
+ @usableFromInline
+ subscript(idx: Index) -> Base.SubSequence {
+ self.base[idx.range]
+ }
+}
+
+extension Collection {
+ @inlinable
+ func batched(by size: Int) -> BatchedCollection<Self> {
+ BatchedCollection(base: self, size: size)
+ }
+}
--- /dev/null
+//
+// CS.BigInt.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2015-12-27.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+//MARK: CS.BigInt
+
+extension CS {
+
+ /// An arbitary precision signed integer type, also known as a "big integer".
+ ///
+ /// Operations on big integers never overflow, but they might take a long time to execute.
+ /// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers.
+ ///
+ /// This particular big integer type uses base-2^64 digits to represent integers.
+ ///
+ /// `BigInt` is essentially a tiny wrapper that extends `BigUInt` with a sign bit and provides signed integer
+ /// operations. Both the underlying absolute value and the negative/positive flag are available as read-write
+ /// properties.
+ ///
+ /// Not all algorithms of `BigUInt` are available for `BigInt` values; for example, there is no square root or
+ /// primality test for signed integers. When you need to call one of these, just extract the absolute value:
+ ///
+ /// ```Swift
+ /// CS.BigInt(255).abs.isPrime() // Returns false
+ /// ```
+ ///
+ public struct BigInt: SignedInteger {
+ public enum Sign {
+ case plus
+ case minus
+ }
+
+ public typealias Magnitude = BigUInt
+
+ /// The type representing a digit in `BigInt`'s underlying number system.
+ public typealias Word = BigUInt.Word
+
+ public static var isSigned: Bool {
+ return true
+ }
+
+ /// The absolute value of this integer.
+ public var magnitude: BigUInt
+
+ /// True iff the value of this integer is negative.
+ public var sign: Sign
+
+ /// Initializes a new big integer with the provided absolute number and sign flag.
+ public init(sign: Sign, magnitude: BigUInt) {
+ self.sign = (magnitude.isZero ? .plus : sign)
+ self.magnitude = magnitude
+ }
+
+ /// Return true iff this integer is zero.
+ ///
+ /// - Complexity: O(1)
+ public var isZero: Bool {
+ return magnitude.isZero
+ }
+
+ /// Returns `-1` if this value is negative and `1` if it’s positive; otherwise, `0`.
+ ///
+ /// - Returns: The sign of this number, expressed as an integer of the same type.
+ public func signum() -> CS.BigInt {
+ switch sign {
+ case .plus:
+ return isZero ? 0 : 1
+ case .minus:
+ return -1
+ }
+ }
+ }
+
+}
--- /dev/null
+//
+// BigUInt.swift
+// BigInt
+//
+// Created by Károly Lőrentey on 2015-12-26.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS {
+
+ /// An arbitary precision unsigned integer type, also known as a "big integer".
+ ///
+ /// Operations on big integers never overflow, but they may take a long time to execute.
+ /// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers.
+ ///
+ /// This particular big integer type uses base-2^64 digits to represent integers; you can think of it as a wrapper
+ /// around `Array<UInt64>`. (In fact, `BigUInt` only uses an array if there are more than two digits.)
+ public struct BigUInt: UnsignedInteger {
+ /// The type representing a digit in `BigUInt`'s underlying number system.
+ public typealias Word = UInt
+
+ /// The storage variants of a `BigUInt`.
+ enum Kind {
+ /// Value consists of the two specified words (low and high). Either or both words may be zero.
+ case inline(Word, Word)
+ /// Words are stored in a slice of the storage array.
+ case slice(from: Int, to: Int)
+ /// Words are stored in the storage array.
+ case array
+ }
+
+ internal fileprivate (set) var kind: Kind // Internal for testing only
+ internal fileprivate (set) var storage: [Word] // Internal for testing only; stored separately to prevent COW copies
+
+ /// Initializes a new BigUInt with value 0.
+ public init() {
+ self.kind = .inline(0, 0)
+ self.storage = []
+ }
+
+ internal init(word: Word) {
+ self.kind = .inline(word, 0)
+ self.storage = []
+ }
+
+ internal init(low: Word, high: Word) {
+ self.kind = .inline(low, high)
+ self.storage = []
+ }
+
+ /// Initializes a new BigUInt with the specified digits. The digits are ordered from least to most significant.
+ public init(words: [Word]) {
+ self.kind = .array
+ self.storage = words
+ normalize()
+ }
+
+ internal init(words: [Word], from startIndex: Int, to endIndex: Int) {
+ self.kind = .slice(from: startIndex, to: endIndex)
+ self.storage = words
+ normalize()
+ }
+ }
+
+}
+
+extension CS.BigUInt {
+ public static var isSigned: Bool {
+ return false
+ }
+
+ /// Return true iff this integer is zero.
+ ///
+ /// - Complexity: O(1)
+ var isZero: Bool {
+ switch kind {
+ case .inline(0, 0): return true
+ case .array: return storage.isEmpty
+ default:
+ return false
+ }
+ }
+
+ /// Returns `1` if this value is, positive; otherwise, `0`.
+ ///
+ /// - Returns: The sign of this number, expressed as an integer of the same type.
+ public func signum() -> CS.BigUInt {
+ return isZero ? 0 : 1
+ }
+}
+
+extension CS.BigUInt {
+ mutating func ensureArray() {
+ switch kind {
+ case let .inline(w0, w1):
+ kind = .array
+ storage = w1 != 0 ? [w0, w1]
+ : w0 != 0 ? [w0]
+ : []
+ case let .slice(from: start, to: end):
+ kind = .array
+ storage = Array(storage[start ..< end])
+ case .array:
+ break
+ }
+ }
+
+ var capacity: Int {
+ guard case .array = kind else { return 0 }
+ return storage.capacity
+ }
+
+ mutating func reserveCapacity(_ minimumCapacity: Int) {
+ switch kind {
+ case let .inline(w0, w1):
+ kind = .array
+ storage.reserveCapacity(minimumCapacity)
+ if w1 != 0 {
+ storage.append(w0)
+ storage.append(w1)
+ }
+ else if w0 != 0 {
+ storage.append(w0)
+ }
+ case let .slice(from: start, to: end):
+ kind = .array
+ var words: [Word] = []
+ words.reserveCapacity(Swift.max(end - start, minimumCapacity))
+ words.append(contentsOf: storage[start ..< end])
+ storage = words
+ case .array:
+ storage.reserveCapacity(minimumCapacity)
+ }
+ }
+
+ /// Gets rid of leading zero digits in the digit array and converts slices into inline digits when possible.
+ internal mutating func normalize() {
+ switch kind {
+ case .slice(from: let start, to: var end):
+ assert(start >= 0 && end <= storage.count && start <= end)
+ while start < end, storage[end - 1] == 0 {
+ end -= 1
+ }
+ switch end - start {
+ case 0:
+ kind = .inline(0, 0)
+ storage = []
+ case 1:
+ kind = .inline(storage[start], 0)
+ storage = []
+ case 2:
+ kind = .inline(storage[start], storage[start + 1])
+ storage = []
+ case storage.count:
+ assert(start == 0)
+ kind = .array
+ default:
+ kind = .slice(from: start, to: end)
+ }
+ case .array where storage.last == 0:
+ while storage.last == 0 {
+ storage.removeLast()
+ }
+ default:
+ break
+ }
+ }
+
+ /// Set this integer to 0 without releasing allocated storage capacity (if any).
+ mutating func clear() {
+ self.load(0)
+ }
+
+ /// Set this integer to `value` by copying its digits without releasing allocated storage capacity (if any).
+ mutating func load(_ value: CS.BigUInt) {
+ switch kind {
+ case .inline, .slice:
+ self = value
+ case .array:
+ self.storage.removeAll(keepingCapacity: true)
+ self.storage.append(contentsOf: value.words)
+ }
+ }
+}
+
+extension CS.BigUInt {
+ //MARK: Collection-like members
+
+ /// The number of digits in this integer, excluding leading zero digits.
+ var count: Int {
+ switch kind {
+ case let .inline(w0, w1):
+ return w1 != 0 ? 2
+ : w0 != 0 ? 1
+ : 0
+ case let .slice(from: start, to: end):
+ return end - start
+ case .array:
+ return storage.count
+ }
+ }
+
+ /// Get or set a digit at a given index.
+ ///
+ /// - Note: Unlike a normal collection, it is OK for the index to be greater than or equal to `endIndex`.
+ /// The subscripting getter returns zero for indexes beyond the most significant digit.
+ /// Setting these extended digits automatically appends new elements to the underlying digit array.
+ /// - Requires: index >= 0
+ /// - Complexity: The getter is O(1). The setter is O(1) if the conditions below are true; otherwise it's O(count).
+ /// - The integer's storage is not shared with another integer
+ /// - The integer wasn't created as a slice of another integer
+ /// - `index < count`
+ subscript(_ index: Int) -> Word {
+ get {
+ precondition(index >= 0)
+ switch (kind, index) {
+ case (.inline(let w0, _), 0): return w0
+ case (.inline(_, let w1), 1): return w1
+ case (.slice(from: let start, to: let end), _) where index < end - start:
+ return storage[start + index]
+ case (.array, _) where index < storage.count:
+ return storage[index]
+ default:
+ return 0
+ }
+ }
+ set(word) {
+ precondition(index >= 0)
+ switch (kind, index) {
+ case let (.inline(_, w1), 0):
+ kind = .inline(word, w1)
+ case let (.inline(w0, _), 1):
+ kind = .inline(w0, word)
+ case let (.slice(from: start, to: end), _) where index < end - start:
+ replace(at: index, with: word)
+ case (.array, _) where index < storage.count:
+ replace(at: index, with: word)
+ default:
+ extend(at: index, with: word)
+ }
+ }
+ }
+
+ private mutating func replace(at index: Int, with word: Word) {
+ ensureArray()
+ precondition(index < storage.count)
+ storage[index] = word
+ if word == 0, index == storage.count - 1 {
+ normalize()
+ }
+ }
+
+ private mutating func extend(at index: Int, with word: Word) {
+ guard word != 0 else { return }
+ reserveCapacity(index + 1)
+ precondition(index >= storage.count)
+ storage.append(contentsOf: repeatElement(0, count: index - storage.count))
+ storage.append(word)
+ }
+
+ /// Returns an integer built from the digits of this integer in the given range.
+ internal func extract(_ bounds: Range<Int>) -> CS.BigUInt {
+ switch kind {
+ case let .inline(w0, w1):
+ let bounds = bounds.clamped(to: 0 ..< 2)
+ if bounds == 0 ..< 2 {
+ return CS.BigUInt(low: w0, high: w1)
+ }
+ else if bounds == 0 ..< 1 {
+ return CS.BigUInt(word: w0)
+ }
+ else if bounds == 1 ..< 2 {
+ return CS.BigUInt(word: w1)
+ }
+ else {
+ return CS.BigUInt()
+ }
+ case let .slice(from: start, to: end):
+ let s = Swift.min(end, start + Swift.max(bounds.lowerBound, 0))
+ let e = Swift.max(s, (bounds.upperBound > end - start ? end : start + bounds.upperBound))
+ return CS.BigUInt(words: storage, from: s, to: e)
+ case .array:
+ let b = bounds.clamped(to: storage.startIndex ..< storage.endIndex)
+ return CS.BigUInt(words: storage, from: b.lowerBound, to: b.upperBound)
+ }
+ }
+
+ internal func extract<Bounds: RangeExpression>(_ bounds: Bounds) -> CS.BigUInt where Bounds.Bound == Int {
+ return self.extract(bounds.relative(to: 0 ..< Int.max))
+ }
+}
+
+extension CS.BigUInt {
+ internal mutating func shiftRight(byWords amount: Int) {
+ assert(amount >= 0)
+ guard amount > 0 else { return }
+ switch kind {
+ case let .inline(_, w1) where amount == 1:
+ kind = .inline(w1, 0)
+ case .inline(_, _):
+ kind = .inline(0, 0)
+ case let .slice(from: start, to: end):
+ let s = start + amount
+ if s >= end {
+ kind = .inline(0, 0)
+ }
+ else {
+ kind = .slice(from: s, to: end)
+ normalize()
+ }
+ case .array:
+ if amount >= storage.count {
+ storage.removeAll(keepingCapacity: true)
+ }
+ else {
+ storage.removeFirst(amount)
+ }
+ }
+ }
+
+ internal mutating func shiftLeft(byWords amount: Int) {
+ assert(amount >= 0)
+ guard amount > 0 else { return }
+ guard !isZero else { return }
+ switch kind {
+ case let .inline(w0, 0) where amount == 1:
+ kind = .inline(0, w0)
+ case let .inline(w0, w1):
+ let c = (w1 == 0 ? 1 : 2)
+ storage.reserveCapacity(amount + c)
+ storage.append(contentsOf: repeatElement(0, count: amount))
+ storage.append(w0)
+ if w1 != 0 {
+ storage.append(w1)
+ }
+ kind = .array
+ case let .slice(from: start, to: end):
+ var words: [Word] = []
+ words.reserveCapacity(amount + count)
+ words.append(contentsOf: repeatElement(0, count: amount))
+ words.append(contentsOf: storage[start ..< end])
+ storage = words
+ kind = .array
+ case .array:
+ storage.insert(contentsOf: repeatElement(0, count: amount), at: 0)
+ }
+ }
+}
+
+extension CS.BigUInt {
+ //MARK: Low and High
+
+ /// Split this integer into a high-order and a low-order part.
+ ///
+ /// - Requires: count > 1
+ /// - Returns: `(low, high)` such that
+ /// - `self == low.add(high, shiftedBy: middleIndex)`
+ /// - `high.width <= floor(width / 2)`
+ /// - `low.width <= ceil(width / 2)`
+ /// - Complexity: Typically O(1), but O(count) in the worst case, because high-order zero digits need to be removed after the split.
+ internal var split: (high: CS.BigUInt, low: CS.BigUInt) {
+ precondition(count > 1)
+ let mid = middleIndex
+ return (self.extract(mid...), self.extract(..<mid))
+ }
+
+ /// Index of the digit at the middle of this integer.
+ ///
+ /// - Returns: The index of the digit that is least significant in `self.high`.
+ internal var middleIndex: Int {
+ return (count + 1) / 2
+ }
+
+ /// The low-order half of this CS.BigUInt.
+ ///
+ /// - Returns: `self[0 ..< middleIndex]`
+ /// - Requires: count > 1
+ internal var low: CS.BigUInt {
+ return self.extract(0 ..< middleIndex)
+ }
+
+ /// The high-order half of this CS.BigUInt.
+ ///
+ /// - Returns: `self[middleIndex ..< count]`
+ /// - Requires: count > 1
+ internal var high: CS.BigUInt {
+ return self.extract(middleIndex ..< count)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public enum Bit: Int {
+ case zero
+ case one
+}
+
+extension Bit {
+ @inlinable
+ func inverted() -> Bit {
+ self == .zero ? .one : .zero
+ }
+}
--- /dev/null
+//
+// Bitwise Ops.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+//MARK: Bitwise Operations
+
+extension CS.BigUInt {
+ /// Return the ones' complement of `a`.
+ ///
+ /// - Complexity: O(a.count)
+ public static prefix func ~(a: CS.BigUInt) -> CS.BigUInt {
+ return CS.BigUInt(words: a.words.map { ~$0 })
+ }
+
+ /// Calculate the bitwise OR of `a` and `b`, and store the result in `a`.
+ ///
+ /// - Complexity: O(max(a.count, b.count))
+ public static func |= (a: inout CS.BigUInt, b: CS.BigUInt) {
+ a.reserveCapacity(b.count)
+ for i in 0 ..< b.count {
+ a[i] |= b[i]
+ }
+ }
+
+ /// Calculate the bitwise AND of `a` and `b` and return the result.
+ ///
+ /// - Complexity: O(max(a.count, b.count))
+ public static func &= (a: inout CS.BigUInt, b: CS.BigUInt) {
+ for i in 0 ..< Swift.max(a.count, b.count) {
+ a[i] &= b[i]
+ }
+ }
+
+ /// Calculate the bitwise XOR of `a` and `b` and return the result.
+ ///
+ /// - Complexity: O(max(a.count, b.count))
+ public static func ^= (a: inout CS.BigUInt, b: CS.BigUInt) {
+ a.reserveCapacity(b.count)
+ for i in 0 ..< b.count {
+ a[i] ^= b[i]
+ }
+ }
+}
+
+extension CS.BigInt {
+ public static prefix func ~(x: CS.BigInt) -> CS.BigInt {
+ switch x.sign {
+ case .plus:
+ return CS.BigInt(sign: .minus, magnitude: x.magnitude + 1)
+ case .minus:
+ return CS.BigInt(sign: .plus, magnitude: x.magnitude - 1)
+ }
+ }
+
+ public static func &(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
+ let left = lhs.words
+ let right = rhs.words
+ // Note we aren't using left.count/right.count here; we account for the sign bit separately later.
+ let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
+ var words: [UInt] = []
+ words.reserveCapacity(count)
+ for i in 0 ..< count {
+ words.append(left[i] & right[i])
+ }
+ if lhs.sign == .minus && rhs.sign == .minus {
+ words.twosComplement()
+ return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
+ }
+ return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
+ }
+
+ public static func |(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
+ let left = lhs.words
+ let right = rhs.words
+ // Note we aren't using left.count/right.count here; we account for the sign bit separately later.
+ let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
+ var words: [UInt] = []
+ words.reserveCapacity(count)
+ for i in 0 ..< count {
+ words.append(left[i] | right[i])
+ }
+ if lhs.sign == .minus || rhs.sign == .minus {
+ words.twosComplement()
+ return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
+ }
+ return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
+ }
+
+ public static func ^(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt {
+ let left = lhs.words
+ let right = rhs.words
+ // Note we aren't using left.count/right.count here; we account for the sign bit separately later.
+ let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count)
+ var words: [UInt] = []
+ words.reserveCapacity(count)
+ for i in 0 ..< count {
+ words.append(left[i] ^ right[i])
+ }
+ if (lhs.sign == .minus) != (rhs.sign == .minus) {
+ words.twosComplement()
+ return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words))
+ }
+ return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words))
+ }
+
+ public static func &=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
+ lhs = lhs & rhs
+ }
+
+ public static func |=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
+ lhs = lhs | rhs
+ }
+
+ public static func ^=(lhs: inout CS.BigInt, rhs: CS.BigInt) {
+ lhs = lhs ^ rhs
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+protocol BlockCipher: Cipher {
+ static var blockSize: Int { get }
+}
--- /dev/null
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public class BlockDecryptor: Cryptor, Updatable {
+ @usableFromInline
+ let blockSize: Int
+
+ @usableFromInline
+ let padding: Padding
+
+ @usableFromInline
+ var worker: CipherModeWorker
+
+ @usableFromInline
+ var accumulated = Array<UInt8>()
+
+ @usableFromInline
+ init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+ self.blockSize = blockSize
+ self.padding = padding
+ self.worker = worker
+ }
+
+ @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ // If a worker (eg GCM) can combine ciphertext + tag
+ // we need to remove tag from the ciphertext.
+ if !isLast && self.accumulated.count < self.blockSize + self.worker.additionalBufferSize {
+ return []
+ }
+
+ let accumulatedWithoutSuffix: Array<UInt8>
+ if self.worker.additionalBufferSize > 0 {
+ // FIXME: how slow is that?
+ accumulatedWithoutSuffix = Array(self.accumulated.prefix(self.accumulated.count - self.worker.additionalBufferSize))
+ } else {
+ accumulatedWithoutSuffix = self.accumulated
+ }
+
+ var processedBytesCount = 0
+ var plaintext = Array<UInt8>(reserveCapacity: accumulatedWithoutSuffix.count)
+ // Processing in a block-size manner. It's good for block modes, but bad for stream modes.
+ for var chunk in accumulatedWithoutSuffix.batched(by: self.blockSize) {
+ if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize {
+ let isLastChunk = processedBytesCount + chunk.count == accumulatedWithoutSuffix.count
+
+ if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
+ chunk = try finalizingWorker.willDecryptLast(bytes: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size
+ }
+
+ if !chunk.isEmpty {
+ plaintext += worker.decrypt(block: chunk)
+ }
+
+ if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
+ plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
+ }
+
+ processedBytesCount += chunk.count
+ }
+ }
+ accumulated.removeFirst(processedBytesCount) // super-slow
+
+ if isLast {
+ if accumulatedWithoutSuffix.isEmpty, var finalizingWorker = worker as? FinalizingDecryptModeWorker {
+ try finalizingWorker.willDecryptLast(bytes: self.accumulated.suffix(self.worker.additionalBufferSize))
+ plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
+ }
+ plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize)
+ }
+
+ return plaintext
+ }
+
+ public func seek(to position: Int) throws {
+ guard var worker = self.worker as? SeekableModeWorker else {
+ fatalError("Not supported")
+ }
+
+ try worker.seek(to: position)
+ self.worker = worker
+
+ accumulated = []
+ }
+}
--- /dev/null
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@usableFromInline
+final class BlockEncryptor: Cryptor, Updatable {
+ private let blockSize: Int
+ private var worker: CipherModeWorker
+ private let padding: Padding
+ // Accumulated bytes. Not all processed bytes.
+ private var accumulated = Array<UInt8>(reserveCapacity: 16)
+
+ private var lastBlockRemainder = 0
+
+ @usableFromInline
+ init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+ self.blockSize = blockSize
+ self.padding = padding
+ self.worker = worker
+ }
+
+ // MARK: Updatable
+
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ if isLast {
+ self.accumulated = self.padding.add(to: self.accumulated, blockSize: self.blockSize)
+ }
+
+ var encrypted = Array<UInt8>(reserveCapacity: accumulated.count)
+ for chunk in self.accumulated.batched(by: self.blockSize) {
+ if isLast || chunk.count == self.blockSize {
+ encrypted += self.worker.encrypt(block: chunk)
+ }
+ }
+
+ // Stream encrypts all, so it removes all elements
+ self.accumulated.removeFirst(encrypted.count)
+
+ if var finalizingWorker = worker as? FinalizingEncryptModeWorker, isLast == true {
+ encrypted = Array(try finalizingWorker.finalize(encrypt: encrypted.slice))
+ }
+
+ return encrypted
+ }
+
+ @usableFromInline
+ func seek(to: Int) throws {
+ fatalError("Not supported")
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public typealias CipherOperationOnBlock = (_ block: ArraySlice<UInt8>) -> Array<UInt8>?
+
+public protocol BlockMode {
+ var options: BlockModeOption { get }
+ //TODO: doesn't have to be public
+ @inlinable func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker
+
+ var customBlockSize: Int? { get }
+}
+
+typealias StreamMode = BlockMode
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public struct BlockModeOption: OptionSet {
+ public let rawValue: Int
+
+ public init(rawValue: Int) {
+ self.rawValue = rawValue
+ }
+
+ @usableFromInline
+ static let none = BlockModeOption(rawValue: 1 << 0)
+
+ @usableFromInline
+ static let initializationVectorRequired = BlockModeOption(rawValue: 1 << 1)
+
+ @usableFromInline
+ static let paddingRequired = BlockModeOption(rawValue: 1 << 2)
+
+ @usableFromInline
+ static let useEncryptToDecrypt = BlockModeOption(rawValue: 1 << 3)
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension Blowfish {
+ /// Initialize with CBC block mode.
+ public convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws {
+ try self.init(key: key.bytes, blockMode: CBC(iv: iv.bytes), padding: padding)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// https://en.wikipedia.org/wiki/Blowfish_(cipher)
+// Based on Paul Kocher implementation
+//
+
+public final class Blowfish {
+ public enum Error: Swift.Error {
+ /// Data padding is required
+ case dataPaddingRequired
+ /// Invalid key or IV
+ case invalidKeyOrInitializationVector
+ /// Invalid IV
+ case invalidInitializationVector
+ /// Invalid block mode
+ case invalidBlockMode
+ }
+
+ public static let blockSize: Int = 8 // 64 bit
+ public let keySize: Int
+
+ private let blockMode: BlockMode
+ private let padding: Padding
+ private var decryptWorker: CipherModeWorker!
+ private var encryptWorker: CipherModeWorker!
+
+ private let N = 16 // rounds
+ private var P: Array<UInt32>
+ private var S: Array<Array<UInt32>>
+ private let origP: Array<UInt32> = [
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,
+ 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,
+ 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,
+ 0xb5470917, 0x9216d5d9, 0x8979fb1b
+ ]
+
+ private let origS: Array<Array<UInt32>> = [
+ [
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+ ],
+ [
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+ ],
+ [
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+ ],
+ [
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+ ]
+ ]
+
+ public init(key: Array<UInt8>, blockMode: BlockMode = CBC(iv: Array<UInt8>(repeating: 0, count: Blowfish.blockSize)), padding: Padding) throws {
+ precondition(key.count >= 5 && key.count <= 56)
+
+ self.blockMode = blockMode
+ self.padding = padding
+ self.keySize = key.count
+
+ self.S = self.origS
+ self.P = self.origP
+
+ self.expandKey(key: key)
+ try self.setupBlockModeWorkers()
+ }
+
+ private func setupBlockModeWorkers() throws {
+ let decryptBlock = { [weak self] (block: ArraySlice<UInt8>) -> Array<UInt8>? in
+ self?.decrypt(block: block)
+ }
+
+ let encryptBlock = { [weak self] (block: ArraySlice<UInt8>) -> Array<UInt8>? in
+ self?.encrypt(block: block)
+ }
+
+ self.encryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: encryptBlock, encryptionOperation: encryptBlock)
+
+ if self.blockMode.options.contains(.useEncryptToDecrypt) {
+ self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: encryptBlock, encryptionOperation: encryptBlock)
+ } else {
+ self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: decryptBlock, encryptionOperation: encryptBlock)
+ }
+ }
+
+ private func reset() {
+ self.S = self.origS
+ self.P = self.origP
+ // todo expand key
+ }
+
+ private func expandKey(key: Array<UInt8>) {
+ var j = 0
+ for i in 0..<(self.N + 2) {
+ var data: UInt32 = 0x0
+ for _ in 0..<4 {
+ data = (data << 8) | UInt32(key[j])
+ j += 1
+ if j >= key.count {
+ j = 0
+ }
+ }
+ self.P[i] ^= data
+ }
+
+ var datal: UInt32 = 0
+ var datar: UInt32 = 0
+
+ for i in stride(from: 0, to: self.N + 2, by: 2) {
+ self.encryptBlowfishBlock(l: &datal, r: &datar)
+ self.P[i] = datal
+ self.P[i + 1] = datar
+ }
+
+ for i in 0..<4 {
+ for j in stride(from: 0, to: 256, by: 2) {
+ self.encryptBlowfishBlock(l: &datal, r: &datar)
+ self.S[i][j] = datal
+ self.S[i][j + 1] = datar
+ }
+ }
+ }
+
+ private func encrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
+ var result = Array<UInt8>()
+
+ var l = UInt32(bytes: block[block.startIndex..<block.startIndex.advanced(by: 4)])
+ var r = UInt32(bytes: block[block.startIndex.advanced(by: 4)..<block.startIndex.advanced(by: 8)])
+
+ self.encryptBlowfishBlock(l: &l, r: &r)
+
+ // because everything is too complex to be solved in reasonable time o_O
+ result += [
+ UInt8((l >> 24) & 0xff),
+ UInt8((l >> 16) & 0xff)
+ ]
+ result += [
+ UInt8((l >> 8) & 0xff),
+ UInt8((l >> 0) & 0xff)
+ ]
+ result += [
+ UInt8((r >> 24) & 0xff),
+ UInt8((r >> 16) & 0xff)
+ ]
+ result += [
+ UInt8((r >> 8) & 0xff),
+ UInt8((r >> 0) & 0xff)
+ ]
+
+ return result
+ }
+
+ private func decrypt(block: ArraySlice<UInt8>) -> Array<UInt8>? {
+ var result = Array<UInt8>()
+
+ var l = UInt32(bytes: block[block.startIndex..<block.startIndex.advanced(by: 4)])
+ var r = UInt32(bytes: block[block.startIndex.advanced(by: 4)..<block.startIndex.advanced(by: 8)])
+
+ self.decryptBlowfishBlock(l: &l, r: &r)
+
+ // because everything is too complex to be solved in reasonable time o_O
+ result += [
+ UInt8((l >> 24) & 0xff),
+ UInt8((l >> 16) & 0xff)
+ ]
+ result += [
+ UInt8((l >> 8) & 0xff),
+ UInt8((l >> 0) & 0xff)
+ ]
+ result += [
+ UInt8((r >> 24) & 0xff),
+ UInt8((r >> 16) & 0xff)
+ ]
+ result += [
+ UInt8((r >> 8) & 0xff),
+ UInt8((r >> 0) & 0xff)
+ ]
+ return result
+ }
+
+ /// Encrypts the 8-byte padded buffer
+ ///
+ /// - Parameters:
+ /// - l: left half
+ /// - r: right half
+ private func encryptBlowfishBlock(l: inout UInt32, r: inout UInt32) {
+ var Xl = l
+ var Xr = r
+
+ for i in 0..<self.N {
+ Xl = Xl ^ self.P[i]
+ Xr = self.F(x: Xl) ^ Xr
+
+ (Xl, Xr) = (Xr, Xl)
+ }
+
+ (Xl, Xr) = (Xr, Xl)
+
+ Xr = Xr ^ self.P[self.N]
+ Xl = Xl ^ self.P[self.N + 1]
+
+ l = Xl
+ r = Xr
+ }
+
+ /// Decrypts the 8-byte padded buffer
+ ///
+ /// - Parameters:
+ /// - l: left half
+ /// - r: right half
+ private func decryptBlowfishBlock(l: inout UInt32, r: inout UInt32) {
+ var Xl = l
+ var Xr = r
+
+ for i in (2...self.N + 1).reversed() {
+ Xl = Xl ^ self.P[i]
+ Xr = self.F(x: Xl) ^ Xr
+
+ (Xl, Xr) = (Xr, Xl)
+ }
+
+ (Xl, Xr) = (Xr, Xl)
+
+ Xr = Xr ^ self.P[1]
+ Xl = Xl ^ self.P[0]
+
+ l = Xl
+ r = Xr
+ }
+
+ private func F(x: UInt32) -> UInt32 {
+ let f1 = self.S[0][Int(x >> 24) & 0xff]
+ let f2 = self.S[1][Int(x >> 16) & 0xff]
+ let f3 = self.S[2][Int(x >> 8) & 0xff]
+ let f4 = self.S[3][Int(x & 0xff)]
+ return ((f1 &+ f2) ^ f3) &+ f4
+ }
+}
+
+extension Blowfish: Cipher {
+ /// Encrypt the 8-byte padded buffer, block by block. Note that for amounts of data larger than a block, it is not safe to just call encrypt() on successive blocks.
+ ///
+ /// - Parameter bytes: Plaintext data
+ /// - Returns: Encrypted data
+ public func encrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Element == UInt8, C.Index == Int {
+ let bytes = self.padding.add(to: Array(bytes), blockSize: Blowfish.blockSize) // FIXME: Array(bytes) copies
+
+ var out = Array<UInt8>()
+ out.reserveCapacity(bytes.count)
+
+ for chunk in bytes.batched(by: Blowfish.blockSize) {
+ out += self.encryptWorker.encrypt(block: chunk)
+ }
+
+ if self.blockMode.options.contains(.paddingRequired) && (out.count % Blowfish.blockSize != 0) {
+ throw Error.dataPaddingRequired
+ }
+
+ return out
+ }
+
+ /// Decrypt the 8-byte padded buffer
+ ///
+ /// - Parameter bytes: Ciphertext data
+ /// - Returns: Plaintext data
+ public func decrypt<C: Collection>(_ bytes: C) throws -> Array<UInt8> where C.Element == UInt8, C.Index == Int {
+ if self.blockMode.options.contains(.paddingRequired) && (bytes.count % Blowfish.blockSize != 0) {
+ throw Error.dataPaddingRequired
+ }
+
+ var out = Array<UInt8>()
+ out.reserveCapacity(bytes.count)
+
+ for chunk in Array(bytes).batched(by: Blowfish.blockSize) {
+ out += self.decryptWorker.decrypt(block: chunk) // FIXME: copying here is innefective
+ }
+
+ out = self.padding.remove(from: out, blockSize: Blowfish.blockSize)
+
+ return out
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Cipher-block chaining (CBC)
+//
+
+public struct CBC: BlockMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
+
+ private let iv: Array<UInt8>
+
+ public let customBlockSize: Int? = nil
+
+ public init(iv: Array<UInt8>) {
+ self.iv = iv
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.iv.count != blockSize {
+ throw Error.invalidInitializationVector
+ }
+
+ return CBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
+ }
+}
+
+struct CBCModeWorker: BlockModeWorker {
+ let cipherOperation: CipherOperationOnBlock
+ var blockSize: Int
+ let additionalBufferSize: Int = 0
+ private let iv: ArraySlice<UInt8>
+ private var prev: ArraySlice<UInt8>?
+
+ @inlinable
+ init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = blockSize
+ self.iv = iv
+ self.cipherOperation = cipherOperation
+ }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
+ return Array(plaintext)
+ }
+ self.prev = ciphertext.slice
+ return ciphertext
+ }
+
+ @inlinable
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let plaintext = cipherOperation(ciphertext) else {
+ return Array(ciphertext)
+ }
+ let result: Array<UInt8> = xor(prev ?? self.iv, plaintext)
+ self.prev = ciphertext
+ return result
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public final class CBCMAC: CMAC {
+ public override func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ var inBytes = bytes
+ bitPadding(to: &inBytes, blockSize: CMAC.BlockSize)
+ let blocks = inBytes.batched(by: CMAC.BlockSize)
+
+ var lastBlockEncryptionResult: [UInt8] = CBCMAC.Zero
+ try blocks.forEach { block in
+ let aes = try AES(key: Array(key), blockMode: CBC(iv: lastBlockEncryptionResult), padding: .noPadding)
+ lastBlockEncryptionResult = try aes.encrypt(block)
+ }
+
+ return lastBlockEncryptionResult
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// CCM mode combines the well known CBC-MAC with the well known counter mode of encryption.
+// https://tools.ietf.org/html/rfc3610
+// https://csrc.nist.gov/publications/detail/sp/800-38c/final
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#elseif canImport(WASILibc)
+import WASILibc
+#endif
+
+/// Counter with Cipher Block Chaining-Message Authentication Code
+public struct CCM: StreamMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ case invalidParameter
+ case fail
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+ private let nonce: Array<UInt8>
+ private let additionalAuthenticatedData: Array<UInt8>?
+ private let tagLength: Int
+ private let messageLength: Int // total message length. need to know in advance
+ public let customBlockSize: Int? = nil
+
+ // `authenticationTag` nil for encryption, known tag for decryption
+ /// For encryption, the value is set at the end of the encryption.
+ /// For decryption, this is a known Tag to validate against.
+ public var authenticationTag: Array<UInt8>?
+
+ /// Initialize CCM
+ ///
+ /// - Parameters:
+ /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
+ /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
+ /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
+ /// - additionalAuthenticatedData: Additional authenticated data.
+ public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, additionalAuthenticatedData: Array<UInt8>? = nil) {
+ self.nonce = iv
+ self.tagLength = tagLength
+ self.additionalAuthenticatedData = additionalAuthenticatedData
+ self.messageLength = messageLength // - tagLength
+ }
+
+ /// Initialize CCM
+ ///
+ /// - Parameters:
+ /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes.
+ /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}.
+ /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance.
+ /// - authenticationTag: Authentication Tag value if not concatenated to ciphertext.
+ /// - additionalAuthenticatedData: Additional authenticated data.
+ public init(iv: Array<UInt8>, tagLength: Int, messageLength: Int, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil) {
+ self.init(iv: iv, tagLength: tagLength, messageLength: messageLength, additionalAuthenticatedData: additionalAuthenticatedData)
+ self.authenticationTag = authenticationTag
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.nonce.isEmpty {
+ throw Error.invalidInitializationVector
+ }
+
+ return CCMModeWorker(blockSize: blockSize, nonce: self.nonce.slice, messageLength: self.messageLength, additionalAuthenticatedData: self.additionalAuthenticatedData, tagLength: self.tagLength, cipherOperation: cipherOperation)
+ }
+}
+
+class CCMModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
+ typealias Counter = Int
+ var counter = 0
+
+ let cipherOperation: CipherOperationOnBlock
+ let blockSize: Int
+ private let tagLength: Int
+ private let messageLength: Int // total message length. need to know in advance
+ private let q: UInt8
+
+ let additionalBufferSize: Int
+ private var keystreamPosIdx = 0
+ private let nonce: Array<UInt8>
+ private var last_y: ArraySlice<UInt8> = []
+ private var keystream: Array<UInt8> = []
+ // Known Tag used to validate during decryption
+ private var expectedTag: Array<UInt8>?
+
+ public enum Error: Swift.Error {
+ case invalidParameter
+ }
+
+ init(blockSize: Int, nonce: ArraySlice<UInt8>, messageLength: Int, additionalAuthenticatedData: [UInt8]?, expectedTag: Array<UInt8>? = nil, tagLength: Int, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = 16 // CCM is defined for 128 block size
+ self.tagLength = tagLength
+ self.additionalBufferSize = tagLength
+ self.messageLength = messageLength
+ self.expectedTag = expectedTag
+ self.cipherOperation = cipherOperation
+ self.nonce = Array(nonce)
+ self.q = UInt8(15 - nonce.count) // n = 15-q
+
+ let hasAssociatedData = additionalAuthenticatedData != nil && !additionalAuthenticatedData!.isEmpty
+ self.processControlInformation(nonce: self.nonce, tagLength: tagLength, hasAssociatedData: hasAssociatedData)
+
+ if let aad = additionalAuthenticatedData, hasAssociatedData {
+ self.process(aad: aad)
+ }
+ }
+
+ // For the very first time setup new IV (aka y0) from the block0
+ private func processControlInformation(nonce: [UInt8], tagLength: Int, hasAssociatedData: Bool) {
+ let block0 = try! format(nonce: nonce, Q: UInt32(self.messageLength), q: self.q, t: UInt8(tagLength), hasAssociatedData: hasAssociatedData).slice
+ let y0 = self.cipherOperation(block0)!.slice
+ self.last_y = y0
+ }
+
+ private func process(aad: [UInt8]) {
+ let encodedAAD = format(aad: aad)
+
+ for block_i in encodedAAD.batched(by: 16) {
+ let y_i = self.cipherOperation(xor(block_i, self.last_y))!.slice
+ self.last_y = y_i
+ }
+ }
+
+ private func S(i: Int) throws -> [UInt8] {
+ let ctr = try format(counter: i, nonce: nonce, q: q)
+ return self.cipherOperation(ctr.slice)!
+ }
+
+ @inlinable
+ func seek(to position: Int) throws {
+ self.counter = position
+ self.keystream = try self.S(i: position)
+ let offset = position % self.blockSize
+ self.keystreamPosIdx = offset
+ }
+
+ func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ var result = Array<UInt8>(reserveCapacity: plaintext.count)
+
+ var processed = 0
+ while processed < plaintext.count {
+ // Need a full block here to update keystream and do CBC
+ if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
+ // y[i], where i is the counter. Can encrypt 1 block at a time
+ self.counter += 1
+ guard let S = try? S(i: counter) else { return Array(plaintext) }
+ let plaintextP = addPadding(Array(plaintext), blockSize: blockSize)
+ guard let y = cipherOperation(xor(last_y, plaintextP)) else { return Array(plaintext) }
+ self.last_y = y.slice
+
+ self.keystream = S
+ self.keystreamPosIdx = 0
+ }
+
+ let xored: Array<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
+ keystreamPosIdx += xored.count
+ processed += xored.count
+ result += xored
+ }
+ return result
+ }
+
+ @inlinable
+ func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // concatenate T at the end
+ guard let S0 = try? S(i: 0) else { return ciphertext }
+
+ let computedTag = xor(last_y.prefix(self.tagLength), S0) as ArraySlice<UInt8>
+ return ciphertext + computedTag
+ }
+
+ // Decryption is stream
+ // CBC is block
+ private var accumulatedPlaintext: [UInt8] = []
+
+ func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ var output = Array<UInt8>(reserveCapacity: ciphertext.count)
+
+ do {
+ var currentCounter = self.counter
+ var processed = 0
+ while processed < ciphertext.count {
+ // Need a full block here to update keystream and do CBC
+ // New keystream for a new block
+ if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize {
+ currentCounter += 1
+ guard let S = try? S(i: currentCounter) else { return Array(ciphertext) }
+ self.keystream = S
+ self.keystreamPosIdx = 0
+ }
+
+ let xored: Array<UInt8> = xor(ciphertext[ciphertext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) // plaintext
+ keystreamPosIdx += xored.count
+ processed += xored.count
+ output += xored
+ self.counter = currentCounter
+ }
+ }
+
+ // Accumulate plaintext for the MAC calculations at the end.
+ // It would be good to process it together though, here.
+ self.accumulatedPlaintext += output
+
+ // Shouldn't return plaintext until validate tag.
+ // With incremental update, can't validate tag until all block are processed.
+ return output
+ }
+
+ @inlinable
+ func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // concatenate T at the end
+ let computedTag = Array(last_y.prefix(self.tagLength))
+ guard let expectedTag = self.expectedTag, expectedTag == computedTag else {
+ throw CCM.Error.fail
+ }
+
+ return plaintext
+ }
+
+ @discardableResult
+ func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // get tag of additionalBufferSize size
+ // `ciphertext` contains at least additionalBufferSize bytes
+ // overwrite expectedTag property used later for verification
+ guard let S0 = try? S(i: 0) else { return ciphertext }
+ self.expectedTag = xor(ciphertext.suffix(self.tagLength), S0) as [UInt8]
+ return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
+ }
+
+ @inlinable
+ func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+
+ // Calculate Tag, from the last CBC block, for accumulated plaintext.
+ var processed = 0
+ for block in self.accumulatedPlaintext.batched(by: self.blockSize) {
+ let blockP = addPadding(Array(block), blockSize: blockSize)
+ guard let y = cipherOperation(xor(last_y, blockP)) else { return plaintext }
+ self.last_y = y.slice
+ processed += block.count
+ }
+ self.accumulatedPlaintext.removeFirst(processed)
+ return plaintext
+ }
+}
+
+// Q - octet length of P
+// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
+// t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16}
+private func format(nonce N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] {
+ var flags0: UInt8 = 0
+
+ if hasAssociatedData {
+ // 7 bit
+ flags0 |= (1 << 6)
+ }
+
+ // 6,5,4 bit is t in 3 bits
+ flags0 |= (((t - 2) / 2) & 0x07) << 3
+
+ // 3,2,1 bit is q in 3 bits
+ flags0 |= ((q - 1) & 0x07) << 0
+
+ var block0: [UInt8] = Array<UInt8>(repeating: 0, count: 16)
+ block0[0] = flags0
+
+ // N in 1...(15-q) octets, n = 15-q
+ // n is an element of {7,8,9,10,11,12,13}
+ let n = 15 - Int(q)
+ guard (n + Int(q)) == 15 else {
+ // n+q == 15
+ throw CCMModeWorker.Error.invalidParameter
+ }
+ block0[1...n] = N[0...(n - 1)]
+
+ // Q in (16-q)...15 octets
+ block0[(16 - Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice
+
+ return block0
+}
+
+/// Formatting of the Counter Blocks. Ctr[i]
+/// The counter generation function.
+/// Q - octet length of P
+/// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8}
+private func format(counter i: Int, nonce N: [UInt8], q: UInt8) throws -> [UInt8] {
+ var flags0: UInt8 = 0
+
+ // bit 8,7 is Reserved
+ // bit 4,5,6 shall be set to 0
+ // 3,2,1 bit is q in 3 bits
+ flags0 |= ((q - 1) & 0x07) << 0
+
+ var block = Array<UInt8>(repeating: 0, count: 16) // block[0]
+ block[0] = flags0
+
+ // N in 1...(15-q) octets, n = 15-q
+ // n is an element of {7,8,9,10,11,12,13}
+ let n = 15 - Int(q)
+ guard (n + Int(q)) == 15 else {
+ // n+q == 15
+ throw CCMModeWorker.Error.invalidParameter
+ }
+ block[1...n] = N[0...(n - 1)]
+
+ // [i]8q in (16-q)...15 octets
+ block[(16 - Int(q))...15] = i.bytes(totalBytes: Int(q)).slice
+
+ return block
+}
+
+/// Resulting can be partitioned into 16-octet blocks
+private func format(aad: [UInt8]) -> [UInt8] {
+ let a = aad.count
+
+ switch Double(a) {
+ case 0..<65280: // 2^16-2^8
+ // [a]16
+ return addPadding(a.bytes(totalBytes: 2) + aad, blockSize: 16)
+ case 65280..<4_294_967_296: // 2^32
+ // [a]32
+ return addPadding([0xFF, 0xFE] + a.bytes(totalBytes: 4) + aad, blockSize: 16)
+ case 4_294_967_296..<pow(2, 64): // 2^64
+ // [a]64
+ return addPadding([0xFF, 0xFF] + a.bytes(totalBytes: 8) + aad, blockSize: 16)
+ default:
+ // Reserved
+ return addPadding(aad, blockSize: 16)
+ }
+}
+
+// If data is not a multiple of block size bytes long then the remainder is zero padded
+// Note: It's similar to ZeroPadding, but it's not the same.
+private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ if bytes.isEmpty {
+ return Array<UInt8>(repeating: 0, count: blockSize)
+ }
+
+ let remainder = bytes.count % blockSize
+ if remainder == 0 {
+ return bytes
+ }
+
+ let paddingCount = blockSize - remainder
+ if paddingCount > 0 {
+ return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
+ }
+ return bytes
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Cipher feedback (CFB)
+//
+
+public struct CFB: BlockMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ }
+
+ public enum SegmentSize: Int {
+ case cfb8 = 1 // Encrypt byte per byte
+ case cfb128 = 16 // Encrypt 16 bytes per 16 bytes (default)
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+ private let iv: Array<UInt8>
+ private let segmentSize: SegmentSize
+ public let customBlockSize: Int?
+
+ public init(iv: Array<UInt8>, segmentSize: SegmentSize = .cfb128) {
+ self.iv = iv
+ self.segmentSize = segmentSize
+ self.customBlockSize = segmentSize.rawValue
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if !(self.iv.count == blockSize || (segmentSize == .cfb8 && self.iv.count == AES.blockSize)) {
+ throw Error.invalidInitializationVector
+ }
+
+ return CFBModeWorker(blockSize: blockSize, iv: self.iv.slice, segmentSize: segmentSize, cipherOperation: cipherOperation)
+ }
+}
+
+struct CFBModeWorker: BlockModeWorker {
+ let cipherOperation: CipherOperationOnBlock
+ let blockSize: Int
+ let additionalBufferSize: Int = 0
+ private let iv: ArraySlice<UInt8>
+ private let segmentSize: CFB.SegmentSize
+ private var prev: ArraySlice<UInt8>?
+
+ init(blockSize: Int, iv: ArraySlice<UInt8>, segmentSize: CFB.SegmentSize, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = blockSize
+ self.iv = iv
+ self.segmentSize = segmentSize
+ self.cipherOperation = cipherOperation
+ }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ switch segmentSize {
+ case .cfb128:
+ guard let ciphertext = cipherOperation(prev ?? iv) else {
+ return Array(plaintext)
+ }
+ self.prev = xor(plaintext, ciphertext.slice)
+ return Array(self.prev ?? [])
+ case .cfb8:
+ guard let ciphertext = cipherOperation(prev ?? iv) else {
+ return Array(plaintext)
+ }
+ let result = [Array(plaintext)[0] ^ Array(ciphertext)[0]]
+ self.prev = Array((prev ?? iv).dropFirst()) + [result[0]]
+ return result
+ }
+ }
+
+ @inlinable
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ switch segmentSize {
+ case .cfb128:
+ guard let plaintext = cipherOperation(prev ?? iv) else {
+ return Array(ciphertext)
+ }
+ let result: Array<UInt8> = xor(plaintext, ciphertext)
+ prev = ciphertext
+ return result
+ case .cfb8:
+ guard let plaintext = cipherOperation(prev ?? iv) else {
+ return Array(ciphertext)
+ }
+ self.prev = Array((prev ?? iv).dropFirst()) + [Array(ciphertext)[0]]
+ return [Array(ciphertext)[0] ^ Array(plaintext)[0]]
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public class CMAC: Authenticator {
+ public enum Error: Swift.Error {
+ case wrongKeyLength
+ }
+
+ internal let key: SecureBytes
+
+ internal static let BlockSize: Int = 16
+ internal static let Zero: Array<UInt8> = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+ private static let Rb: Array<UInt8> = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87]
+
+ public init(key: Array<UInt8>) throws {
+ self.key = SecureBytes(bytes: key)
+ }
+
+ // MARK: Authenticator
+
+ // AES-CMAC
+ public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ let cipher = try AES(key: Array(key), blockMode: CBC(iv: CMAC.Zero), padding: .noPadding)
+ return try self.authenticate(bytes, cipher: cipher)
+ }
+
+ // CMAC using a Cipher
+ public func authenticate(_ bytes: Array<UInt8>, cipher: Cipher) throws -> Array<UInt8> {
+ let l = try cipher.encrypt(CMAC.Zero)
+ var subKey1 = self.leftShiftOneBit(l)
+ if (l[0] & 0x80) != 0 {
+ subKey1 = xor(CMAC.Rb, subKey1)
+ }
+ var subKey2 = self.leftShiftOneBit(subKey1)
+ if (subKey1[0] & 0x80) != 0 {
+ subKey2 = xor(CMAC.Rb, subKey2)
+ }
+
+ let lastBlockComplete: Bool
+ let blockCount = (bytes.count + CMAC.BlockSize - 1) / CMAC.BlockSize
+ if blockCount == 0 {
+ lastBlockComplete = false
+ } else {
+ lastBlockComplete = bytes.count % CMAC.BlockSize == 0
+ }
+ var paddedBytes = bytes
+ if !lastBlockComplete {
+ bitPadding(to: &paddedBytes, blockSize: CMAC.BlockSize)
+ }
+
+ var blocks = Array(paddedBytes.batched(by: CMAC.BlockSize))
+ var lastBlock = blocks.popLast()!
+ if lastBlockComplete {
+ lastBlock = xor(lastBlock, subKey1)
+ } else {
+ lastBlock = xor(lastBlock, subKey2)
+ }
+
+ var x = Array<UInt8>(repeating: 0x00, count: CMAC.BlockSize)
+ var y = Array<UInt8>(repeating: 0x00, count: CMAC.BlockSize)
+ for block in blocks {
+ y = xor(block, x)
+ x = try cipher.encrypt(y)
+ }
+ // the difference between CMAC and CBC-MAC is that CMAC xors the final block with a secret value
+ y = self.process(lastBlock: lastBlock, with: x)
+ return try cipher.encrypt(y)
+ }
+
+ func process(lastBlock: ArraySlice<UInt8>, with x: [UInt8]) -> [UInt8] {
+ xor(lastBlock, x)
+ }
+
+ // MARK: Helper methods
+
+ /**
+ Performs left shift by one bit to the bit string aquired after concatenating al bytes in the byte array
+ - parameters:
+ - bytes: byte array
+ - returns: bit shifted bit string split again in array of bytes
+ */
+ private func leftShiftOneBit(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ var shifted = Array<UInt8>(repeating: 0x00, count: bytes.count)
+ let last = bytes.count - 1
+ for index in 0..<last {
+ shifted[index] = bytes[index] << 1
+ if (bytes[index + 1] & 0x80) != 0 {
+ shifted[index] += 0x01
+ }
+ }
+ shifted[last] = bytes[last] << 1
+ return shifted
+ }
+}
--- /dev/null
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// To avoid name conflict with BigInt library, I choose to rename
+// BigInt -> BigInteger
+// BigUInt -> BigUInteger
+
+public typealias BigInteger = CS.BigInt
+public typealias BigUInteger = CS.BigUInt
+
+public enum CS {
+ // namespace
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if swift(>=4.1)
+// TODO: remove this file when Xcode 9.2 is no longer used
+#else
+ extension Sequence {
+ @inlinable
+ public func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
+ try flatMap(transform)
+ }
+ }
+#endif
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Counter (CTR)
+
+public struct CTR: StreamMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+ private let iv: Array<UInt8>
+ private let counter: Int
+ public let customBlockSize: Int? = nil
+
+ public init(iv: Array<UInt8>, counter: Int = 0) {
+ self.iv = iv
+ self.counter = counter
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.iv.count != blockSize {
+ throw Error.invalidInitializationVector
+ }
+
+ return CTRModeWorker(blockSize: blockSize, iv: self.iv.slice, counter: self.counter, cipherOperation: cipherOperation)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct CTRModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker {
+ typealias Counter = CTRCounter
+
+ final class CTRCounter {
+ private let constPrefix: Array<UInt8>
+ private var value: UInt64
+ //TODO: make it an updatable value, computing is too slow
+ var bytes: Array<UInt8> {
+ self.constPrefix + self.value.bytes()
+ }
+
+ @inlinable
+ init(_ initialValue: Array<UInt8>) {
+ let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2)
+ self.constPrefix = Array(initialValue[initialValue.startIndex..<halfIndex])
+
+ let suffixBytes = Array(initialValue[halfIndex...])
+ value = UInt64(bytes: suffixBytes)
+ }
+
+ convenience init(nonce: Array<UInt8>, startAt index: Int) {
+ self.init(buildCounterValue(nonce, counter: UInt64(index)))
+ }
+
+ static func += (lhs: CTRCounter, rhs: Int) {
+ lhs.value += UInt64(rhs)
+ }
+ }
+
+ let cipherOperation: CipherOperationOnBlock
+ let additionalBufferSize: Int = 0
+ let iv: Array<UInt8>
+ var counter: CTRCounter
+
+ private let blockSize: Int
+
+ // The same keystream is used for the block length plaintext
+ // As new data is added, keystream suffix is used to xor operation.
+ private var keystream: Array<UInt8>
+ private var keystreamPosIdx = 0
+
+ init(blockSize: Int, iv: ArraySlice<UInt8>, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.cipherOperation = cipherOperation
+ self.blockSize = blockSize
+ self.iv = Array(iv)
+
+ // the first keystream is calculated from the nonce = initial value of counter
+ self.counter = CTRCounter(nonce: Array(iv), startAt: counter)
+ self.keystream = Array(cipherOperation(self.counter.bytes.slice)!)
+ }
+
+ @inlinable
+ mutating func seek(to position: Int) throws {
+ let offset = position % self.blockSize
+ self.counter = CTRCounter(nonce: self.iv, startAt: position / self.blockSize)
+ self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!)
+ self.keystreamPosIdx = offset
+ }
+
+ // plaintext is at most blockSize long
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ var result = Array<UInt8>(reserveCapacity: plaintext.count)
+
+ var processed = 0
+ while processed < plaintext.count {
+ // Update keystream
+ if self.keystreamPosIdx == self.blockSize {
+ self.counter += 1
+ self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!)
+ self.keystreamPosIdx = 0
+ }
+
+ let xored: Array<UInt8> = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...])
+ keystreamPosIdx += xored.count
+ processed += xored.count
+ result += xored
+ }
+
+ return result
+ }
+
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ self.encrypt(block: ciphertext)
+ }
+}
+
+private func buildCounterValue(_ iv: Array<UInt8>, counter: UInt64) -> Array<UInt8> {
+ let noncePartLen = iv.count / 2
+ let noncePrefix = iv[iv.startIndex..<iv.startIndex.advanced(by: noncePartLen)]
+ let nonceSuffix = iv[iv.startIndex.advanced(by: noncePartLen)..<iv.startIndex.advanced(by: iv.count)]
+ let c = UInt64(bytes: nonceSuffix) + counter
+ return noncePrefix + c.bytes()
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension ChaCha20 {
+ public convenience init(key: String, iv: String) throws {
+ try self.init(key: key.bytes, iv: iv.bytes)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// https://tools.ietf.org/html/rfc7539
+//
+
+public final class ChaCha20: BlockCipher {
+ public enum Error: Swift.Error {
+ case invalidKeyOrInitializationVector
+ case notSupported
+ }
+
+ public static let blockSize = 64 // 512 / 8
+ public let keySize: Int
+
+ fileprivate let key: CookiesKey
+ fileprivate var counter: Array<UInt8>
+
+ public init(key: Array<UInt8>, iv nonce: Array<UInt8>) throws {
+ precondition(nonce.count == 12 || nonce.count == 8)
+
+ if key.count != 32 {
+ throw Error.invalidKeyOrInitializationVector
+ }
+
+ self.key = CookiesKey(bytes: key)
+ self.keySize = self.key.count
+
+ if nonce.count == 8 {
+ self.counter = [0, 0, 0, 0, 0, 0, 0, 0] + nonce
+ } else {
+ self.counter = [0, 0, 0, 0] + nonce
+ }
+
+ assert(self.counter.count == 16)
+ }
+
+ /// https://tools.ietf.org/html/rfc7539#section-2.3.
+ fileprivate func core(block: inout Array<UInt8>, counter: Array<UInt8>, key: Array<UInt8>) {
+ precondition(block.count == ChaCha20.blockSize)
+ precondition(counter.count == 16)
+ precondition(key.count == 32)
+
+ let j0: UInt32 = 0x61707865
+ let j1: UInt32 = 0x3320646e // 0x3620646e sigma/tau
+ let j2: UInt32 = 0x79622d32
+ let j3: UInt32 = 0x6b206574
+ let j4: UInt32 = UInt32(bytes: key[0..<4]).bigEndian
+ let j5: UInt32 = UInt32(bytes: key[4..<8]).bigEndian
+ let j6: UInt32 = UInt32(bytes: key[8..<12]).bigEndian
+ let j7: UInt32 = UInt32(bytes: key[12..<16]).bigEndian
+ let j8: UInt32 = UInt32(bytes: key[16..<20]).bigEndian
+ let j9: UInt32 = UInt32(bytes: key[20..<24]).bigEndian
+ let j10: UInt32 = UInt32(bytes: key[24..<28]).bigEndian
+ let j11: UInt32 = UInt32(bytes: key[28..<32]).bigEndian
+ let j12: UInt32 = UInt32(bytes: counter[0..<4]).bigEndian
+ let j13: UInt32 = UInt32(bytes: counter[4..<8]).bigEndian
+ let j14: UInt32 = UInt32(bytes: counter[8..<12]).bigEndian
+ let j15: UInt32 = UInt32(bytes: counter[12..<16]).bigEndian
+
+ var (x0, x1, x2, x3, x4, x5, x6, x7) = (j0, j1, j2, j3, j4, j5, j6, j7)
+ var (x8, x9, x10, x11, x12, x13, x14, x15) = (j8, j9, j10, j11, j12, j13, j14, j15)
+
+ for _ in 0..<10 { // 20 rounds
+ x0 = x0 &+ x4
+ x12 ^= x0
+ x12 = (x12 << 16) | (x12 >> 16)
+ x8 = x8 &+ x12
+ x4 ^= x8
+ x4 = (x4 << 12) | (x4 >> 20)
+ x0 = x0 &+ x4
+ x12 ^= x0
+ x12 = (x12 << 8) | (x12 >> 24)
+ x8 = x8 &+ x12
+ x4 ^= x8
+ x4 = (x4 << 7) | (x4 >> 25)
+ x1 = x1 &+ x5
+ x13 ^= x1
+ x13 = (x13 << 16) | (x13 >> 16)
+ x9 = x9 &+ x13
+ x5 ^= x9
+ x5 = (x5 << 12) | (x5 >> 20)
+ x1 = x1 &+ x5
+ x13 ^= x1
+ x13 = (x13 << 8) | (x13 >> 24)
+ x9 = x9 &+ x13
+ x5 ^= x9
+ x5 = (x5 << 7) | (x5 >> 25)
+ x2 = x2 &+ x6
+ x14 ^= x2
+ x14 = (x14 << 16) | (x14 >> 16)
+ x10 = x10 &+ x14
+ x6 ^= x10
+ x6 = (x6 << 12) | (x6 >> 20)
+ x2 = x2 &+ x6
+ x14 ^= x2
+ x14 = (x14 << 8) | (x14 >> 24)
+ x10 = x10 &+ x14
+ x6 ^= x10
+ x6 = (x6 << 7) | (x6 >> 25)
+ x3 = x3 &+ x7
+ x15 ^= x3
+ x15 = (x15 << 16) | (x15 >> 16)
+ x11 = x11 &+ x15
+ x7 ^= x11
+ x7 = (x7 << 12) | (x7 >> 20)
+ x3 = x3 &+ x7
+ x15 ^= x3
+ x15 = (x15 << 8) | (x15 >> 24)
+ x11 = x11 &+ x15
+ x7 ^= x11
+ x7 = (x7 << 7) | (x7 >> 25)
+ x0 = x0 &+ x5
+ x15 ^= x0
+ x15 = (x15 << 16) | (x15 >> 16)
+ x10 = x10 &+ x15
+ x5 ^= x10
+ x5 = (x5 << 12) | (x5 >> 20)
+ x0 = x0 &+ x5
+ x15 ^= x0
+ x15 = (x15 << 8) | (x15 >> 24)
+ x10 = x10 &+ x15
+ x5 ^= x10
+ x5 = (x5 << 7) | (x5 >> 25)
+ x1 = x1 &+ x6
+ x12 ^= x1
+ x12 = (x12 << 16) | (x12 >> 16)
+ x11 = x11 &+ x12
+ x6 ^= x11
+ x6 = (x6 << 12) | (x6 >> 20)
+ x1 = x1 &+ x6
+ x12 ^= x1
+ x12 = (x12 << 8) | (x12 >> 24)
+ x11 = x11 &+ x12
+ x6 ^= x11
+ x6 = (x6 << 7) | (x6 >> 25)
+ x2 = x2 &+ x7
+ x13 ^= x2
+ x13 = (x13 << 16) | (x13 >> 16)
+ x8 = x8 &+ x13
+ x7 ^= x8
+ x7 = (x7 << 12) | (x7 >> 20)
+ x2 = x2 &+ x7
+ x13 ^= x2
+ x13 = (x13 << 8) | (x13 >> 24)
+ x8 = x8 &+ x13
+ x7 ^= x8
+ x7 = (x7 << 7) | (x7 >> 25)
+ x3 = x3 &+ x4
+ x14 ^= x3
+ x14 = (x14 << 16) | (x14 >> 16)
+ x9 = x9 &+ x14
+ x4 ^= x9
+ x4 = (x4 << 12) | (x4 >> 20)
+ x3 = x3 &+ x4
+ x14 ^= x3
+ x14 = (x14 << 8) | (x14 >> 24)
+ x9 = x9 &+ x14
+ x4 ^= x9
+ x4 = (x4 << 7) | (x4 >> 25)
+ }
+
+ x0 = x0 &+ j0
+ x1 = x1 &+ j1
+ x2 = x2 &+ j2
+ x3 = x3 &+ j3
+ x4 = x4 &+ j4
+ x5 = x5 &+ j5
+ x6 = x6 &+ j6
+ x7 = x7 &+ j7
+ x8 = x8 &+ j8
+ x9 = x9 &+ j9
+ x10 = x10 &+ j10
+ x11 = x11 &+ j11
+ x12 = x12 &+ j12
+ x13 = x13 &+ j13
+ x14 = x14 &+ j14
+ x15 = x15 &+ j15
+
+ block.replaceSubrange(0..<4, with: x0.bigEndian.bytes())
+ block.replaceSubrange(4..<8, with: x1.bigEndian.bytes())
+ block.replaceSubrange(8..<12, with: x2.bigEndian.bytes())
+ block.replaceSubrange(12..<16, with: x3.bigEndian.bytes())
+ block.replaceSubrange(16..<20, with: x4.bigEndian.bytes())
+ block.replaceSubrange(20..<24, with: x5.bigEndian.bytes())
+ block.replaceSubrange(24..<28, with: x6.bigEndian.bytes())
+ block.replaceSubrange(28..<32, with: x7.bigEndian.bytes())
+ block.replaceSubrange(32..<36, with: x8.bigEndian.bytes())
+ block.replaceSubrange(36..<40, with: x9.bigEndian.bytes())
+ block.replaceSubrange(40..<44, with: x10.bigEndian.bytes())
+ block.replaceSubrange(44..<48, with: x11.bigEndian.bytes())
+ block.replaceSubrange(48..<52, with: x12.bigEndian.bytes())
+ block.replaceSubrange(52..<56, with: x13.bigEndian.bytes())
+ block.replaceSubrange(56..<60, with: x14.bigEndian.bytes())
+ block.replaceSubrange(60..<64, with: x15.bigEndian.bytes())
+ }
+
+ // XORKeyStream
+ func process(bytes: ArraySlice<UInt8>, counter: inout Array<UInt8>, key: Array<UInt8>) -> Array<UInt8> {
+ precondition(counter.count == 16)
+ precondition(key.count == 32)
+
+ var block = Array<UInt8>(repeating: 0, count: ChaCha20.blockSize)
+ var bytesSlice = bytes
+ var out = Array<UInt8>(reserveCapacity: bytesSlice.count)
+
+ while bytesSlice.count >= ChaCha20.blockSize {
+ self.core(block: &block, counter: counter, key: key)
+ for (i, x) in block.enumerated() {
+ out.append(bytesSlice[bytesSlice.startIndex + i] ^ x)
+ }
+ var u: UInt32 = 1
+ for i in 0..<4 {
+ u += UInt32(counter[i])
+ counter[i] = UInt8(u & 0xff)
+ u >>= 8
+ }
+ bytesSlice = bytesSlice[bytesSlice.startIndex + ChaCha20.blockSize..<bytesSlice.endIndex]
+ }
+
+ if !bytesSlice.isEmpty {
+ self.core(block: &block, counter: counter, key: key)
+ for (i, v) in bytesSlice.enumerated() {
+ out.append(v ^ block[i])
+ }
+ }
+ return out
+ }
+}
+
+// MARK: Cipher
+
+extension ChaCha20: Cipher {
+ public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ self.process(bytes: bytes, counter: &self.counter, key: Array(self.key))
+ }
+
+ public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ try self.encrypt(bytes)
+ }
+}
+
+// MARK: Encryptor
+
+extension ChaCha20 {
+ public struct ChaChaEncryptor: Cryptor, Updatable {
+ private var accumulated = Array<UInt8>()
+ private let chacha: ChaCha20
+
+ init(chacha: ChaCha20) {
+ self.chacha = chacha
+ }
+
+ public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ var encrypted = Array<UInt8>()
+ encrypted.reserveCapacity(self.accumulated.count)
+ for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
+ if isLast || self.accumulated.count >= ChaCha20.blockSize {
+ encrypted += try self.chacha.encrypt(chunk)
+ self.accumulated.removeFirst(chunk.count) // TODO: improve performance
+ }
+ }
+ return encrypted
+ }
+
+ public func seek(to: Int) throws {
+ throw Error.notSupported
+ }
+ }
+}
+
+// MARK: Decryptor
+
+extension ChaCha20 {
+ public struct ChaChaDecryptor: Cryptor, Updatable {
+ private var accumulated = Array<UInt8>()
+
+ private var offset: Int = 0
+ private var offsetToRemove: Int = 0
+ private let chacha: ChaCha20
+
+ init(chacha: ChaCha20) {
+ self.chacha = chacha
+ }
+
+ public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = true) throws -> Array<UInt8> {
+ // prepend "offset" number of bytes at the beginning
+ if self.offset > 0 {
+ self.accumulated += Array<UInt8>(repeating: 0, count: self.offset) + bytes
+ self.offsetToRemove = self.offset
+ self.offset = 0
+ } else {
+ self.accumulated += bytes
+ }
+
+ var plaintext = Array<UInt8>()
+ plaintext.reserveCapacity(self.accumulated.count)
+ for chunk in self.accumulated.batched(by: ChaCha20.blockSize) {
+ if isLast || self.accumulated.count >= ChaCha20.blockSize {
+ plaintext += try self.chacha.decrypt(chunk)
+
+ // remove "offset" from the beginning of first chunk
+ if self.offsetToRemove > 0 {
+ plaintext.removeFirst(self.offsetToRemove) // TODO: improve performance
+ self.offsetToRemove = 0
+ }
+
+ self.accumulated.removeFirst(chunk.count)
+ }
+ }
+
+ return plaintext
+ }
+
+ public func seek(to: Int) throws {
+ throw Error.notSupported
+ }
+ }
+}
+
+// MARK: Cryptors
+
+extension ChaCha20: Cryptors {
+ //TODO: Use BlockEncryptor/BlockDecryptor
+
+ public func makeEncryptor() -> Cryptor & Updatable {
+ ChaCha20.ChaChaEncryptor(chacha: self)
+ }
+
+ public func makeDecryptor() -> Cryptor & Updatable {
+ ChaCha20.ChaChaDecryptor(chacha: self)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// CRC - cyclic redundancy check code.
+public final class Checksum {
+
+ @usableFromInline
+ static let table32: Array<UInt32> = [
+ 0x0000_0000, 0x7707_3096, 0xEE0E_612C, 0x9909_51BA, 0x076D_C419, 0x706A_F48F, 0xE963_A535, 0x9E64_95A3,
+ 0x0EDB_8832, 0x79DC_B8A4, 0xE0D5_E91E, 0x97D2_D988, 0x09B6_4C2B, 0x7EB1_7CBD, 0xE7B8_2D07, 0x90BF_1D91,
+ 0x1DB7_1064, 0x6AB0_20F2, 0xF3B9_7148, 0x84BE_41DE, 0x1ADA_D47D, 0x6DDD_E4EB, 0xF4D4_B551, 0x83D3_85C7,
+ 0x136C_9856, 0x646B_A8C0, 0xFD62_F97A, 0x8A65_C9EC, 0x1401_5C4F, 0x6306_6CD9, 0xFA0F_3D63, 0x8D08_0DF5,
+ 0x3B6E_20C8, 0x4C69_105E, 0xD560_41E4, 0xA267_7172, 0x3C03_E4D1, 0x4B04_D447, 0xD20D_85FD, 0xA50A_B56B,
+ 0x35B5_A8FA, 0x42B2_986C, 0xDBBB_C9D6, 0xACBC_F940, 0x32D8_6CE3, 0x45DF_5C75, 0xDCD6_0DCF, 0xABD1_3D59,
+ 0x26D9_30AC, 0x51DE_003A, 0xC8D7_5180, 0xBFD0_6116, 0x21B4_F4B5, 0x56B3_C423, 0xCFBA_9599, 0xB8BD_A50F,
+ 0x2802_B89E, 0x5F05_8808, 0xC60C_D9B2, 0xB10B_E924, 0x2F6F_7C87, 0x5868_4C11, 0xC161_1DAB, 0xB666_2D3D,
+ 0x76DC_4190, 0x01DB_7106, 0x98D2_20BC, 0xEFD5_102A, 0x71B1_8589, 0x06B6_B51F, 0x9FBF_E4A5, 0xE8B8_D433,
+ 0x7807_C9A2, 0x0F00_F934, 0x9609_A88E, 0xE10E_9818, 0x7F6A_0DBB, 0x086D_3D2D, 0x9164_6C97, 0xE663_5C01,
+ 0x6B6B_51F4, 0x1C6C_6162, 0x8565_30D8, 0xF262_004E, 0x6C06_95ED, 0x1B01_A57B, 0x8208_F4C1, 0xF50F_C457,
+ 0x65B0_D9C6, 0x12B7_E950, 0x8BBE_B8EA, 0xFCB9_887C, 0x62DD_1DDF, 0x15DA_2D49, 0x8CD3_7CF3, 0xFBD4_4C65,
+ 0x4DB2_6158, 0x3AB5_51CE, 0xA3BC_0074, 0xD4BB_30E2, 0x4ADF_A541, 0x3DD8_95D7, 0xA4D1_C46D, 0xD3D6_F4FB,
+ 0x4369_E96A, 0x346E_D9FC, 0xAD67_8846, 0xDA60_B8D0, 0x4404_2D73, 0x3303_1DE5, 0xAA0A_4C5F, 0xDD0D_7CC9,
+ 0x5005_713C, 0x2702_41AA, 0xBE0B_1010, 0xC90C_2086, 0x5768_B525, 0x206F_85B3, 0xB966_D409, 0xCE61_E49F,
+ 0x5EDE_F90E, 0x29D9_C998, 0xB0D0_9822, 0xC7D7_A8B4, 0x59B3_3D17, 0x2EB4_0D81, 0xB7BD_5C3B, 0xC0BA_6CAD,
+ 0xEDB8_8320, 0x9ABF_B3B6, 0x03B6_E20C, 0x74B1_D29A, 0xEAD5_4739, 0x9DD2_77AF, 0x04DB_2615, 0x73DC_1683,
+ 0xE363_0B12, 0x9464_3B84, 0x0D6D_6A3E, 0x7A6A_5AA8, 0xE40E_CF0B, 0x9309_FF9D, 0x0A00_AE27, 0x7D07_9EB1,
+ 0xF00F_9344, 0x8708_A3D2, 0x1E01_F268, 0x6906_C2FE, 0xF762_575D, 0x8065_67CB, 0x196C_3671, 0x6E6B_06E7,
+ 0xFED4_1B76, 0x89D3_2BE0, 0x10DA_7A5A, 0x67DD_4ACC, 0xF9B9_DF6F, 0x8EBE_EFF9, 0x17B7_BE43, 0x60B0_8ED5,
+ 0xD6D6_A3E8, 0xA1D1_937E, 0x38D8_C2C4, 0x4FDF_F252, 0xD1BB_67F1, 0xA6BC_5767, 0x3FB5_06DD, 0x48B2_364B,
+ 0xD80D_2BDA, 0xAF0A_1B4C, 0x3603_4AF6, 0x4104_7A60, 0xDF60_EFC3, 0xA867_DF55, 0x316E_8EEF, 0x4669_BE79,
+ 0xCB61_B38C, 0xBC66_831A, 0x256F_D2A0, 0x5268_E236, 0xCC0C_7795, 0xBB0B_4703, 0x2202_16B9, 0x5505_262F,
+ 0xC5BA_3BBE, 0xB2BD_0B28, 0x2BB4_5A92, 0x5CB3_6A04, 0xC2D7_FFA7, 0xB5D0_CF31, 0x2CD9_9E8B, 0x5BDE_AE1D,
+ 0x9B64_C2B0, 0xEC63_F226, 0x756A_A39C, 0x026D_930A, 0x9C09_06A9, 0xEB0E_363F, 0x7207_6785, 0x0500_5713,
+ 0x95BF_4A82, 0xE2B8_7A14, 0x7BB1_2BAE, 0x0CB6_1B38, 0x92D2_8E9B, 0xE5D5_BE0D, 0x7CDC_EFB7, 0x0BDB_DF21,
+ 0x86D3_D2D4, 0xF1D4_E242, 0x68DD_B3F8, 0x1FDA_836E, 0x81BE_16CD, 0xF6B9_265B, 0x6FB0_77E1, 0x18B7_4777,
+ 0x8808_5AE6, 0xFF0F_6A70, 0x6606_3BCA, 0x1101_0B5C, 0x8F65_9EFF, 0xF862_AE69, 0x616B_FFD3, 0x166C_CF45,
+ 0xA00A_E278, 0xD70D_D2EE, 0x4E04_8354, 0x3903_B3C2, 0xA767_2661, 0xD060_16F7, 0x4969_474D, 0x3E6E_77DB,
+ 0xAED1_6A4A, 0xD9D6_5ADC, 0x40DF_0B66, 0x37D8_3BF0, 0xA9BC_AE53, 0xDEBB_9EC5, 0x47B2_CF7F, 0x30B5_FFE9,
+ 0xBDBD_F21C, 0xCABA_C28A, 0x53B3_9330, 0x24B4_A3A6, 0xBAD0_3605, 0xCDD7_0693, 0x54DE_5729, 0x23D9_67BF,
+ 0xB366_7A2E, 0xC461_4AB8, 0x5D68_1B02, 0x2A6F_2B94, 0xB40B_BE37, 0xC30C_8EA1, 0x5A05_DF1B, 0x2D02_EF8D
+ ]
+
+ @usableFromInline
+ static let table32c: Array<UInt32> = [
+ 0x0000_0000, 0xF26B_8303, 0xE13B_70F7, 0x1350_F3F4, 0xC79A_971F, 0x35F1_141C, 0x26A1_E7E8, 0xD4CA_64EB,
+ 0x8AD9_58CF, 0x78B2_DBCC, 0x6BE2_2838, 0x9989_AB3B, 0x4D43_CFD0, 0xBF28_4CD3, 0xAC78_BF27, 0x5E13_3C24,
+ 0x105E_C76F, 0xE235_446C, 0xF165_B798, 0x030E_349B, 0xD7C4_5070, 0x25AF_D373, 0x36FF_2087, 0xC494_A384,
+ 0x9A87_9FA0, 0x68EC_1CA3, 0x7BBC_EF57, 0x89D7_6C54, 0x5D1D_08BF, 0xAF76_8BBC, 0xBC26_7848, 0x4E4D_FB4B,
+ 0x20BD_8EDE, 0xD2D6_0DDD, 0xC186_FE29, 0x33ED_7D2A, 0xE727_19C1, 0x154C_9AC2, 0x061C_6936, 0xF477_EA35,
+ 0xAA64_D611, 0x580F_5512, 0x4B5F_A6E6, 0xB934_25E5, 0x6DFE_410E, 0x9F95_C20D, 0x8CC5_31F9, 0x7EAE_B2FA,
+ 0x30E3_49B1, 0xC288_CAB2, 0xD1D8_3946, 0x23B3_BA45, 0xF779_DEAE, 0x0512_5DAD, 0x1642_AE59, 0xE429_2D5A,
+ 0xBA3A_117E, 0x4851_927D, 0x5B01_6189, 0xA96A_E28A, 0x7DA0_8661, 0x8FCB_0562, 0x9C9B_F696, 0x6EF0_7595,
+ 0x417B_1DBC, 0xB310_9EBF, 0xA040_6D4B, 0x522B_EE48, 0x86E1_8AA3, 0x748A_09A0, 0x67DA_FA54, 0x95B1_7957,
+ 0xCBA2_4573, 0x39C9_C670, 0x2A99_3584, 0xD8F2_B687, 0x0C38_D26C, 0xFE53_516F, 0xED03_A29B, 0x1F68_2198,
+ 0x5125_DAD3, 0xA34E_59D0, 0xB01E_AA24, 0x4275_2927, 0x96BF_4DCC, 0x64D4_CECF, 0x7784_3D3B, 0x85EF_BE38,
+ 0xDBFC_821C, 0x2997_011F, 0x3AC7_F2EB, 0xC8AC_71E8, 0x1C66_1503, 0xEE0D_9600, 0xFD5D_65F4, 0x0F36_E6F7,
+ 0x61C6_9362, 0x93AD_1061, 0x80FD_E395, 0x7296_6096, 0xA65C_047D, 0x5437_877E, 0x4767_748A, 0xB50C_F789,
+ 0xEB1F_CBAD, 0x1974_48AE, 0x0A24_BB5A, 0xF84F_3859, 0x2C85_5CB2, 0xDEEE_DFB1, 0xCDBE_2C45, 0x3FD5_AF46,
+ 0x7198_540D, 0x83F3_D70E, 0x90A3_24FA, 0x62C8_A7F9, 0xB602_C312, 0x4469_4011, 0x5739_B3E5, 0xA552_30E6,
+ 0xFB41_0CC2, 0x092A_8FC1, 0x1A7A_7C35, 0xE811_FF36, 0x3CDB_9BDD, 0xCEB0_18DE, 0xDDE0_EB2A, 0x2F8B_6829,
+ 0x82F6_3B78, 0x709D_B87B, 0x63CD_4B8F, 0x91A6_C88C, 0x456C_AC67, 0xB707_2F64, 0xA457_DC90, 0x563C_5F93,
+ 0x082F_63B7, 0xFA44_E0B4, 0xE914_1340, 0x1B7F_9043, 0xCFB5_F4A8, 0x3DDE_77AB, 0x2E8E_845F, 0xDCE5_075C,
+ 0x92A8_FC17, 0x60C3_7F14, 0x7393_8CE0, 0x81F8_0FE3, 0x5532_6B08, 0xA759_E80B, 0xB409_1BFF, 0x4662_98FC,
+ 0x1871_A4D8, 0xEA1A_27DB, 0xF94A_D42F, 0x0B21_572C, 0xDFEB_33C7, 0x2D80_B0C4, 0x3ED0_4330, 0xCCBB_C033,
+ 0xA24B_B5A6, 0x5020_36A5, 0x4370_C551, 0xB11B_4652, 0x65D1_22B9, 0x97BA_A1BA, 0x84EA_524E, 0x7681_D14D,
+ 0x2892_ED69, 0xDAF9_6E6A, 0xC9A9_9D9E, 0x3BC2_1E9D, 0xEF08_7A76, 0x1D63_F975, 0x0E33_0A81, 0xFC58_8982,
+ 0xB215_72C9, 0x407E_F1CA, 0x532E_023E, 0xA145_813D, 0x758F_E5D6, 0x87E4_66D5, 0x94B4_9521, 0x66DF_1622,
+ 0x38CC_2A06, 0xCAA7_A905, 0xD9F7_5AF1, 0x2B9C_D9F2, 0xFF56_BD19, 0x0D3D_3E1A, 0x1E6D_CDEE, 0xEC06_4EED,
+ 0xC38D_26C4, 0x31E6_A5C7, 0x22B6_5633, 0xD0DD_D530, 0x0417_B1DB, 0xF67C_32D8, 0xE52C_C12C, 0x1747_422F,
+ 0x4954_7E0B, 0xBB3F_FD08, 0xA86F_0EFC, 0x5A04_8DFF, 0x8ECE_E914, 0x7CA5_6A17, 0x6FF5_99E3, 0x9D9E_1AE0,
+ 0xD3D3_E1AB, 0x21B8_62A8, 0x32E8_915C, 0xC083_125F, 0x1449_76B4, 0xE622_F5B7, 0xF572_0643, 0x0719_8540,
+ 0x590A_B964, 0xAB61_3A67, 0xB831_C993, 0x4A5A_4A90, 0x9E90_2E7B, 0x6CFB_AD78, 0x7FAB_5E8C, 0x8DC0_DD8F,
+ 0xE330_A81A, 0x115B_2B19, 0x020B_D8ED, 0xF060_5BEE, 0x24AA_3F05, 0xD6C1_BC06, 0xC591_4FF2, 0x37FA_CCF1,
+ 0x69E9_F0D5, 0x9B82_73D6, 0x88D2_8022, 0x7AB9_0321, 0xAE73_67CA, 0x5C18_E4C9, 0x4F48_173D, 0xBD23_943E,
+ 0xF36E_6F75, 0x0105_EC76, 0x1255_1F82, 0xE03E_9C81, 0x34F4_F86A, 0xC69F_7B69, 0xD5CF_889D, 0x27A4_0B9E,
+ 0x79B7_37BA, 0x8BDC_B4B9, 0x988C_474D, 0x6AE7_C44E, 0xBE2D_A0A5, 0x4C46_23A6, 0x5F16_D052, 0xAD7D_5351
+ ]
+
+ @usableFromInline
+ static let table16: Array<UInt16> = [
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+ ]
+
+ @usableFromInline
+ init() {
+ //
+ }
+
+ /// Polynomial: 0xEDB88320 (Reversed) - IEEE
+ @inlinable
+ func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF
+ for chunk in message.batched(by: 256) {
+ for b in chunk {
+ let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF)
+ crc = (crc >> 8) ^ Checksum.table32[idx]
+ }
+ }
+ return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF
+ }
+
+ /// Polynomial: 0x82F63B78 (Reversed) - Castagnoli
+ @inlinable
+ func crc32c(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF
+ for chunk in message.batched(by: 256) {
+ for b in chunk {
+ let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF)
+ crc = (crc >> 8) ^ Checksum.table32c[idx]
+ }
+ }
+ return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF
+ }
+
+ /// Polynomial: 0xA001 (Reversed) - IBM
+ @inlinable
+ func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
+ var crc: UInt16 = seed != nil ? seed! : 0x0000
+ for chunk in message.batched(by: 256) {
+ for b in chunk {
+ crc = (crc >> 8) ^ Checksum.table16[Int((crc ^ UInt16(b)) & 0xFF)]
+ }
+ }
+ return crc
+ }
+}
+
+// MARK: Public interface
+
+public extension Checksum {
+ /// Calculate CRC32.
+ ///
+ /// - parameter message: Message
+ /// - parameter seed: Seed value (Optional)
+ /// - parameter reflect: is reflect (default true)
+ ///
+ /// - returns: Calculated code
+ @inlinable
+ static func crc32(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ Checksum().crc32(message, seed: seed, reflect: reflect)
+ }
+
+ /// Calculate CRC32C
+ ///
+ /// - parameter message: Message
+ /// - parameter seed: Seed value (Optional)
+ /// - parameter reflect: is reflect (default true)
+ ///
+ /// - returns: Calculated code
+ @inlinable
+ static func crc32c(_ message: Array<UInt8>, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 {
+ Checksum().crc32c(message, seed: seed, reflect: reflect)
+ }
+
+ /// Calculate CRC16
+ ///
+ /// - parameter message: Message
+ /// - parameter seed: Seed value (Optional)
+ ///
+ /// - returns: Calculated code
+ @inlinable
+ static func crc16(_ message: Array<UInt8>, seed: UInt16? = nil) -> UInt16 {
+ Checksum().crc16(message, seed: seed)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public enum CipherError: Error {
+ case encrypt
+ case decrypt
+}
+
+public protocol Cipher: AnyObject {
+ var keySize: Int { get }
+
+ /// Encrypt given bytes at once
+ ///
+ /// - parameter bytes: Plaintext data
+ /// - returns: Encrypted data
+ func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
+ func encrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8>
+
+ /// Decrypt given bytes at once
+ ///
+ /// - parameter bytes: Ciphertext data
+ /// - returns: Plaintext data
+ func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
+ func decrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8>
+}
+
+extension Cipher {
+ public func encrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ try self.encrypt(bytes.slice)
+ }
+
+ public func decrypt(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ try self.decrypt(bytes.slice)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public protocol CipherModeWorker {
+ var cipherOperation: CipherOperationOnBlock { get }
+
+ // Additional space needed when incrementally process data
+ // eg. for GCM combined mode
+ var additionalBufferSize: Int { get }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8>
+
+ @inlinable
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8>
+}
+
+/// Block workers use `BlockEncryptor`
+public protocol BlockModeWorker: CipherModeWorker {
+ var blockSize: Int { get }
+}
+
+public protocol CounterModeWorker: CipherModeWorker {
+ associatedtype Counter
+ var counter: Counter { get set }
+}
+
+public protocol SeekableModeWorker: CipherModeWorker {
+ mutating func seek(to position: Int) throws
+}
+
+/// Stream workers use `StreamEncryptor`
+public protocol StreamModeWorker: CipherModeWorker {
+}
+
+public protocol FinalizingEncryptModeWorker: CipherModeWorker {
+ // Any final calculations, eg. calculate tag
+ // Called after the last block is encrypted
+ mutating func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
+}
+
+public protocol FinalizingDecryptModeWorker: CipherModeWorker {
+ // Called before decryption, hence input is ciphertext.
+ // ciphertext is either a last block, or a tag (for stream workers)
+ @discardableResult
+ mutating func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
+ // Called after decryption, hence input is ciphertext
+ mutating func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
+ // Any final calculations, eg. calculate tag
+ // Called after the last block is encrypted
+ mutating func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8>
+}
--- /dev/null
+//
+// Codable.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2017-8-11.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS {
+
+ // Little-endian to big-endian
+ struct Units<Unit: FixedWidthInteger, Words: RandomAccessCollection>: RandomAccessCollection
+ where Words.Element: FixedWidthInteger, Words.Index == Int {
+ typealias Word = Words.Element
+ let words: Words
+ init(of type: Unit.Type, _ words: Words) {
+ precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
+ self.words = words
+ }
+ var count: Int { return (words.count * Word.bitWidth + Unit.bitWidth - 1) / Unit.bitWidth }
+ var startIndex: Int { return 0 }
+ var endIndex: Int { return count }
+ subscript(_ index: Int) -> Unit {
+ let index = count - 1 - index
+ if Unit.bitWidth == Word.bitWidth {
+ return Unit(words[index])
+ }
+ else if Unit.bitWidth > Word.bitWidth {
+ let c = Unit.bitWidth / Word.bitWidth
+ var unit: Unit = 0
+ var j = 0
+ for i in (c * index) ..< Swift.min(c * (index + 1), words.endIndex) {
+ unit |= Unit(words[i]) << j
+ j += Word.bitWidth
+ }
+ return unit
+ }
+ // Unit.bitWidth < Word.bitWidth
+ let c = Word.bitWidth / Unit.bitWidth
+ let i = index / c
+ let j = index % c
+ return Unit(truncatingIfNeeded: words[i] >> (j * Unit.bitWidth))
+ }
+ }
+}
+
+extension Array where Element: FixedWidthInteger {
+ // Big-endian to little-endian
+ init<Unit: FixedWidthInteger>(count: Int?, generator: () throws -> Unit?) rethrows {
+ typealias Word = Element
+ precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
+ self = []
+ if Unit.bitWidth == Word.bitWidth {
+ if let count = count {
+ self.reserveCapacity(count)
+ }
+ while let unit = try generator() {
+ self.append(Word(unit))
+ }
+ }
+ else if Unit.bitWidth > Word.bitWidth {
+ let wordsPerUnit = Unit.bitWidth / Word.bitWidth
+ if let count = count {
+ self.reserveCapacity(count * wordsPerUnit)
+ }
+ while let unit = try generator() {
+ var shift = Unit.bitWidth - Word.bitWidth
+ while shift >= 0 {
+ self.append(Word(truncatingIfNeeded: unit >> shift))
+ shift -= Word.bitWidth
+ }
+ }
+ }
+ else {
+ let unitsPerWord = Word.bitWidth / Unit.bitWidth
+ if let count = count {
+ self.reserveCapacity((count + unitsPerWord - 1) / unitsPerWord)
+ }
+ var word: Word = 0
+ var c = 0
+ while let unit = try generator() {
+ word <<= Unit.bitWidth
+ word |= Word(unit)
+ c += Unit.bitWidth
+ if c == Word.bitWidth {
+ self.append(word)
+ word = 0
+ c = 0
+ }
+ }
+ if c > 0 {
+ self.append(word << c)
+ var shifted: Word = 0
+ for i in self.indices {
+ let word = self[i]
+ self[i] = shifted | (word >> c)
+ shifted = word << (Word.bitWidth - c)
+ }
+ }
+ }
+ self.reverse()
+ }
+}
+
+extension CS.BigInt: Codable {
+ public init(from decoder: Decoder) throws {
+ var container = try decoder.unkeyedContainer()
+
+ // Decode sign
+ let sign: CS.BigInt.Sign
+ switch try container.decode(String.self) {
+ case "+":
+ sign = .plus
+ case "-":
+ sign = .minus
+ default:
+ throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath,
+ debugDescription: "Invalid big integer sign"))
+ }
+
+ // Decode magnitude
+ let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in
+ guard !container.isAtEnd else { return nil }
+ return try container.decode(UInt64.self)
+ }
+ let magnitude = CS.BigUInt(words: words)
+
+ self.init(sign: sign, magnitude: magnitude)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.unkeyedContainer()
+ try container.encode(sign == .plus ? "+" : "-")
+ let units = CS.Units(of: UInt64.self, self.magnitude.words)
+ if units.isEmpty {
+ try container.encode(0 as UInt64)
+ }
+ else {
+ try container.encode(contentsOf: units)
+ }
+ }
+}
+
+extension CS.BigUInt: Codable {
+ public init(from decoder: Decoder) throws {
+ let value = try CS.BigInt(from: decoder)
+ guard value.sign == .plus else {
+ throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
+ debugDescription: "BigUInt cannot hold a negative value"))
+ }
+ self = value.magnitude
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ try CS.BigInt(sign: .plus, magnitude: self).encode(to: encoder)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+extension Collection where Self.Element == UInt8, Self.Index == Int {
+ // Big endian order
+ @inlinable
+ func toUInt32Array() -> Array<UInt32> {
+ guard !isEmpty else {
+ return []
+ }
+
+ let c = strideCount(from: startIndex, to: endIndex, by: 4)
+ return Array<UInt32>(unsafeUninitializedCapacity: c) { buf, count in
+ var counter = 0
+ for idx in stride(from: startIndex, to: endIndex, by: 4) {
+ let val = UInt32(bytes: self, fromIndex: idx).bigEndian
+ buf[counter] = val
+ counter += 1
+ }
+ count = counter
+ assert(counter == c)
+ }
+ }
+
+ // Big endian order
+ @inlinable
+ func toUInt64Array() -> Array<UInt64> {
+ guard !isEmpty else {
+ return []
+ }
+
+ let c = strideCount(from: startIndex, to: endIndex, by: 8)
+ return Array<UInt64>(unsafeUninitializedCapacity: c) { buf, count in
+ var counter = 0
+ for idx in stride(from: startIndex, to: endIndex, by: 8) {
+ let val = UInt64(bytes: self, fromIndex: idx).bigEndian
+ buf[counter] = val
+ counter += 1
+ }
+ count = counter
+ assert(counter == c)
+ }
+ }
+}
+
+@usableFromInline
+func strideCount(from: Int, to: Int, by: Int) -> Int {
+ let count = to - from
+ return count / by + (count % by > 0 ? 1 : 0)
+}
--- /dev/null
+//
+// Comparable.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+import Foundation
+
+extension CS.BigUInt: Comparable {
+ //MARK: Comparison
+
+ /// Compare `a` to `b` and return an `NSComparisonResult` indicating their order.
+ ///
+ /// - Complexity: O(count)
+ public static func compare(_ a: CS.BigUInt, _ b: CS.BigUInt) -> ComparisonResult {
+ if a.count != b.count { return a.count > b.count ? .orderedDescending : .orderedAscending }
+ for i in (0 ..< a.count).reversed() {
+ let ad = a[i]
+ let bd = b[i]
+ if ad != bd { return ad > bd ? .orderedDescending : .orderedAscending }
+ }
+ return .orderedSame
+ }
+
+ /// Return true iff `a` is equal to `b`.
+ ///
+ /// - Complexity: O(count)
+ public static func ==(a: CS.BigUInt, b: CS.BigUInt) -> Bool {
+ return CS.BigUInt.compare(a, b) == .orderedSame
+ }
+
+ /// Return true iff `a` is less than `b`.
+ ///
+ /// - Complexity: O(count)
+ public static func <(a: CS.BigUInt, b: CS.BigUInt) -> Bool {
+ return CS.BigUInt.compare(a, b) == .orderedAscending
+ }
+}
+
+extension CS.BigInt {
+ /// Return true iff `a` is equal to `b`.
+ public static func ==(a: CS.BigInt, b: CS.BigInt) -> Bool {
+ return a.sign == b.sign && a.magnitude == b.magnitude
+ }
+
+ /// Return true iff `a` is less than `b`.
+ public static func <(a: CS.BigInt, b: CS.BigInt) -> Bool {
+ switch (a.sign, b.sign) {
+ case (.plus, .plus):
+ return a.magnitude < b.magnitude
+ case (.plus, .minus):
+ return false
+ case (.minus, .plus):
+ return true
+ case (.minus, .minus):
+ return a.magnitude > b.magnitude
+ }
+ }
+}
+
+
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// Cryptor (Encryptor or Decryptor)
+public protocol Cryptor {
+ /// Seek to position in file, if block mode allows random access.
+ ///
+ /// - parameter to: new value of counter
+ mutating func seek(to: Int) throws
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#endif
+
+/// Worker cryptor/decryptor of `Updatable` types
+public protocol Cryptors: AnyObject {
+
+ /// Cryptor suitable for encryption
+ func makeEncryptor() throws -> Cryptor & Updatable
+
+ /// Cryptor suitable for decryption
+ func makeDecryptor() throws -> Cryptor & Updatable
+
+ /// Generate array of random bytes. Helper function.
+ static func randomIV(_ blockSize: Int) -> Array<UInt8>
+}
+
+extension Cryptors {
+ /// Generate array of random values.
+ /// Convenience helper that uses `Swift.RandomNumberGenerator`.
+ /// - Parameter count: Length of array
+ public static func randomIV(_ count: Int) -> Array<UInt8> {
+ (0..<count).map({ _ in UInt8.random(in: 0...UInt8.max) })
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+/// Conform to this protocol if your type can both be instantiated and expressed as an ASN1 DER representation.
+internal protocol DERCodable: DERDecodable, DEREncodable { }
+
+/// Conform to this protocol if your type can be instantiated from a ASN1 DER representation
+internal protocol DERDecodable {
+ /// Attempts to instantiate an instance of your Public CookiesKey when given a DER representation of your Public CookiesKey
+ ///
+ /// - Parameter publicDER: The ASN.1 DER representation of your Public CookiesKey
+ init(publicDER: Array<UInt8>) throws
+
+ /// Attempts to instantiate an instance of your Private CookiesKey when given a DER representation of your Private CookiesKey
+ ///
+ /// - Parameter privateDER: The ASN.1 DER representation of your Private CookiesKey
+ init(privateDER: Array<UInt8>) throws
+
+ /// Attempts to instantiate a CookiesKey when given the ASN1 DER encoded external representation of the CookiesKey
+ ///
+ /// - Parameter rawRepresentation: The ASN1 DER Encoded external representation (either public or private)
+ /// - Note: The external representation is identical to that of `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework
+ init(rawRepresentation: Data) throws
+}
+
+/// Conform to this protocol if your type can be described in an ASN1 DER representation
+internal protocol DEREncodable {
+ /// Returns the DER encoded representation of the Public CookiesKey
+ func publicKeyDER() throws -> Array<UInt8>
+
+ /// Returns the DER encoded representation of the Private CookiesKey
+ func privateKeyDER() throws -> Array<UInt8>
+
+ /// A semantically similar function that mimics the `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework
+ /// - Note: If called on a Private CookiesKey, this method will return the Private Keys DER Representation. Likewise, if called on a Public CookiesKey, this method will return the Public Keys DER Representation
+ /// - Note: If you'd like to export the Public Keys DER from a Private CookiesKey, use the `publicKeyExternalRepresentation()` function
+ func externalRepresentation() throws -> Data
+
+ /// A semantically similar function that mimics the `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework
+ /// - Note: This function only ever exports the Public Key's DER representation. If called on a Private CookiesKey, the corresponding Public CookiesKey will be extracted and exported.
+ func publicKeyExternalRepresentation() throws -> Data
+}
+
+struct DER {
+ internal enum Error: Swift.Error {
+ /// We were provided invalid DER data
+ case invalidDERFormat
+ }
+
+ /// Integer to Octet String Primitive
+ /// - Parameters:
+ /// - x: nonnegative integer to be converted
+ /// - size: intended length of the resulting octet string
+ /// - Returns: corresponding octet string of length xLen
+ /// - Note: https://datatracker.ietf.org/doc/html/rfc3447#section-4.1
+ internal static func i2osp(x: [UInt8], size: Int) -> [UInt8] {
+ var modulus = x
+ while modulus.count < size {
+ modulus.insert(0x00, at: 0)
+ }
+ if modulus[0] >= 0x80 {
+ modulus.insert(0x00, at: 0)
+ }
+ return modulus
+ }
+
+ /// Integer to Octet String Primitive
+ /// - Parameters:
+ /// - x: nonnegative integer to be converted
+ /// - size: intended length of the resulting octet string
+ /// - Returns: corresponding octet string of length xLen
+ /// - Note: https://datatracker.ietf.org/doc/html/rfc3447#section-4.1
+ internal static func i2ospData(x: [UInt8], size: Int) -> Data {
+ return Data(DER.i2osp(x: x, size: size))
+ }
+}
--- /dev/null
+//
+// Data Conversion.swift
+// BigInt
+//
+// Created by Károly Lőrentey on 2016-01-04.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+import Foundation
+
+extension CS.BigUInt {
+ //MARK: NSData Conversion
+
+ /// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer
+ public init(_ buffer: UnsafeRawBufferPointer) {
+ // This assumes Word is binary.
+ precondition(Word.bitWidth % 8 == 0)
+
+ self.init()
+
+ let length = buffer.count
+ guard length > 0 else { return }
+ let bytesPerDigit = Word.bitWidth / 8
+ var index = length / bytesPerDigit
+ var c = bytesPerDigit - length % bytesPerDigit
+ if c == bytesPerDigit {
+ c = 0
+ index -= 1
+ }
+
+ var word: Word = 0
+ for byte in buffer {
+ word <<= 8
+ word += Word(byte)
+ c += 1
+ if c == bytesPerDigit {
+ self[index] = word
+ index -= 1
+ c = 0
+ word = 0
+ }
+ }
+ assert(c == 0 && word == 0 && index == -1)
+ }
+
+
+ /// Initializes an integer from the bits stored inside a piece of `Data`.
+ /// The data is assumed to be in network (big-endian) byte order.
+ public init(_ data: Data) {
+ // This assumes Word is binary.
+ precondition(Word.bitWidth % 8 == 0)
+
+ self.init()
+
+ let length = data.count
+ guard length > 0 else { return }
+ let bytesPerDigit = Word.bitWidth / 8
+ var index = length / bytesPerDigit
+ var c = bytesPerDigit - length % bytesPerDigit
+ if c == bytesPerDigit {
+ c = 0
+ index -= 1
+ }
+ let word: Word = data.withUnsafeBytes { buffPtr in
+ var word: Word = 0
+ let p = buffPtr.bindMemory(to: UInt8.self)
+ for byte in p {
+ word <<= 8
+ word += Word(byte)
+ c += 1
+ if c == bytesPerDigit {
+ self[index] = word
+ index -= 1
+ c = 0
+ word = 0
+ }
+ }
+ return word
+ }
+ assert(c == 0 && word == 0 && index == -1)
+ }
+
+ /// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order.
+ public func serialize() -> Data {
+ // This assumes Digit is binary.
+ precondition(Word.bitWidth % 8 == 0)
+
+ let byteCount = (self.bitWidth + 7) / 8
+
+ guard byteCount > 0 else { return Data() }
+
+ var data = Data(count: byteCount)
+ data.withUnsafeMutableBytes { buffPtr in
+ let p = buffPtr.bindMemory(to: UInt8.self)
+ var i = byteCount - 1
+ for var word in self.words {
+ for _ in 0 ..< Word.bitWidth / 8 {
+ p[i] = UInt8(word & 0xFF)
+ word >>= 8
+ if i == 0 {
+ assert(word == 0)
+ break
+ }
+ i -= 1
+ }
+ }
+ }
+ return data
+ }
+}
+
+extension CS.BigInt {
+
+ /// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer,
+ /// where the first byte indicates sign (0 for positive, 1 for negative)
+ public init(_ buffer: UnsafeRawBufferPointer) {
+ // This assumes Word is binary.
+ precondition(Word.bitWidth % 8 == 0)
+
+ self.init()
+
+ let length = buffer.count
+
+ // Serialized data for a BigInt should contain at least 2 bytes: one representing
+ // the sign, and another for the non-zero magnitude. Zero is represented by an
+ // empty Data struct, and negative zero is not supported.
+ guard length > 1, let firstByte = buffer.first else { return }
+
+ // The first byte gives the sign
+ // This byte is compared to a bitmask to allow additional functionality to be added
+ // to this byte in the future.
+ self.sign = firstByte & 0b1 == 0 ? .plus : .minus
+
+ self.magnitude = CS.BigUInt(UnsafeRawBufferPointer(rebasing: buffer.dropFirst(1)))
+ }
+
+ /// Initializes an integer from the bits stored inside a piece of `Data`.
+ /// The data is assumed to be in network (big-endian) byte order with a first
+ /// byte to represent the sign (0 for positive, 1 for negative)
+ public init(_ data: Data) {
+ // This assumes Word is binary.
+ // This is the same assumption made when initializing CS.BigUInt from Data
+ precondition(Word.bitWidth % 8 == 0)
+
+ self.init()
+
+ // Serialized data for a BigInt should contain at least 2 bytes: one representing
+ // the sign, and another for the non-zero magnitude. Zero is represented by an
+ // empty Data struct, and negative zero is not supported.
+ guard data.count > 1, let firstByte = data.first else { return }
+
+ // The first byte gives the sign
+ // This byte is compared to a bitmask to allow additional functionality to be added
+ // to this byte in the future.
+ self.sign = firstByte & 0b1 == 0 ? .plus : .minus
+
+ // The remaining bytes are read and stored as the magnitude
+ self.magnitude = CS.BigUInt(data.dropFirst(1))
+ }
+
+ /// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order and a prepended byte to indicate the sign (0 for positive, 1 for negative)
+ public func serialize() -> Data {
+ // Create a data object for the magnitude portion of the BigInt
+ let magnitudeData = self.magnitude.serialize()
+
+ // Similar to CS.BigUInt, a value of 0 should return an initialized, empty Data struct
+ guard magnitudeData.count > 0 else { return magnitudeData }
+
+ // Create a new Data struct for the signed BigInt value
+ var data = Data(capacity: magnitudeData.count + 1)
+
+ // The first byte should be 0 for a positive value, or 1 for a negative value
+ // i.e., the sign bit is the LSB
+ data.append(self.sign == .plus ? 0 : 1)
+
+ data.append(magnitudeData)
+ return data
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension Data {
+ /// Two octet checksum as defined in RFC-4880. Sum of all octets, mod 65536
+ public func checksum() -> UInt16 {
+ let s = self.withUnsafeBytes { buf in
+ return buf.lazy.map(UInt32.init).reduce(UInt32(0), +)
+ }
+ return UInt16(s % 65535)
+ }
+
+ public func md5() -> Data {
+ Data( Digest.md5(bytes))
+ }
+
+ public func sha1() -> Data {
+ Data( Digest.sha1(bytes))
+ }
+
+ public func sha224() -> Data {
+ Data( Digest.sha224(bytes))
+ }
+
+ public func sha256() -> Data {
+ Data( Digest.sha256(bytes))
+ }
+
+ public func sha384() -> Data {
+ Data( Digest.sha384(bytes))
+ }
+
+ public func sha512() -> Data {
+ Data( Digest.sha512(bytes))
+ }
+
+ public func sha3(_ variant: SHA3.Variant) -> Data {
+ Data( Digest.sha3(bytes, variant: variant))
+ }
+
+ public func crc32(seed: UInt32? = nil, reflect: Bool = true) -> Data {
+ Data( Checksum.crc32(bytes, seed: seed, reflect: reflect).bytes())
+ }
+
+ public func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> Data {
+ Data( Checksum.crc32c(bytes, seed: seed, reflect: reflect).bytes())
+ }
+
+ public func crc16(seed: UInt16? = nil) -> Data {
+ Data( Checksum.crc16(bytes, seed: seed).bytes())
+ }
+
+ public func encrypt(cipher: Cipher) throws -> Data {
+ Data( try cipher.encrypt(bytes.slice))
+ }
+
+ public func decrypt(cipher: Cipher) throws -> Data {
+ Data( try cipher.decrypt(bytes.slice))
+ }
+
+ public func authenticate(with authenticator: Authenticator) throws -> Data {
+ Data( try authenticator.authenticate(bytes))
+ }
+}
+
+extension Data {
+ public init(hex: String) {
+ self.init(Array<UInt8>(hex: hex))
+ }
+
+ public var bytes: Array<UInt8> {
+ Array(self)
+ }
+
+ public func toHexString() -> String {
+ self.bytes.toHexString()
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@available(*, renamed: "Digest")
+public typealias Hash = Digest
+
+/// Hash functions to calculate Digest.
+public struct Digest {
+ /// Calculate MD5 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func md5(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ MD5().calculate(for: bytes)
+ }
+
+ /// Calculate SHA1 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func sha1(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ SHA1().calculate(for: bytes)
+ }
+
+ /// Calculate SHA2-224 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func sha224(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ self.sha2(bytes, variant: .sha224)
+ }
+
+ /// Calculate SHA2-256 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func sha256(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ self.sha2(bytes, variant: .sha256)
+ }
+
+ /// Calculate SHA2-384 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func sha384(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ self.sha2(bytes, variant: .sha384)
+ }
+
+ /// Calculate SHA2-512 Digest
+ /// - parameter bytes: input message
+ /// - returns: Digest bytes
+ public static func sha512(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ self.sha2(bytes, variant: .sha512)
+ }
+
+ /// Calculate SHA2 Digest
+ /// - parameter bytes: input message
+ /// - parameter variant: SHA-2 variant
+ /// - returns: Digest bytes
+ public static func sha2(_ bytes: Array<UInt8>, variant: SHA2.Variant) -> Array<UInt8> {
+ SHA2(variant: variant).calculate(for: bytes)
+ }
+
+ /// Calculate SHA3 Digest
+ /// - parameter bytes: input message
+ /// - parameter variant: SHA-3 variant
+ /// - returns: Digest bytes
+ public static func sha3(_ bytes: Array<UInt8>, variant: SHA3.Variant) -> Array<UInt8> {
+ SHA3(variant: variant).calculate(for: bytes)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+internal protocol DigestType {
+ func calculate(for bytes: Array<UInt8>) -> Array<UInt8>
+}
--- /dev/null
+//
+// Division.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+//MARK: Full-width multiplication and division
+
+// TODO: Return to `where Magnitude == Self` when SR-13491 is resolved
+extension FixedWidthInteger {
+ private var halfShift: Self {
+ return Self(Self.bitWidth / 2)
+
+ }
+ private var high: Self {
+ return self &>> halfShift
+ }
+
+ private var low: Self {
+ let mask: Self = 1 &<< halfShift - 1
+ return self & mask
+ }
+
+ private var upshifted: Self {
+ return self &<< halfShift
+ }
+
+ private var split: (high: Self, low: Self) {
+ return (self.high, self.low)
+ }
+
+ private init(_ value: (high: Self, low: Self)) {
+ self = value.high.upshifted + value.low
+ }
+
+ /// Divide the double-width integer `dividend` by `self` and return the quotient and remainder.
+ ///
+ /// - Requires: `dividend.high < self`, so that the result will fit in a single digit.
+ /// - Complexity: O(1) with 2 divisions, 6 multiplications and ~12 or so additions/subtractions.
+ internal func fastDividingFullWidth(_ dividend: (high: Self, low: Self.Magnitude)) -> (quotient: Self, remainder: Self) {
+ // Division is complicated; doing it with single-digit operations is maddeningly complicated.
+ // This is a Swift adaptation for "divlu2" in Hacker's Delight,
+ // which is in turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1).
+ precondition(dividend.high < self)
+
+ // This replaces the implementation in stdlib, which is much slower.
+ // FIXME: Speed up stdlib. It should use full-width idiv on Intel processors, and
+ // fall back to a reasonably fast algorithm elsewhere.
+
+ // The trick here is that we're actually implementing a 4/2 long division using half-words,
+ // with the long division loop unrolled into two 3/2 half-word divisions.
+ // Luckily, 3/2 half-word division can be approximated by a single full-word division operation
+ // that, when the divisor is normalized, differs from the correct result by at most 2.
+
+ /// Find the half-word quotient in `u / vn`, which must be normalized.
+ /// `u` contains three half-words in the two halves of `u.high` and the lower half of
+ /// `u.low`. (The weird distribution makes for a slightly better fit with the input.)
+ /// `vn` contains the normalized divisor, consisting of two half-words.
+ ///
+ /// - Requires: u.high < vn && u.low.high == 0 && vn.leadingZeroBitCount == 0
+ func quotient(dividing u: (high: Self, low: Self), by vn: Self) -> Self {
+ let (vn1, vn0) = vn.split
+ // Get approximate quotient.
+ let (q, r) = u.high.quotientAndRemainder(dividingBy: vn1)
+ let p = q * vn0
+ // q is often already correct, but sometimes the approximation overshoots by at most 2.
+ // The code that follows checks for this while being careful to only perform single-digit operations.
+ if q.high == 0 && p <= r.upshifted + u.low { return q }
+ let r2 = r + vn1
+ if r2.high != 0 { return q - 1 }
+ if (q - 1).high == 0 && p - vn0 <= r2.upshifted + u.low { return q - 1 }
+ //assert((r + 2 * vn1).high != 0 || p - 2 * vn0 <= (r + 2 * vn1).upshifted + u.low)
+ return q - 2
+ }
+ /// Divide 3 half-digits by 2 half-digits to get a half-digit quotient and a full-digit remainder.
+ ///
+ /// - Requires: u.high < v && u.low.high == 0 && vn.width = width(Digit)
+ func quotientAndRemainder(dividing u: (high: Self, low: Self), by v: Self) -> (quotient: Self, remainder: Self) {
+ let q = quotient(dividing: u, by: v)
+ // Note that `uh.low` masks off a couple of bits, and `q * v` and the
+ // subtraction are likely to overflow. Despite this, the end result (remainder) will
+ // still be correct and it will fit inside a single (full) Digit.
+ let r = Self(u) &- q &* v
+ assert(r < v)
+ return (q, r)
+ }
+
+ // Normalize the dividend and the divisor (self) such that the divisor has no leading zeroes.
+ let z = Self(self.leadingZeroBitCount)
+ let w = Self(Self.bitWidth) - z
+ let vn = self << z
+
+ let un32 = (z == 0 ? dividend.high : (dividend.high &<< z) | ((dividend.low as! Self) &>> w)) // No bits are lost
+ let un10 = dividend.low &<< z
+ let (un1, un0) = un10.split
+
+ // Divide `(un32,un10)` by `vn`, splitting the full 4/2 division into two 3/2 ones.
+ let (q1, un21) = quotientAndRemainder(dividing: (un32, (un1 as! Self)), by: vn)
+ let (q0, rn) = quotientAndRemainder(dividing: (un21, (un0 as! Self)), by: vn)
+
+ // Undo normalization of the remainder and combine the two halves of the quotient.
+ let mod = rn >> z
+ let div = Self((q1, q0))
+ return (div, mod)
+ }
+
+ /// Return the quotient of the 3/2-word division `x/y` as a single word.
+ ///
+ /// - Requires: (x.0, x.1) <= y && y.0.high != 0
+ /// - Returns: The exact value when it fits in a single word, otherwise `Self`.
+ static func approximateQuotient(dividing x: (Self, Self, Self), by y: (Self, Self)) -> Self {
+ // Start with q = (x.0, x.1) / y.0, (or Word.max on overflow)
+ var q: Self
+ var r: Self
+ if x.0 == y.0 {
+ q = Self.max
+ let (s, o) = x.0.addingReportingOverflow(x.1)
+ if o { return q }
+ r = s
+ }
+ else {
+ (q, r) = y.0.fastDividingFullWidth((x.0, (x.1 as! Magnitude)))
+ }
+ // Now refine q by considering x.2 and y.1.
+ // Note that since y is normalized, q * y - x is between 0 and 2.
+ let (ph, pl) = q.multipliedFullWidth(by: y.1)
+ if ph < r || (ph == r && pl <= x.2) { return q }
+
+ let (r1, ro) = r.addingReportingOverflow(y.0)
+ if ro { return q - 1 }
+
+ let (pl1, so) = pl.subtractingReportingOverflow((y.1 as! Magnitude))
+ let ph1 = (so ? ph - 1 : ph)
+
+ if ph1 < r1 || (ph1 == r1 && pl1 <= x.2) { return q - 1 }
+ return q - 2
+ }
+}
+
+extension CS.BigUInt {
+ //MARK: Division
+
+ /// Divide this integer by the word `y`, leaving the quotient in its place and returning the remainder.
+ ///
+ /// - Requires: y > 0
+ /// - Complexity: O(count)
+ internal mutating func divide(byWord y: Word) -> Word {
+ precondition(y > 0)
+ if y == 1 { return 0 }
+
+ var remainder: Word = 0
+ for i in (0 ..< count).reversed() {
+ let u = self[i]
+ (self[i], remainder) = y.fastDividingFullWidth((remainder, u))
+ }
+ return remainder
+ }
+
+ /// Divide this integer by the word `y` and return the resulting quotient and remainder.
+ ///
+ /// - Requires: y > 0
+ /// - Returns: (quotient, remainder) where quotient = floor(x/y), remainder = x - quotient * y
+ /// - Complexity: O(x.count)
+ internal func quotientAndRemainder(dividingByWord y: Word) -> (quotient: CS.BigUInt, remainder: Word) {
+ var div = self
+ let mod = div.divide(byWord: y)
+ return (div, mod)
+ }
+
+ /// Divide `x` by `y`, putting the quotient in `x` and the remainder in `y`.
+ /// Reusing integers like this reduces the number of allocations during the calculation.
+ static func divide(_ x: inout CS.BigUInt, by y: inout CS.BigUInt) {
+ // This is a Swift adaptation of "divmnu" from Hacker's Delight, which is in
+ // turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1).
+
+ precondition(!y.isZero)
+
+ // First, let's take care of the easy cases.
+ if x < y {
+ (x, y) = (0, x)
+ return
+ }
+ if y.count == 1 {
+ // The single-word case reduces to a simpler loop.
+ y = CS.BigUInt(x.divide(byWord: y[0]))
+ return
+ }
+
+ // In the hard cases, we will perform the long division algorithm we learned in school.
+ // It works by successively calculating the single-word quotient of the top y.count + 1
+ // words of x divided by y, replacing the top of x with the remainder, and repeating
+ // the process one word lower.
+ //
+ // The tricky part is that the algorithm needs to be able to do n+1/n word divisions,
+ // but we only have a primitive for dividing two words by a single
+ // word. (Remember that this step is also tricky when we do it on paper!)
+ //
+ // The solution is that the long division can be approximated by a single full division
+ // using just the most significant words. We can then use multiplications and
+ // subtractions to refine the approximation until we get the correct quotient word.
+ //
+ // We could do this by doing a simple 2/1 full division, but Knuth goes one step further,
+ // and implements a 3/2 division. This results in an exact approximation in the
+ // vast majority of cases, eliminating an extra subtraction over big integers.
+ //
+ // The function `approximateQuotient` above implements Knuth's 3/2 division algorithm.
+ // It requires that the divisor's most significant word is larger than
+ // Word.max / 2. This ensures that the approximation has tiny error bounds,
+ // which is what makes this entire approach viable.
+ // To satisfy this requirement, we will normalize the division by multiplying
+ // both the divisor and the dividend by the same (small) factor.
+ let z = y.leadingZeroBitCount
+ y <<= z
+ x <<= z // We'll calculate the remainder in the normalized dividend.
+ var quotient = CS.BigUInt()
+ assert(y.leadingZeroBitCount == 0)
+
+ // We're ready to start the long division!
+ let dc = y.count
+ let d1 = y[dc - 1]
+ let d0 = y[dc - 2]
+ var product: CS.BigUInt = 0
+ for j in (dc ... x.count).reversed() {
+ // Approximate dividing the top dc+1 words of `remainder` using the topmost 3/2 words.
+ let r2 = x[j]
+ let r1 = x[j - 1]
+ let r0 = x[j - 2]
+ let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0))
+
+ // Multiply the entire divisor with `q` and subtract the result from remainder.
+ // Normalization ensures the 3/2 quotient will either be exact for the full division, or
+ // it may overshoot by at most 1, in which case the product will be greater
+ // than the remainder.
+ product.load(y)
+ product.multiply(byWord: q)
+ if product <= x.extract(j - dc ..< j + 1) {
+ x.subtract(product, shiftedBy: j - dc)
+ quotient[j - dc] = q
+ }
+ else {
+ // This case is extremely rare -- it has a probability of 1/2^(Word.bitWidth - 1).
+ x.add(y, shiftedBy: j - dc)
+ x.subtract(product, shiftedBy: j - dc)
+ quotient[j - dc] = q - 1
+ }
+ }
+ // The remainder's normalization needs to be undone, but otherwise we're done.
+ x >>= z
+ y = x
+ x = quotient
+ }
+
+ /// Divide `x` by `y`, putting the remainder in `x`.
+ mutating func formRemainder(dividingBy y: CS.BigUInt, normalizedBy shift: Int) {
+ precondition(!y.isZero)
+ assert(y.leadingZeroBitCount == 0)
+ if y.count == 1 {
+ let remainder = self.divide(byWord: y[0] >> shift)
+ self.load(CS.BigUInt(remainder))
+ return
+ }
+ self <<= shift
+ if self >= y {
+ let dc = y.count
+ let d1 = y[dc - 1]
+ let d0 = y[dc - 2]
+ var product: CS.BigUInt = 0
+ for j in (dc ... self.count).reversed() {
+ let r2 = self[j]
+ let r1 = self[j - 1]
+ let r0 = self[j - 2]
+ let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0))
+ product.load(y)
+ product.multiply(byWord: q)
+ if product <= self.extract(j - dc ..< j + 1) {
+ self.subtract(product, shiftedBy: j - dc)
+ }
+ else {
+ self.add(y, shiftedBy: j - dc)
+ self.subtract(product, shiftedBy: j - dc)
+ }
+ }
+ }
+ self >>= shift
+ }
+
+
+ /// Divide this integer by `y` and return the resulting quotient and remainder.
+ ///
+ /// - Requires: `y > 0`
+ /// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y`
+ /// - Complexity: O(count^2)
+ public func quotientAndRemainder(dividingBy y: CS.BigUInt) -> (quotient: CS.BigUInt, remainder: CS.BigUInt) {
+ var x = self
+ var y = y
+ CS.BigUInt.divide(&x, by: &y)
+ return (x, y)
+ }
+
+ /// Divide `x` by `y` and return the quotient.
+ ///
+ /// - Note: Use `divided(by:)` if you also need the remainder.
+ public static func /(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
+ return x.quotientAndRemainder(dividingBy: y).quotient
+ }
+
+ /// Divide `x` by `y` and return the remainder.
+ ///
+ /// - Note: Use `divided(by:)` if you also need the remainder.
+ public static func %(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
+ var x = x
+ let shift = y.leadingZeroBitCount
+ x.formRemainder(dividingBy: y << shift, normalizedBy: shift)
+ return x
+ }
+
+ /// Divide `x` by `y` and store the quotient in `x`.
+ ///
+ /// - Note: Use `divided(by:)` if you also need the remainder.
+ public static func /=(x: inout CS.BigUInt, y: CS.BigUInt) {
+ var y = y
+ CS.BigUInt.divide(&x, by: &y)
+ }
+
+ /// Divide `x` by `y` and store the remainder in `x`.
+ ///
+ /// - Note: Use `divided(by:)` if you also need the remainder.
+ public static func %=(x: inout CS.BigUInt, y: CS.BigUInt) {
+ let shift = y.leadingZeroBitCount
+ x.formRemainder(dividingBy: y << shift, normalizedBy: shift)
+ }
+}
+
+extension CS.BigInt {
+ /// Divide this integer by `y` and return the resulting quotient and remainder.
+ ///
+ /// - Requires: `y > 0`
+ /// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y`
+ /// - Complexity: O(count^2)
+ public func quotientAndRemainder(dividingBy y: CS.BigInt) -> (quotient: CS.BigInt, remainder: CS.BigInt) {
+ var a = self.magnitude
+ var b = y.magnitude
+ CS.BigUInt.divide(&a, by: &b)
+ return ( CS.BigInt(sign: self.sign == y.sign ? .plus : .minus, magnitude: a),
+ CS.BigInt(sign: self.sign, magnitude: b))
+ }
+
+ /// Divide `a` by `b` and return the quotient. Traps if `b` is zero.
+ public static func /(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
+ return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude / b.magnitude)
+ }
+
+ /// Divide `a` by `b` and return the remainder. The result has the same sign as `a`.
+ public static func %(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
+ return CS.BigInt(sign: a.sign, magnitude: a.magnitude % b.magnitude)
+ }
+
+ /// Return the result of `a` mod `b`. The result is always a nonnegative integer that is less than the absolute value of `b`.
+ public func modulus(_ mod: CS.BigInt) -> CS.BigInt {
+ let remainder = self.magnitude % mod.magnitude
+ return CS.BigInt(
+ self.sign == .minus && !remainder.isZero
+ ? mod.magnitude - remainder
+ : remainder)
+ }
+}
+
+extension CS.BigInt {
+ /// Divide `a` by `b` storing the quotient in `a`.
+ public static func /=(a: inout CS.BigInt, b: CS.BigInt) { a = a / b }
+ /// Divide `a` by `b` storing the remainder in `a`.
+ public static func %=(a: inout CS.BigInt, b: CS.BigInt) { a = a % b }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Electronic codebook (ECB)
+//
+
+public struct ECB: BlockMode {
+ public let options: BlockModeOption = .paddingRequired
+ public let customBlockSize: Int? = nil
+
+ public init() {
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ ECBModeWorker(blockSize: blockSize, cipherOperation: cipherOperation)
+ }
+}
+
+struct ECBModeWorker: BlockModeWorker {
+ typealias Element = Array<UInt8>
+ let cipherOperation: CipherOperationOnBlock
+ let blockSize: Int
+ let additionalBufferSize: Int = 0
+
+ init(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = blockSize
+ self.cipherOperation = cipherOperation
+ }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let ciphertext = cipherOperation(plaintext) else {
+ return Array(plaintext)
+ }
+ return ciphertext
+ }
+
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ self.encrypt(block: ciphertext)
+ }
+}
--- /dev/null
+//
+// Exponentiation.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ //MARK: Exponentiation
+
+ /// Returns this integer raised to the power `exponent`.
+ ///
+ /// This function calculates the result by [successively squaring the base while halving the exponent][expsqr].
+ ///
+ /// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
+ ///
+ /// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is
+ /// a simple integer value. If you want to calculate big exponents, you'll probably need to use
+ /// the modulo arithmetic variant.
+ /// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.)
+ /// - SeeAlso: `BigUInt.power(_:, modulus:)`
+ /// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too.
+ public func power(_ exponent: Int) -> CS.BigUInt {
+ if exponent == 0 { return 1 }
+ if exponent == 1 { return self }
+ if exponent < 0 {
+ precondition(!self.isZero)
+ return self == 1 ? 1 : 0
+ }
+ if self <= 1 { return self }
+ var result = CS.BigUInt(1)
+ var b = self
+ var e = exponent
+ while e > 0 {
+ if e & 1 == 1 {
+ result *= b
+ }
+ e >>= 1
+ b *= b
+ }
+ return result
+ }
+
+ /// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`.
+ ///
+ /// Uses the [right-to-left binary method][rtlb].
+ ///
+ /// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
+ ///
+ /// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch
+ public func power(_ exponent: CS.BigUInt, modulus: CS.BigUInt) -> CS.BigUInt {
+ precondition(!modulus.isZero)
+ if modulus == (1 as CS.BigUInt) { return 0 }
+ let shift = modulus.leadingZeroBitCount
+ let normalizedModulus = modulus << shift
+ var result = CS.BigUInt(1)
+ var b = self
+ b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
+ for var e in exponent.words {
+ for _ in 0 ..< Word.bitWidth {
+ if e & 1 == 1 {
+ result *= b
+ result.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
+ }
+ e >>= 1
+ b *= b
+ b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift)
+ }
+ }
+ return result
+ }
+}
+
+extension CS.BigInt {
+ /// Returns this integer raised to the power `exponent`.
+ ///
+ /// This function calculates the result by [successively squaring the base while halving the exponent][expsqr].
+ ///
+ /// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
+ ///
+ /// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is
+ /// a simple integer value. If you want to calculate big exponents, you'll probably need to use
+ /// the modulo arithmetic variant.
+ /// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.)
+ /// - SeeAlso: `BigUInt.power(_:, modulus:)`
+ /// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too.
+ public func power(_ exponent: Int) -> CS.BigInt {
+ return CS.BigInt(sign: self.sign == .minus && exponent & 1 != 0 ? .minus : .plus,
+ magnitude: self.magnitude.power(exponent))
+ }
+
+ /// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`.
+ ///
+ /// Uses the [right-to-left binary method][rtlb].
+ ///
+ /// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
+ ///
+ /// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch
+ public func power(_ exponent: CS.BigInt, modulus: CS.BigInt) -> CS.BigInt {
+ precondition(!modulus.isZero)
+ if modulus.magnitude == 1 { return 0 }
+ if exponent.isZero { return 1 }
+ if exponent == 1 { return self.modulus(modulus) }
+ if exponent < 0 {
+ precondition(!self.isZero)
+ guard magnitude == 1 else { return 0 }
+ guard sign == .minus else { return 1 }
+ guard exponent.magnitude[0] & 1 != 0 else { return 1 }
+ return CS.BigInt(modulus.magnitude - 1)
+ }
+ let power = self.magnitude.power(exponent.magnitude,
+ modulus: modulus.magnitude)
+ if self.sign == .plus || exponent.magnitude[0] & 1 == 0 || power.isZero {
+ return CS.BigInt(power)
+ }
+ return CS.BigInt(modulus.magnitude - power)
+ }
+}
--- /dev/null
+//
+// Floating Point Conversion.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2017-08-11.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ public init?<T: BinaryFloatingPoint>(exactly source: T) {
+ guard source.isFinite else { return nil }
+ guard !source.isZero else { self = 0; return }
+ guard source.sign == .plus else { return nil }
+ let value = source.rounded(.towardZero)
+ guard value == source else { return nil }
+ assert(value.floatingPointClass == .positiveNormal)
+ assert(value.exponent >= 0)
+ let significand = value.significandBitPattern
+ self = (CS.BigUInt(1) << value.exponent) + CS.BigUInt(significand) >> (T.significandBitCount - Int(value.exponent))
+ }
+
+ public init<T: BinaryFloatingPoint>(_ source: T) {
+ self.init(exactly: source.rounded(.towardZero))!
+ }
+}
+
+extension CS.BigInt {
+ public init?<T: BinaryFloatingPoint>(exactly source: T) {
+ switch source.sign{
+ case .plus:
+ guard let magnitude = CS.BigUInt(exactly: source) else { return nil }
+ self = CS.BigInt(sign: .plus, magnitude: magnitude)
+ case .minus:
+ guard let magnitude = CS.BigUInt(exactly: -source) else { return nil }
+ self = CS.BigInt(sign: .minus, magnitude: magnitude)
+ }
+ }
+
+ public init<T: BinaryFloatingPoint>(_ source: T) {
+ self.init(exactly: source.rounded(.towardZero))!
+ }
+}
+
+extension BinaryFloatingPoint where RawExponent: FixedWidthInteger, RawSignificand: FixedWidthInteger {
+ public init(_ value: CS.BigInt) {
+ guard !value.isZero else { self = 0; return }
+ let v = value.magnitude
+ let bitWidth = v.bitWidth
+ var exponent = bitWidth - 1
+ let shift = bitWidth - Self.significandBitCount - 1
+ var significand = value.magnitude >> (shift - 1)
+ if significand[0] & 3 == 3 { // Handle rounding
+ significand >>= 1
+ significand += 1
+ if significand.trailingZeroBitCount >= Self.significandBitCount {
+ exponent += 1
+ }
+ }
+ else {
+ significand >>= 1
+ }
+ let bias = 1 << (Self.exponentBitCount - 1) - 1
+ guard exponent <= bias else { self = Self.infinity; return }
+ significand &= 1 << Self.significandBitCount - 1
+ self = Self.init(sign: value.sign == .plus ? .plus : .minus,
+ exponentBitPattern: RawExponent(bias + exponent),
+ significandBitPattern: RawSignificand(significand))
+ }
+
+ public init(_ value: CS.BigUInt) {
+ self.init(CS.BigInt(sign: .plus, magnitude: value))
+ }
+}
--- /dev/null
+//
+// GCD.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ //MARK: Greatest Common Divisor
+
+ /// Returns the greatest common divisor of `self` and `b`.
+ ///
+ /// - Complexity: O(count^2) where count = max(self.count, b.count)
+ public func greatestCommonDivisor(with b: CS.BigUInt) -> CS.BigUInt {
+ // This is Stein's algorithm: https://en.wikipedia.org/wiki/Binary_GCD_algorithm
+ if self.isZero { return b }
+ if b.isZero { return self }
+
+ let az = self.trailingZeroBitCount
+ let bz = b.trailingZeroBitCount
+ let twos = Swift.min(az, bz)
+
+ var (x, y) = (self >> az, b >> bz)
+ if x < y { swap(&x, &y) }
+
+ while !x.isZero {
+ x >>= x.trailingZeroBitCount
+ if x < y { swap(&x, &y) }
+ x -= y
+ }
+ return y << twos
+ }
+
+ /// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse],
+ /// or `nil` if there is no such number.
+ ///
+ /// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
+ ///
+ /// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`.
+ /// - Requires: modulus > 1
+ /// - Complexity: O(count^3)
+ public func inverse(_ modulus: CS.BigUInt) -> CS.BigUInt? {
+ precondition(modulus > 1)
+ var t1 = CS.BigInt(0)
+ var t2 = CS.BigInt(1)
+ var r1 = modulus
+ var r2 = self
+ while !r2.isZero {
+ let quotient = r1 / r2
+ (t1, t2) = (t2, t1 - CS.BigInt(quotient) * t2)
+ (r1, r2) = (r2, r1 - quotient * r2)
+ }
+ if r1 > 1 { return nil }
+ if t1.sign == .minus { return modulus - t1.magnitude }
+ return t1.magnitude
+ }
+}
+
+extension CS.BigInt {
+ /// Returns the greatest common divisor of `a` and `b`.
+ ///
+ /// - Complexity: O(count^2) where count = max(a.count, b.count)
+ public func greatestCommonDivisor(with b: CS.BigInt) -> CS.BigInt {
+ return CS.BigInt(self.magnitude.greatestCommonDivisor(with: b.magnitude))
+ }
+
+ /// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse],
+ /// or `nil` if there is no such number.
+ ///
+ /// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
+ ///
+ /// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`.
+ /// - Requires: modulus.magnitude > 1
+ /// - Complexity: O(count^3)
+ public func inverse(_ modulus: CS.BigInt) -> CS.BigInt? {
+ guard let inv = self.magnitude.inverse(modulus.magnitude) else { return nil }
+ return CS.BigInt(self.sign == .plus || inv.isZero ? inv : modulus.magnitude - inv)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Galois/Counter Mode (GCM)
+// https://csrc.nist.gov/publications/detail/sp/800-38d/final
+// ref: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.694.695&rep=rep1&type=pdf
+//
+
+public final class GCM: BlockMode {
+ public enum Mode {
+ /// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
+ case combined
+ /// Some applications may need to store the authentication tag and the encrypted message at different locations.
+ case detached
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ /// Special symbol FAIL that indicates that the inputs are not authentic.
+ case fail
+ }
+
+ private let iv: Array<UInt8>
+ private let additionalAuthenticatedData: Array<UInt8>?
+ private let mode: Mode
+ public let customBlockSize: Int? = nil
+
+ /// Length of authentication tag, in bytes.
+ /// For encryption, the value is given as init parameter.
+ /// For decryption, the length of given authentication tag is used.
+ private let tagLength: Int
+
+ // `authenticationTag` nil for encryption, known tag for decryption
+ /// For encryption, the value is set at the end of the encryption.
+ /// For decryption, this is a known Tag to validate against.
+ public var authenticationTag: Array<UInt8>?
+
+ // encrypt
+ /// Possible tag lengths: 4,8,12,13,14,15,16
+ public init(iv: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, tagLength: Int = 16, mode: Mode = .detached) {
+ self.iv = iv
+ self.additionalAuthenticatedData = additionalAuthenticatedData
+ self.mode = mode
+ self.tagLength = tagLength
+ }
+
+ // decrypt
+ public convenience init(iv: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
+ self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode)
+ self.authenticationTag = authenticationTag
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.iv.isEmpty {
+ throw Error.invalidInitializationVector
+ }
+
+ let worker = GCMModeWorker(iv: iv.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation)
+ worker.didCalculateTag = { [weak self] tag in
+ self?.authenticationTag = tag
+ }
+ return worker
+ }
+}
+
+// MARK: - Worker
+
+final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
+ let cipherOperation: CipherOperationOnBlock
+
+ // Callback called when authenticationTag is ready
+ var didCalculateTag: ((Array<UInt8>) -> Void)?
+
+ private let tagLength: Int
+ // GCM nonce is 96-bits by default. It's the most effective length for the IV
+ private static let nonceSize = 12
+
+ // GCM is designed for 128-bit ciphers like AES (but not really for Blowfish). 64-bit mode is not implemented.
+ let blockSize = 16 // 128 bit
+ let additionalBufferSize: Int
+ private let iv: ArraySlice<UInt8>
+ private let mode: GCM.Mode
+ private var counter: UInt128
+ private let eky0: UInt128 // move to GF?
+ private let h: UInt128
+
+ // Additional authenticated data
+ private let aad: ArraySlice<UInt8>?
+ // Known Tag used to validate during decryption
+ private var expectedTag: Array<UInt8>?
+
+ // Note: need new worker to reset instance
+ // Use empty aad if not specified. AAD is optional.
+ private lazy var gf: GF = {
+ if let aad = aad {
+ return GF(aad: Array(aad), h: h, blockSize: blockSize)
+ }
+ return GF(aad: [UInt8](), h: h, blockSize: blockSize)
+ }()
+
+ init(iv: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, tagLength: Int, mode: GCM.Mode, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.cipherOperation = cipherOperation
+ self.iv = iv
+ self.mode = mode
+ self.aad = aad
+ self.expectedTag = expectedTag
+ self.tagLength = tagLength
+ self.h = UInt128(cipherOperation(Array<UInt8>(repeating: 0, count: self.blockSize).slice)!) // empty block
+
+ if mode == .combined {
+ self.additionalBufferSize = tagLength
+ } else {
+ self.additionalBufferSize = 0
+ }
+
+ // Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH
+ // counter = GF.ghash(aad: [UInt8](), ciphertext: nonce)
+ if iv.count == GCMModeWorker.nonceSize {
+ self.counter = makeCounter(nonce: Array(self.iv))
+ } else {
+ self.counter = GF.ghash(h: self.h, aad: [UInt8](), ciphertext: Array(iv), blockSize: self.blockSize)
+ }
+
+ // Set constants
+ self.eky0 = UInt128(cipherOperation(self.counter.bytes.slice)!)
+ }
+
+ func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ self.counter = incrementCounter(self.counter)
+
+ guard let ekyN = cipherOperation(counter.bytes.slice) else {
+ return Array(plaintext)
+ }
+
+ // plaintext block ^ ek1
+ let ciphertext = xor(plaintext, ekyN) as Array<UInt8>
+
+ // update ghash incrementally
+ gf.ghashUpdate(block: ciphertext)
+
+ return Array(ciphertext)
+ }
+
+ @inlinable
+ func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // Calculate MAC tag.
+ let ghash = self.gf.ghashFinish()
+ let tag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength))
+
+ // Notify handler
+ self.didCalculateTag?(tag)
+
+ switch self.mode {
+ case .combined:
+ return (ciphertext + tag).slice
+ case .detached:
+ return ciphertext
+ }
+ }
+
+ @inlinable
+ func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ self.counter = incrementCounter(self.counter)
+
+ // update ghash incrementally
+ self.gf.ghashUpdate(block: Array(ciphertext))
+
+ guard let ekN = cipherOperation(counter.bytes.slice) else {
+ return Array(ciphertext)
+ }
+
+ // ciphertext block ^ ek1
+ let plaintext = xor(ciphertext, ekN) as Array<UInt8>
+ return plaintext
+ }
+
+ // The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
+ // output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
+ // authentic.
+ @discardableResult
+ func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // Validate tag
+ switch self.mode {
+ case .combined:
+ // overwrite expectedTag property used later for verification
+ self.expectedTag = Array(ciphertext.suffix(self.tagLength))
+ return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
+ case .detached:
+ return ciphertext
+ }
+ }
+
+ @inlinable
+ func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // Calculate MAC tag.
+ let ghash = self.gf.ghashFinish()
+ let computedTag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength))
+
+ // Validate tag
+ guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
+ throw GCM.Error.fail
+ }
+
+ return plaintext
+ }
+
+ func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // do nothing
+ plaintext
+ }
+}
+
+// MARK: - Local utils
+
+private func makeCounter(nonce: Array<UInt8>) -> UInt128 {
+ UInt128(nonce + [0, 0, 0, 1])
+}
+
+// Successive counter values are generated using the function incr(), which treats the rightmost 32
+// bits of its argument as a nonnegative integer with the least significant bit on the right
+private func incrementCounter(_ counter: UInt128) -> UInt128 {
+ let b = counter.i.b + 1
+ let a = (b == 0 ? counter.i.a + 1 : counter.i.a)
+ return UInt128((a, b))
+}
+
+// If data is not a multiple of block size bytes long then the remainder is zero padded
+// Note: It's similar to ZeroPadding, but it's not the same.
+private func addPadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ if bytes.isEmpty {
+ return Array<UInt8>(repeating: 0, count: blockSize)
+ }
+
+ let remainder = bytes.count % blockSize
+ if remainder == 0 {
+ return bytes
+ }
+
+ let paddingCount = blockSize - remainder
+ if paddingCount > 0 {
+ return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
+ }
+ return bytes
+}
+
+// MARK: - GF
+
+/// The Field GF(2^128)
+private final class GF {
+ static let r = UInt128(a: 0xE100000000000000, b: 0)
+
+ let blockSize: Int
+ let h: UInt128
+
+ // AAD won't change
+ let aadLength: Int
+
+ // Updated for every consumed block
+ var ciphertextLength: Int
+
+ // Start with 0
+ var x: UInt128
+
+ init(aad: [UInt8], h: UInt128, blockSize: Int) {
+ self.blockSize = blockSize
+ self.aadLength = aad.count
+ self.ciphertextLength = 0
+ self.h = h
+ self.x = 0
+
+ // Calculate for AAD at the begining
+ self.x = GF.calculateX(aad: aad, x: self.x, h: h, blockSize: blockSize)
+ }
+
+ @discardableResult
+ func ghashUpdate(block ciphertextBlock: Array<UInt8>) -> UInt128 {
+ self.ciphertextLength += ciphertextBlock.count
+ self.x = GF.calculateX(block: addPadding(ciphertextBlock, blockSize: self.blockSize), x: self.x, h: self.h, blockSize: self.blockSize)
+ return self.x
+ }
+
+ func ghashFinish() -> UInt128 {
+ // len(A) || len(C)
+ let len = UInt128(a: UInt64(aadLength * 8), b: UInt64(ciphertextLength * 8))
+ x = GF.multiply(self.x ^ len, self.h)
+ return self.x
+ }
+
+ // GHASH. One-time calculation
+ static func ghash(x startx: UInt128 = 0, h: UInt128, aad: Array<UInt8>, ciphertext: Array<UInt8>, blockSize: Int) -> UInt128 {
+ var x = self.calculateX(aad: aad, x: startx, h: h, blockSize: blockSize)
+ x = self.calculateX(ciphertext: ciphertext, x: x, h: h, blockSize: blockSize)
+
+ // len(aad) || len(ciphertext)
+ let len = UInt128(a: UInt64(aad.count * 8), b: UInt64(ciphertext.count * 8))
+ x = self.multiply(x ^ len, h)
+
+ return x
+ }
+
+ // Calculate Ciphertext part, for all blocks
+ // Not used with incremental calculation.
+ private static func calculateX(ciphertext: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+ let pciphertext = addPadding(ciphertext, blockSize: blockSize)
+ let blocksCount = pciphertext.count / blockSize
+
+ var x = startx
+ for i in 0..<blocksCount {
+ let cpos = i * blockSize
+ let block = pciphertext[pciphertext.startIndex.advanced(by: cpos)..<pciphertext.startIndex.advanced(by: cpos + blockSize)]
+ x = self.calculateX(block: Array(block), x: x, h: h, blockSize: blockSize)
+ }
+ return x
+ }
+
+ // block is expected to be padded with addPadding
+ private static func calculateX(block ciphertextBlock: Array<UInt8>, x: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+ let k = x ^ UInt128(ciphertextBlock)
+ return self.multiply(k, h)
+ }
+
+ // Calculate AAD part, for all blocks
+ private static func calculateX(aad: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 {
+ let paad = addPadding(aad, blockSize: blockSize)
+ let blocksCount = paad.count / blockSize
+
+ var x = startx
+ for i in 0..<blocksCount {
+ let apos = i * blockSize
+ let k = x ^ UInt128(paad[paad.startIndex.advanced(by: apos)..<paad.startIndex.advanced(by: apos + blockSize)])
+ x = self.multiply(k, h)
+ }
+
+ return x
+ }
+
+ // Multiplication GF(2^128).
+ private static func multiply(_ x: UInt128, _ y: UInt128) -> UInt128 {
+ var z: UInt128 = 0
+ var v = x
+ var k = UInt128(a: 1 << 63, b: 0)
+
+ for _ in 0..<128 {
+ if y & k == k {
+ z = z ^ v
+ }
+
+ if v & 1 != 1 {
+ v = v >> 1
+ } else {
+ v = (v >> 1) ^ self.r
+ }
+
+ k = k >> 1
+ }
+
+ return z
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// Array of bytes. Caution: don't use directly because generic is slow.
+///
+/// - parameter value: integer value
+/// - parameter length: length of output array. By default size of value type
+///
+/// - returns: Array of bytes
+@_specialize(where T == Int)
+@_specialize(where T == UInt)
+@_specialize(where T == UInt8)
+@_specialize(where T == UInt16)
+@_specialize(where T == UInt32)
+@_specialize(where T == UInt64)
+@inlinable
+func arrayOfBytes<T: FixedWidthInteger>(value: T, length totalBytes: Int = MemoryLayout<T>.size) -> Array<UInt8> {
+ let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+ valuePointer.pointee = value
+
+ let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
+ var bytes = Array<UInt8>(repeating: 0, count: totalBytes)
+ for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
+ bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
+ }
+
+ valuePointer.deinitialize(count: 1)
+ valuePointer.deallocate()
+
+ return bytes
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// https://www.ietf.org/rfc/rfc5869.txt
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#elseif canImport(WASILibc)
+import WASILibc
+#endif
+
+/// A key derivation function.
+///
+/// HKDF - HMAC-based Extract-and-Expand CookiesKey Derivation Function.
+public struct HKDF {
+ public enum Error: Swift.Error {
+ case invalidInput
+ case derivedKeyTooLong
+ }
+
+ private let numBlocks: Int // l
+ private let dkLen: Int
+ private let info: Array<UInt8>
+ private let prk: Array<UInt8>
+ private let variant: HMAC.Variant
+
+ /// - parameters:
+ /// - variant: hash variant
+ /// - salt: optional salt (if not provided, it is set to a sequence of variant.digestLength zeros)
+ /// - info: optional context and application specific information
+ /// - keyLength: intended length of derived key
+ public init(password: Array<UInt8>, salt: Array<UInt8>? = nil, info: Array<UInt8>? = nil, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha2(.sha256)) throws {
+ guard !password.isEmpty else {
+ throw Error.invalidInput
+ }
+
+ let dkLen = keyLength ?? variant.digestLength
+ let keyLengthFinal = Double(dkLen)
+ let hLen = Double(variant.digestLength)
+ let numBlocks = Int(ceil(keyLengthFinal / hLen)) // l = ceil(keyLength / hLen)
+ guard numBlocks <= 255 else {
+ throw Error.derivedKeyTooLong
+ }
+
+ /// HKDF-Extract(salt, password) -> PRK
+ /// - PRK - a pseudo-random key; it is used by calculate()
+ self.prk = try HMAC(key: salt ?? [], variant: variant).authenticate(password)
+ self.info = info ?? []
+ self.variant = variant
+ self.dkLen = dkLen
+ self.numBlocks = numBlocks
+ }
+
+ public func calculate() throws -> Array<UInt8> {
+ let hmac = HMAC(key: prk, variant: variant)
+ var ret = Array<UInt8>()
+ ret.reserveCapacity(self.numBlocks * self.variant.digestLength)
+ var value = Array<UInt8>()
+ for i in 1...self.numBlocks {
+ value.append(contentsOf: self.info)
+ value.append(UInt8(i))
+
+ let bytes = try hmac.authenticate(value)
+ ret.append(contentsOf: bytes)
+
+ /// update value to use it as input for next iteration
+ value = bytes
+ }
+ return Array(ret.prefix(self.dkLen))
+ }
+
+ public func callAsFunction() throws -> Array<UInt8> {
+ try calculate()
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension HMAC {
+ public convenience init(key: String, variant: HMAC.Variant = .md5) throws {
+ self.init(key: key.bytes, variant: variant)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public final class HMAC: Authenticator {
+ public enum Error: Swift.Error {
+ case authenticateError
+ case invalidInput
+ }
+
+ public enum Variant {
+ case md5
+ case sha1
+ case sha2(SHA2.Variant)
+ case sha3(SHA3.Variant)
+
+ @available(*, deprecated, message: "Use sha2(variant) instead.")
+ case sha256, sha384, sha512
+
+ var digestLength: Int {
+ switch self {
+ case .sha1:
+ return SHA1.digestLength
+ case .sha256:
+ return SHA2.Variant.sha256.digestLength
+ case .sha384:
+ return SHA2.Variant.sha384.digestLength
+ case .sha512:
+ return SHA2.Variant.sha512.digestLength
+ case .sha2(let variant):
+ return variant.digestLength
+ case .sha3(let variant):
+ return variant.digestLength
+ case .md5:
+ return MD5.digestLength
+ }
+ }
+
+ func calculateHash(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ switch self {
+ case .sha1:
+ return Digest.sha1(bytes)
+ case .sha256:
+ return Digest.sha256(bytes)
+ case .sha384:
+ return Digest.sha384(bytes)
+ case .sha512:
+ return Digest.sha512(bytes)
+ case .sha2(let variant):
+ return Digest.sha2(bytes, variant: variant)
+ case .sha3(let variant):
+ return Digest.sha3(bytes, variant: variant)
+ case .md5:
+ return Digest.md5(bytes)
+ }
+ }
+
+ func blockSize() -> Int {
+ switch self {
+ case .md5:
+ return MD5.blockSize
+ case .sha1:
+ return SHA1.blockSize
+ case .sha256:
+ return SHA2.Variant.sha256.blockSize
+ case .sha384:
+ return SHA2.Variant.sha384.blockSize
+ case .sha512:
+ return SHA2.Variant.sha512.blockSize
+ case .sha2(let variant):
+ return variant.blockSize
+ case .sha3(let variant):
+ return variant.blockSize
+ }
+ }
+ }
+
+ var key: Array<UInt8>
+ let variant: Variant
+
+ public init(key: Array<UInt8>, variant: HMAC.Variant = .md5) {
+ self.variant = variant
+ self.key = key
+
+ if key.count > variant.blockSize() {
+ let hash = variant.calculateHash(key)
+ self.key = hash
+ }
+
+ if key.count < variant.blockSize() {
+ self.key = ZeroPadding().add(to: key, blockSize: variant.blockSize())
+ }
+ }
+
+ // MARK: Authenticator
+
+ public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ var opad = Array<UInt8>(repeating: 0x5c, count: variant.blockSize())
+ for idx in self.key.indices {
+ opad[idx] = self.key[idx] ^ opad[idx]
+ }
+ var ipad = Array<UInt8>(repeating: 0x36, count: variant.blockSize())
+ for idx in self.key.indices {
+ ipad[idx] = self.key[idx] ^ ipad[idx]
+ }
+
+ let ipadAndMessageHash = self.variant.calculateHash(ipad + bytes)
+ let result = self.variant.calculateHash(opad + ipadAndMessageHash)
+
+ // return Array(result[0..<10]) // 80 bits
+ return result
+ }
+}
--- /dev/null
+//
+// Hashable.swift
+// BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt: Hashable {
+ //MARK: Hashing
+
+ /// Append this `BigUInt` to the specified hasher.
+ public func hash(into hasher: inout Hasher) {
+ for word in self.words {
+ hasher.combine(word)
+ }
+ }
+}
+
+extension CS.BigInt: Hashable {
+ /// Append this `BigInt` to the specified hasher.
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(sign)
+ hasher.combine(magnitude)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+/// Padding with random bytes, ending with the number of added bytes.
+/// Read the [Wikipedia](https://en.wikipedia.org/wiki/Padding_(cryptography)#ISO_10126)
+/// and [Crypto-IT](http://www.crypto-it.net/eng/theory/padding.html) articles for more info.
+struct ISO10126Padding: PaddingProtocol {
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ let padding = UInt8(blockSize - (bytes.count % blockSize))
+ var withPadding = bytes
+ if padding > 0 {
+ withPadding += (0..<(padding - 1)).map { _ in UInt8.random(in: 0...255) } + [padding]
+ }
+ return withPadding
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize: Int?) -> Array<UInt8> {
+ guard !bytes.isEmpty, let lastByte = bytes.last else {
+ return bytes
+ }
+
+ assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+ let padding = Int(lastByte) // last byte
+ let finalLength = bytes.count - padding
+
+ if finalLength < 0 {
+ return bytes
+ }
+
+ if padding >= 1 {
+ return Array(bytes[0..<finalLength])
+ }
+
+ return bytes
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+// First byte is 0x80, rest is zero padding
+// http://www.crypto-it.net/eng/theory/padding.html
+// http://www.embedx.com/pdfs/ISO_STD_7816/info_isoiec7816-4%7Bed21.0%7Den.pdf
+struct ISO78164Padding: PaddingProtocol {
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ var padded = Array(bytes)
+ padded.append(0x80)
+
+ while (padded.count % blockSize) != 0 {
+ padded.append(0x00)
+ }
+ return padded
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ if let idx = bytes.lastIndex(of: 0x80) {
+ return Array(bytes[..<idx])
+ } else {
+ return bytes
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Created by Marcin Krzyzanowski on 12/08/14.
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#endif
+
+extension FixedWidthInteger {
+ @inlinable
+ func bytes(totalBytes: Int = MemoryLayout<Self>.size) -> Array<UInt8> {
+ arrayOfBytes(value: self.littleEndian, length: totalBytes)
+ // TODO: adjust bytes order
+ // var value = self.littleEndian
+ // return withUnsafeBytes(of: &value, Array.init).reversed()
+ }
+}
--- /dev/null
+//
+// Integer Conversion.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2017-08-11.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ public init?<T: BinaryInteger>(exactly source: T) {
+ guard source >= (0 as T) else { return nil }
+ if source.bitWidth <= 2 * Word.bitWidth {
+ var it = source.words.makeIterator()
+ self.init(low: it.next() ?? 0, high: it.next() ?? 0)
+ precondition(it.next() == nil, "Length of BinaryInteger.words is greater than its bitWidth")
+ }
+ else {
+ self.init(words: source.words)
+ }
+ }
+
+ public init<T: BinaryInteger>(_ source: T) {
+ precondition(source >= (0 as T), "BigUInt cannot represent negative values")
+ self.init(exactly: source)!
+ }
+
+ public init<T: BinaryInteger>(truncatingIfNeeded source: T) {
+ self.init(words: source.words)
+ }
+
+ public init<T: BinaryInteger>(clamping source: T) {
+ if source <= (0 as T) {
+ self.init()
+ }
+ else {
+ self.init(words: source.words)
+ }
+ }
+}
+
+extension CS.BigInt {
+ public init() {
+ self.init(sign: .plus, magnitude: 0)
+ }
+
+ /// Initializes a new signed big integer with the same value as the specified unsigned big integer.
+ public init(_ integer: CS.BigUInt) {
+ self.magnitude = integer
+ self.sign = .plus
+ }
+
+ public init<T>(_ source: T) where T : BinaryInteger {
+ if source >= (0 as T) {
+ self.init(sign: .plus, magnitude: CS.BigUInt(source))
+ }
+ else {
+ var words = Array(source.words)
+ words.twosComplement()
+ self.init(sign: .minus, magnitude: CS.BigUInt(words: words))
+ }
+ }
+
+ public init?<T>(exactly source: T) where T : BinaryInteger {
+ self.init(source)
+ }
+
+ public init<T>(clamping source: T) where T : BinaryInteger {
+ self.init(source)
+ }
+
+ public init<T>(truncatingIfNeeded source: T) where T : BinaryInteger {
+ self.init(source)
+ }
+}
+
+extension CS.BigUInt: ExpressibleByIntegerLiteral {
+ /// Initialize a new big integer from an integer literal.
+ public init(integerLiteral value: UInt64) {
+ self.init(value)
+ }
+}
+
+extension CS.BigInt: ExpressibleByIntegerLiteral {
+ /// Initialize a new big integer from an integer literal.
+ public init(integerLiteral value: Int64) {
+ self.init(value)
+ }
+}
+
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public final class MD5: DigestType {
+ static let blockSize: Int = 64
+ static let digestLength: Int = 16 // 128 / 8
+ fileprivate static let hashInitialValue: Array<UInt32> = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
+
+ fileprivate var accumulated = Array<UInt8>()
+ fileprivate var processedBytesTotalCount: Int = 0
+ fileprivate var accumulatedHash: Array<UInt32> = MD5.hashInitialValue
+
+ /** specifies the per-round shift amounts */
+ private let s: Array<UInt32> = [
+ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
+ ]
+
+ /** binary integer part of the sines of integers (Radians) */
+ private let k: Array<UInt32> = [
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+ ]
+
+ public init() {
+ }
+
+ public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
+ do {
+ return try update(withBytes: bytes.slice, isLast: true)
+ } catch {
+ fatalError()
+ }
+ }
+
+ public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ calculate(for: bytes)
+ }
+
+
+ // mutating currentHash in place is way faster than returning new result
+ fileprivate func process(block chunk: ArraySlice<UInt8>, currentHash: inout Array<UInt32>) {
+ assert(chunk.count == 16 * 4)
+
+ // Initialize hash value for this chunk:
+ var A: UInt32 = currentHash[0]
+ var B: UInt32 = currentHash[1]
+ var C: UInt32 = currentHash[2]
+ var D: UInt32 = currentHash[3]
+
+ var dTemp: UInt32 = 0
+
+ // Main loop
+ for j in 0..<self.k.count {
+ var g = 0
+ var F: UInt32 = 0
+
+ switch j {
+ case 0...15:
+ F = (B & C) | ((~B) & D)
+ g = j
+ case 16...31:
+ F = (D & B) | (~D & C)
+ g = (5 * j + 1) % 16
+ case 32...47:
+ F = B ^ C ^ D
+ g = (3 * j + 5) % 16
+ case 48...63:
+ F = C ^ (B | (~D))
+ g = (7 * j) % 16
+ default:
+ break
+ }
+ dTemp = D
+ D = C
+ C = B
+
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15 and get M[g] value
+ let gAdvanced = g << 2
+
+ var Mg = UInt32(chunk[chunk.startIndex &+ gAdvanced])
+ Mg |= UInt32(chunk[chunk.startIndex &+ gAdvanced &+ 1]) << 8
+ Mg |= UInt32(chunk[chunk.startIndex &+ gAdvanced &+ 2]) << 16
+ Mg |= UInt32(chunk[chunk.startIndex &+ gAdvanced &+ 3]) << 24
+
+ B = B &+ rotateLeft(A &+ F &+ self.k[j] &+ Mg, by: self.s[j])
+ A = dTemp
+ }
+
+ currentHash[0] = currentHash[0] &+ A
+ currentHash[1] = currentHash[1] &+ B
+ currentHash[2] = currentHash[2] &+ C
+ currentHash[3] = currentHash[3] &+ D
+ }
+}
+
+extension MD5: Updatable {
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ if isLast {
+ let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8
+ let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b
+
+ // Step 1. Append padding
+ bitPadding(to: &self.accumulated, blockSize: MD5.blockSize, allowance: 64 / 8)
+
+ // Step 2. Append Length a 64-bit representation of lengthInBits
+ self.accumulated += lengthBytes.reversed()
+ }
+
+ var processedBytes = 0
+ for chunk in self.accumulated.batched(by: MD5.blockSize) {
+ if isLast || (self.accumulated.count - processedBytes) >= MD5.blockSize {
+ self.process(block: chunk, currentHash: &self.accumulatedHash)
+ processedBytes += chunk.count
+ }
+ }
+ self.accumulated.removeFirst(processedBytes)
+ self.processedBytesTotalCount += processedBytes
+
+ // output current hash
+ var result = Array<UInt8>()
+ result.reserveCapacity(MD5.digestLength)
+
+ for hElement in self.accumulatedHash {
+ let hLE = hElement.littleEndian
+ result += Array<UInt8>(arrayLiteral: UInt8(hLE & 0xff), UInt8((hLE >> 8) & 0xff), UInt8((hLE >> 16) & 0xff), UInt8((hLE >> 24) & 0xff))
+ }
+
+ // reset hash value for instance
+ if isLast {
+ self.accumulatedHash = MD5.hashInitialValue
+ }
+
+ return result
+ }
+}
--- /dev/null
+//
+// Multiplication.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+
+ //MARK: Multiplication
+
+ /// Multiply this big integer by a single word, and store the result in place of the original big integer.
+ ///
+ /// - Complexity: O(count)
+ public mutating func multiply(byWord y: Word) {
+ guard y != 0 else { self = 0; return }
+ guard y != 1 else { return }
+ var carry: Word = 0
+ let c = self.count
+ for i in 0 ..< c {
+ let (h, l) = self[i].multipliedFullWidth(by: y)
+ let (low, o) = l.addingReportingOverflow(carry)
+ self[i] = low
+ carry = (o ? h + 1 : h)
+ }
+ self[c] = carry
+ }
+
+ /// Multiply this big integer by a single Word, and return the result.
+ ///
+ /// - Complexity: O(count)
+ public func multiplied(byWord y: Word) -> CS.BigUInt {
+ var r = self
+ r.multiply(byWord: y)
+ return r
+ }
+
+ /// Multiply `x` by `y`, and add the result to this integer, optionally shifted `shift` words to the left.
+ ///
+ /// - Note: This is the fused multiply/shift/add operation; it is more efficient than doing the components
+ /// individually. (The fused operation doesn't need to allocate space for temporary big integers.)
+ /// - Returns: `self` is set to `self + (x * y) << (shift * 2^Word.bitWidth)`
+ /// - Complexity: O(count)
+ public mutating func multiplyAndAdd(_ x: CS.BigUInt, _ y: Word, shiftedBy shift: Int = 0) {
+ precondition(shift >= 0)
+ guard y != 0 && x.count > 0 else { return }
+ guard y != 1 else { self.add(x, shiftedBy: shift); return }
+ var mulCarry: Word = 0
+ var addCarry = false
+ let xc = x.count
+ var xi = 0
+ while xi < xc || addCarry || mulCarry > 0 {
+ let (h, l) = x[xi].multipliedFullWidth(by: y)
+ let (low, o) = l.addingReportingOverflow(mulCarry)
+ mulCarry = (o ? h + 1 : h)
+
+ let ai = shift + xi
+ let (sum1, so1) = self[ai].addingReportingOverflow(low)
+ if addCarry {
+ let (sum2, so2) = sum1.addingReportingOverflow(1)
+ self[ai] = sum2
+ addCarry = so1 || so2
+ }
+ else {
+ self[ai] = sum1
+ addCarry = so1
+ }
+ xi += 1
+ }
+ }
+
+ /// Multiply this integer by `y` and return the result.
+ ///
+ /// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than
+ /// `BigUInt.directMultiplicationLimit` words.
+ /// - Complexity: O(n^log2(3))
+ public func multiplied(by y: CS.BigUInt) -> CS.BigUInt {
+ // This method is mostly defined for symmetry with the rest of the arithmetic operations.
+ return self * y
+ }
+
+ /// Multiplication switches to an asymptotically better recursive algorithm when arguments have more words than this limit.
+ public static var directMultiplicationLimit: Int = 1024
+
+ /// Multiply `a` by `b` and return the result.
+ ///
+ /// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than
+ /// `BigUInt.directMultiplicationLimit` words.
+ /// - Complexity: O(n^log2(3))
+ public static func *(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt {
+ let xc = x.count
+ let yc = y.count
+ if xc == 0 { return CS.BigUInt() }
+ if yc == 0 { return CS.BigUInt() }
+ if yc == 1 { return x.multiplied(byWord: y[0]) }
+ if xc == 1 { return y.multiplied(byWord: x[0]) }
+
+ if Swift.min(xc, yc) <= CS.BigUInt.directMultiplicationLimit {
+ // Long multiplication.
+ let left = (xc < yc ? y : x)
+ let right = (xc < yc ? x : y)
+ var result = CS.BigUInt()
+ for i in (0 ..< right.count).reversed() {
+ result.multiplyAndAdd(left, right[i], shiftedBy: i)
+ }
+ return result
+ }
+
+ if yc < xc {
+ let (xh, xl) = x.split
+ var r = xl * y
+ r.add(xh * y, shiftedBy: x.middleIndex)
+ return r
+ }
+ else if xc < yc {
+ let (yh, yl) = y.split
+ var r = yl * x
+ r.add(yh * x, shiftedBy: y.middleIndex)
+ return r
+ }
+
+ let shift = x.middleIndex
+
+ // Karatsuba multiplication:
+ // x * y = <a,b> * <c,d> = <ac, ac + bd - (a-b)(c-d), bd> (ignoring carry)
+ let (a, b) = x.split
+ let (c, d) = y.split
+
+ let high = a * c
+ let low = b * d
+ let xp = a >= b
+ let yp = c >= d
+ let xm = (xp ? a - b : b - a)
+ let ym = (yp ? c - d : d - c)
+ let m = xm * ym
+
+ var r = low
+ r.add(high, shiftedBy: 2 * shift)
+ r.add(low, shiftedBy: shift)
+ r.add(high, shiftedBy: shift)
+ if xp == yp {
+ r.subtract(m, shiftedBy: shift)
+ }
+ else {
+ r.add(m, shiftedBy: shift)
+ }
+ return r
+ }
+
+ /// Multiply `a` by `b` and store the result in `a`.
+ public static func *=(a: inout CS.BigUInt, b: CS.BigUInt) {
+ a = a * b
+ }
+}
+
+extension CS.BigInt {
+ /// Multiply `a` with `b` and return the result.
+ public static func *(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
+ return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude * b.magnitude)
+ }
+
+ /// Multiply `a` with `b` in place.
+ public static func *=(a: inout CS.BigInt, b: CS.BigInt) { a = a * b }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+struct NoPadding: PaddingProtocol {
+ init() {
+ }
+
+ func add(to data: Array<UInt8>, blockSize _: Int) -> Array<UInt8> {
+ data
+ }
+
+ func remove(from data: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ data
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// The OCB Authenticated-Encryption Algorithm
+// https://tools.ietf.org/html/rfc7253
+//
+
+public final class OCB: BlockMode {
+
+ public enum Mode {
+ /// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.
+ case combined
+ /// Some applications may need to store the authentication tag and the encrypted message at different locations.
+ case detached
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired]
+
+ public enum Error: Swift.Error {
+ case invalidNonce
+ case fail
+ }
+
+ private let N: Array<UInt8>
+ private let additionalAuthenticatedData: Array<UInt8>?
+ private let mode: Mode
+ public let customBlockSize: Int? = nil
+
+ /// Length of authentication tag, in bytes.
+ /// For encryption, the value is given as init parameter.
+ /// For decryption, the length of given authentication tag is used.
+ private let tagLength: Int
+
+ // `authenticationTag` nil for encryption, known tag for decryption
+ /// For encryption, the value is set at the end of the encryption.
+ /// For decryption, this is a known Tag to validate against.
+ public var authenticationTag: Array<UInt8>?
+
+ // encrypt
+ public init(nonce N: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, tagLength: Int = 16, mode: Mode = .detached) {
+ self.N = N
+ self.additionalAuthenticatedData = additionalAuthenticatedData
+ self.mode = mode
+ self.tagLength = tagLength
+ }
+
+ // decrypt
+ @inlinable
+ public convenience init(nonce N: Array<UInt8>, authenticationTag: Array<UInt8>, additionalAuthenticatedData: Array<UInt8>? = nil, mode: Mode = .detached) {
+ self.init(nonce: N, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode)
+ self.authenticationTag = authenticationTag
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.N.isEmpty || self.N.count > 15 {
+ throw Error.invalidNonce
+ }
+
+ let worker = OCBModeWorker(N: N.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation, encryptionOperation: encryptionOperation)
+ worker.didCalculateTag = { [weak self] tag in
+ self?.authenticationTag = tag
+ }
+ return worker
+ }
+}
+
+// MARK: - Worker
+
+final class OCBModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker {
+
+ let cipherOperation: CipherOperationOnBlock
+ var hashOperation: CipherOperationOnBlock!
+
+ // Callback called when authenticationTag is ready
+ var didCalculateTag: ((Array<UInt8>) -> Void)?
+
+ private let tagLength: Int
+
+ let blockSize = 16 // 128 bit
+ var additionalBufferSize: Int
+ private let mode: OCB.Mode
+
+ // Additional authenticated data
+ private let aad: ArraySlice<UInt8>?
+ // Known Tag used to validate during decryption
+ private var expectedTag: Array<UInt8>?
+
+ /*
+ * KEY-DEPENDENT
+ */
+ // NOTE: elements are lazily calculated
+ private var l = [Array<UInt8>]()
+ private var lAsterisk: Array<UInt8>
+ private var lDollar: Array<UInt8>
+
+ /*
+ * PER-ENCRYPTION/DECRYPTION
+ */
+ private var mainBlockCount: UInt64
+ private var offsetMain: Array<UInt8>
+ private var checksum: Array<UInt8>
+
+ init(N: ArraySlice<UInt8>, aad: ArraySlice<UInt8>? = nil, expectedTag: Array<UInt8>? = nil, tagLength: Int, mode: OCB.Mode, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) {
+
+ self.cipherOperation = cipherOperation
+ self.hashOperation = encryptionOperation
+ self.mode = mode
+ self.aad = aad
+ self.expectedTag = expectedTag
+ self.tagLength = tagLength
+
+ if mode == .combined {
+ self.additionalBufferSize = tagLength
+ } else {
+ self.additionalBufferSize = 0
+ }
+
+ /*
+ * KEY-DEPENDENT INITIALIZATION
+ */
+
+ let zeros = Array<UInt8>(repeating: 0, count: self.blockSize)
+ self.lAsterisk = self.hashOperation(zeros.slice)! /// L_* = ENCIPHER(K, zeros(128))
+ self.lDollar = double(self.lAsterisk) /// L_$ = double(L_*)
+ self.l.append(double(self.lDollar)) /// L_0 = double(L_$)
+
+ /*
+ * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALIZATION
+ */
+
+ /// Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N
+ var nonce = Array<UInt8>(repeating: 0, count: blockSize)
+ nonce[(nonce.count - N.count)...] = N
+ nonce[0] = UInt8(tagLength) << 4
+ nonce[blockSize - 1 - N.count] |= 1
+
+ /// bottom = str2num(Nonce[123..128])
+ let bottom = nonce[15] & 0x3F
+
+ /// Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6))
+ nonce[15] &= 0xC0
+ let Ktop = self.hashOperation(nonce.slice)!
+
+ /// Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72])
+ let Stretch = Ktop + xor(Ktop[0..<8], Ktop[1..<9])
+
+ /// Offset_0 = Stretch[1+bottom..128+bottom]
+ var offsetMAIN_0 = Array<UInt8>(repeating: 0, count: blockSize)
+ let bits = bottom % 8
+ let bytes = Int(bottom / 8)
+ if bits == 0 {
+ offsetMAIN_0[0..<blockSize] = Stretch[bytes..<(bytes + blockSize)]
+ } else {
+ for i in 0..<self.blockSize {
+ let b1 = Stretch[bytes + i]
+ let b2 = Stretch[bytes + i + 1]
+ offsetMAIN_0[i] = ((b1 << bits) | (b2 >> (8 - bits)))
+ }
+ }
+
+ self.mainBlockCount = 0
+ self.offsetMain = Array<UInt8>(offsetMAIN_0.slice)
+ self.checksum = Array<UInt8>(repeating: 0, count: self.blockSize) /// Checksum_0 = zeros(128)
+ }
+
+ /// L_i = double(L_{i-1}) for every integer i > 0
+ func getLSub(_ n: Int) -> Array<UInt8> {
+ while n >= self.l.count {
+ self.l.append(double(self.l.last!))
+ }
+ return self.l[n]
+ }
+
+ func computeTag() -> Array<UInt8> {
+
+ let sum = self.hashAAD()
+
+ /// Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A)
+ return xor(self.hashOperation(xor(xor(self.checksum, self.offsetMain).slice, self.lDollar))!, sum)
+ }
+
+ func hashAAD() -> Array<UInt8> {
+ var sum = Array<UInt8>(repeating: 0, count: blockSize)
+
+ guard let aad = self.aad else {
+ return sum
+ }
+
+ var offset = Array<UInt8>(repeating: 0, count: blockSize)
+ var blockCount: UInt64 = 1
+ for aadBlock in aad.batched(by: self.blockSize) {
+
+ if aadBlock.count == self.blockSize {
+
+ /// Offset_i = Offset_{i-1} xor L_{ntz(i)}
+ offset = xor(offset, self.getLSub(ntz(blockCount)))
+
+ /// Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i)
+ sum = xor(sum, self.hashOperation(xor(aadBlock, offset))!)
+ } else {
+ if !aadBlock.isEmpty {
+
+ /// Offset_* = Offset_m xor L_*
+ offset = xor(offset, self.lAsterisk)
+
+ /// CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_*
+ let cipherInput: Array<UInt8> = xor(extend(aadBlock, size: blockSize), offset)
+
+ /// Sum = Sum_m xor ENCIPHER(K, CipherInput)
+ sum = xor(sum, self.hashOperation(cipherInput.slice)!)
+ }
+ }
+ blockCount += 1
+ }
+
+ return sum
+ }
+
+ func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+
+ if plaintext.count == self.blockSize {
+ return self.processBlock(block: plaintext, forEncryption: true)
+ } else {
+ return self.processFinalBlock(block: plaintext, forEncryption: true)
+ }
+ }
+
+ func finalize(encrypt ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+
+ let tag = self.computeTag()
+
+ self.didCalculateTag?(tag)
+
+ switch self.mode {
+ case .combined:
+ return ciphertext + tag
+ case .detached:
+ return ciphertext
+ }
+ }
+
+ func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+
+ if ciphertext.count == self.blockSize {
+ return self.processBlock(block: ciphertext, forEncryption: false)
+ } else {
+ return self.processFinalBlock(block: ciphertext, forEncryption: false)
+ }
+ }
+
+ func finalize(decrypt plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // do nothing
+ plaintext
+ }
+
+ private func processBlock(block: ArraySlice<UInt8>, forEncryption: Bool) -> Array<UInt8> {
+
+ /*
+ * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
+ */
+
+ self.mainBlockCount += 1
+
+ /// Offset_i = Offset_{i-1} xor L_{ntz(i)}
+ self.offsetMain = xor(self.offsetMain, self.getLSub(ntz(self.mainBlockCount)))
+
+ /// C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i)
+ /// P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i)
+ var mainBlock = Array<UInt8>(block)
+ mainBlock = xor(mainBlock, offsetMain)
+ mainBlock = self.cipherOperation(mainBlock.slice)!
+ mainBlock = xor(mainBlock, self.offsetMain)
+
+ /// Checksum_i = Checksum_{i-1} xor P_i
+ if forEncryption {
+ self.checksum = xor(self.checksum, block)
+ } else {
+ self.checksum = xor(self.checksum, mainBlock)
+ }
+
+ return mainBlock
+ }
+
+ private func processFinalBlock(block: ArraySlice<UInt8>, forEncryption: Bool) -> Array<UInt8> {
+
+ let out: Array<UInt8>
+
+ if block.isEmpty {
+ /// C_* = <empty string>
+ /// P_* = <empty string>
+ out = []
+
+ } else {
+
+ /// Offset_* = Offset_m xor L_*
+ self.offsetMain = xor(self.offsetMain, self.lAsterisk)
+
+ /// Pad = ENCIPHER(K, Offset_*)
+ let Pad = self.hashOperation(self.offsetMain.slice)!
+
+ /// C_* = P_* xor Pad[1..bitlen(P_*)]
+ /// P_* = C_* xor Pad[1..bitlen(C_*)]
+ out = xor(block, Pad[0..<block.count])
+
+ /// Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*)))
+ let plaintext = forEncryption ? block : out.slice
+ self.checksum = xor(self.checksum, extend(plaintext, size: self.blockSize))
+ }
+ return out
+ }
+
+ // The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single
+ // output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not
+ // authentic.
+ @discardableResult
+ func willDecryptLast(bytes ciphertext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // Validate tag
+ switch self.mode {
+ case .combined:
+ // overwrite expectedTag property used later for verification
+ self.expectedTag = Array(ciphertext.suffix(self.tagLength))
+ return ciphertext[ciphertext.startIndex..<ciphertext.endIndex.advanced(by: -Swift.min(tagLength, ciphertext.count))]
+ case .detached:
+ return ciphertext
+ }
+ }
+
+ func didDecryptLast(bytes plaintext: ArraySlice<UInt8>) throws -> ArraySlice<UInt8> {
+ // Calculate MAC tag.
+ let computedTag = self.computeTag()
+
+ // Validate tag
+ guard let expectedTag = self.expectedTag, computedTag == expectedTag else {
+ throw OCB.Error.fail
+ }
+
+ return plaintext
+ }
+}
+
+// MARK: - Local utils
+
+private func ntz(_ x: UInt64) -> Int {
+ if x == 0 {
+ return 64
+ }
+
+ var xv = x
+ var n = 0
+ while (xv & 1) == 0 {
+ n += 1
+ xv = xv >> 1
+ }
+ return n
+}
+
+private func double(_ block: Array<UInt8>) -> Array<UInt8> {
+ var ( carry, result) = shiftLeft(block)
+
+ /*
+ * NOTE: This construction is an attempt at a constant-time implementation.
+ */
+ result[15] ^= (0x87 >> ((1 - carry) << 3))
+
+ return result
+}
+
+private func shiftLeft(_ block: Array<UInt8>) -> (UInt8, Array<UInt8>) {
+ var output = Array<UInt8>(repeating: 0, count: block.count)
+
+ var bit: UInt8 = 0
+
+ for i in 0..<block.count {
+ let b = block[block.count - 1 - i]
+ output[block.count - 1 - i] = ((b << 1) | bit)
+ bit = (b >> 7) & 1
+ }
+ return (bit, output)
+}
+
+private func extend(_ block: ArraySlice<UInt8>, size: Int) -> Array<UInt8> {
+ var output = Array<UInt8>(repeating: 0, count: size)
+ output[0..<block.count] = block
+ output[block.count] |= 0x80
+ return output
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Output Feedback (OFB)
+//
+
+public struct OFB: BlockMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt]
+ private let iv: Array<UInt8>
+ public let customBlockSize: Int? = nil
+
+ public init(iv: Array<UInt8>) {
+ self.iv = iv
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.iv.count != blockSize {
+ throw Error.invalidInitializationVector
+ }
+
+ return OFBModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
+ }
+}
+
+struct OFBModeWorker: BlockModeWorker {
+ let cipherOperation: CipherOperationOnBlock
+ let blockSize: Int
+ let additionalBufferSize: Int = 0
+ private let iv: ArraySlice<UInt8>
+ private var prev: ArraySlice<UInt8>?
+
+ init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = blockSize
+ self.iv = iv
+ self.cipherOperation = cipherOperation
+ }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let ciphertext = cipherOperation(prev ?? iv) else {
+ return Array(plaintext)
+ }
+ self.prev = ciphertext.slice
+ return xor(plaintext, ciphertext)
+ }
+
+ @inlinable
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let decrypted = cipherOperation(prev ?? iv) else {
+ return Array(ciphertext)
+ }
+ let plaintext: Array<UInt8> = xor(decrypted, ciphertext)
+ prev = decrypted.slice
+ return plaintext
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/*
+ Bit shifting with overflow protection using overflow operator "&".
+ Approach is consistent with standard overflow operators &+, &-, &*, &/
+ and introduce new overflow operators for shifting: &<<, &>>
+
+ Note: Works with unsigned integers values only
+
+ Usage
+
+ var i = 1 // init
+ var j = i &<< 2 //shift left
+ j &<<= 2 //shift left and assign
+
+ @see: https://medium.com/@krzyzanowskim/swiftly-shift-bits-and-protect-yourself-be33016ce071
+
+ This fuctonality is now implemented as part of Swift 3, SE-0104 https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md
+ */
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public extension PKCS5 {
+ /// A key derivation function.
+ ///
+ /// PBKDF1 is recommended only for compatibility with existing
+ /// applications since the keys it produces may not be large enough for
+ /// some applications.
+ struct PBKDF1 {
+ public enum Error: Swift.Error {
+ case invalidInput
+ case derivedKeyTooLong
+ }
+
+ public enum Variant {
+ case md5, sha1
+
+ @usableFromInline
+ var size: Int {
+ switch self {
+ case .md5:
+ return MD5.digestLength
+ case .sha1:
+ return SHA1.digestLength
+ }
+ }
+
+ @usableFromInline
+ func calculateHash(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ switch self {
+ case .sha1:
+ return Digest.sha1(bytes)
+ case .md5:
+ return Digest.md5(bytes)
+ }
+ }
+ }
+
+ @usableFromInline
+ let iterations: Int // c
+
+ @usableFromInline
+ let variant: Variant
+
+ @usableFromInline
+ let keyLength: Int
+
+ @usableFromInline
+ let t1: Array<UInt8>
+
+ /// - parameters:
+ /// - salt: salt, an eight-bytes
+ /// - variant: hash variant
+ /// - iterations: iteration count, a positive integer
+ /// - keyLength: intended length of derived key
+ public init(password: Array<UInt8>, salt: Array<UInt8>, variant: Variant = .sha1, iterations: Int = 4096 /* c */, keyLength: Int? = nil /* dkLen */ ) throws {
+ precondition(iterations > 0)
+ precondition(salt.count == 8)
+
+ let keyLength = keyLength ?? variant.size
+
+ if keyLength > variant.size {
+ throw Error.derivedKeyTooLong
+ }
+
+ let t1 = variant.calculateHash(password + salt)
+
+ self.iterations = iterations
+ self.variant = variant
+ self.keyLength = keyLength
+ self.t1 = t1
+ }
+
+ /// Apply the underlying hash function Hash for c iterations
+ @inlinable
+ public func calculate() -> Array<UInt8> {
+ var t = self.t1
+ for _ in 2...self.iterations {
+ t = self.variant.calculateHash(t)
+ }
+ return Array(t[0..<self.keyLength])
+ }
+
+ public func callAsFunction() -> Array<UInt8> {
+ calculate()
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// https://www.ietf.org/rfc/rfc2898.txt
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#elseif canImport(WASILibc)
+import WASILibc
+#endif
+
+public extension PKCS5 {
+ /// A key derivation function.
+ ///
+ /// PBKDF2 - Password-Based CookiesKey Derivation Function 2. CookiesKey stretching technique.
+ /// DK = PBKDF2(PRF, Password, Salt, c, dkLen)
+ struct PBKDF2 {
+ public enum Error: Swift.Error {
+ case invalidInput
+ case derivedKeyTooLong
+ }
+
+ private let salt: Array<UInt8> // S
+ fileprivate let iterations: Int // c
+ private let numBlocks: Int // l
+ private let dkLen: Int
+ fileprivate let prf: HMAC
+
+ /// - parameters:
+ /// - salt: salt
+ /// - variant: hash variant
+ /// - iterations: iteration count, a positive integer
+ /// - keyLength: intended length of derived key
+ /// - variant: MAC variant. Defaults to SHA256
+ public init(password: Array<UInt8>, salt: Array<UInt8>, iterations: Int = 4096 /* c */, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha2(.sha256)) throws {
+ precondition(iterations > 0)
+
+ let prf = HMAC(key: password, variant: variant)
+
+ guard iterations > 0 && !salt.isEmpty else {
+ throw Error.invalidInput
+ }
+
+ self.dkLen = keyLength ?? variant.digestLength
+ let keyLengthFinal = Double(dkLen)
+ let hLen = Double(prf.variant.digestLength)
+ if keyLengthFinal > (pow(2, 32) - 1) * hLen {
+ throw Error.derivedKeyTooLong
+ }
+
+ self.salt = salt
+ self.iterations = iterations
+ self.prf = prf
+
+ self.numBlocks = Int(ceil(Double(keyLengthFinal) / hLen)) // l = ceil(keyLength / hLen)
+ }
+
+ public func calculate() throws -> Array<UInt8> {
+ var ret = Array<UInt8>()
+ ret.reserveCapacity(self.numBlocks * self.prf.variant.digestLength)
+ for i in 1...self.numBlocks {
+ // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
+ if let value = try calculateBlock(self.salt, blockNum: i) {
+ ret.append(contentsOf: value)
+ }
+ }
+ return Array(ret.prefix(self.dkLen))
+ }
+
+ public func callAsFunction() throws -> Array<UInt8> {
+ try calculate()
+ }
+ }
+}
+
+private extension PKCS5.PBKDF2 {
+ func ARR(_ i: Int) -> Array<UInt8> {
+ var inti = Array<UInt8>(repeating: 0, count: 4)
+ inti[0] = UInt8((i >> 24) & 0xff)
+ inti[1] = UInt8((i >> 16) & 0xff)
+ inti[2] = UInt8((i >> 8) & 0xff)
+ inti[3] = UInt8(i & 0xff)
+ return inti
+ }
+
+ // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
+ // U_1 = PRF (P, S || INT (i))
+ func calculateBlock(_ salt: Array<UInt8>, blockNum: Int) throws -> Array<UInt8>? {
+ guard let u1 = try? prf.authenticate(salt + ARR(blockNum)) else { // blockNum.bytes() is slower
+ return nil
+ }
+
+ var u = u1
+ var ret = u
+ if iterations > 1 {
+ // U_2 = PRF (P, U_1) ,
+ // U_c = PRF (P, U_{c-1}) .
+ for _ in 2...iterations {
+ u = try prf.authenticate(u)
+ for x in 0..<ret.count {
+ ret[x] = ret[x] ^ u[x]
+ }
+ }
+ }
+ return ret
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Propagating Cipher Block Chaining (PCBC)
+//
+
+public struct PCBC: BlockMode {
+ public enum Error: Swift.Error {
+ /// Invalid IV
+ case invalidInitializationVector
+ }
+
+ public let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired]
+ private let iv: Array<UInt8>
+ public let customBlockSize: Int? = nil
+
+ public init(iv: Array<UInt8>) {
+ self.iv = iv
+ }
+
+ public func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker {
+ if self.iv.count != blockSize {
+ throw Error.invalidInitializationVector
+ }
+
+ return PCBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation)
+ }
+}
+
+struct PCBCModeWorker: BlockModeWorker {
+ let cipherOperation: CipherOperationOnBlock
+ var blockSize: Int
+ let additionalBufferSize: Int = 0
+ private let iv: ArraySlice<UInt8>
+ private var prev: ArraySlice<UInt8>?
+
+ @inlinable
+ init(blockSize: Int, iv: ArraySlice<UInt8>, cipherOperation: @escaping CipherOperationOnBlock) {
+ self.blockSize = blockSize
+ self.iv = iv
+ self.cipherOperation = cipherOperation
+ }
+
+ @inlinable
+ mutating func encrypt(block plaintext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else {
+ return Array(plaintext)
+ }
+ self.prev = xor(plaintext, ciphertext.slice)
+ return ciphertext
+ }
+
+ @inlinable
+ mutating func decrypt(block ciphertext: ArraySlice<UInt8>) -> Array<UInt8> {
+ guard let plaintext = cipherOperation(ciphertext) else {
+ return Array(ciphertext)
+ }
+ let result: Array<UInt8> = xor(prev ?? self.iv, plaintext)
+ self.prev = xor(result, ciphertext)
+ return result
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// PKCS is a group of public-key cryptography standards devised
+// and published by RSA Security Inc, starting in the early 1990s.
+//
+
+/// EMSA PKCS1 v1.5 Padding Scheme
+///
+/// The EMSA Version of the PKCS1 v1.5 padding scheme is **deterministic** (it pads the messages contents with 255 value bytes)
+/// ```
+/// // The returned structure
+/// // - PS is the applied padding
+/// // - M is your original Message
+/// EM = 0x00 || 0x01 || PS || 0x00 || M.
+/// ```
+/// - Note: This Padding scheme is intended to be used for encoding RSA Signatures
+///
+/// [EMSA-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2)
+struct EMSAPKCS1v15Padding: PaddingProtocol {
+
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ var r = blockSize - ((bytes.count + 3) % blockSize)
+ if r <= 0 { r = blockSize - 3 }
+
+ return [0x00, 0x01] + Array<UInt8>(repeating: 0xFF, count: r) + [0x00] + bytes
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+ assert(bytes.prefix(2) == [0x00, 0x01], "Invalid padding prefix")
+
+ guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes }
+
+ guard (paddingLength + 1) <= bytes.count else { return bytes }
+
+ return Array(bytes[(paddingLength + 1)...])
+ }
+}
+
+/// EME PKCS1 v1.5 Padding Scheme
+///
+/// The EME Version of the PKCS1 v1.5 padding scheme is **non deterministic** (it pads the messages contents with psuedo-random bytes)
+/// ```
+/// // The returned structure
+/// // - PS is the applied padding
+/// // - M is your original Message
+/// EM = 0x00 || 0x02 || PS || 0x00 || M.
+/// ```
+/// - Note: This Padding scheme is intended to be used for encoding messages before RSA Encryption
+///
+/// [EME-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-7.2.1)
+struct EMEPKCS1v15Padding: PaddingProtocol {
+
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ var r = blockSize - ((bytes.count + 3) % blockSize)
+ if r <= 0 { r = blockSize - 3 }
+
+ return [0x00, 0x02] + (0..<r).map { _ in UInt8.random(in: 1...UInt8.max) } + [0x00] + bytes
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+ assert(bytes.prefix(2) == [0x00, 0x02], "Invalid padding prefix")
+
+ guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes }
+
+ guard (paddingLength + 1) <= bytes.count else { return bytes }
+
+ return Array(bytes[(paddingLength + 1)...])
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// PKCS is a group of public-key cryptography standards devised
+// and published by RSA Security Inc, starting in the early 1990s.
+//
+
+public enum PKCS5 {
+ typealias Padding = PKCS7Padding
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public enum PKCS7 {
+ typealias Padding = PKCS7Padding
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// PKCS is a group of public-key cryptography standards devised
+// and published by RSA Security Inc, starting in the early 1990s.
+//
+
+struct PKCS7Padding: PaddingProtocol {
+ enum Error: Swift.Error {
+ case invalidPaddingValue
+ }
+
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ let padding = UInt8(blockSize - (bytes.count % blockSize))
+ var withPadding = bytes
+ if padding == 0 {
+ // If the original data is a multiple of N bytes, then an extra block of bytes with value N is added.
+ withPadding += Array<UInt8>(repeating: UInt8(blockSize), count: Int(blockSize))
+ } else {
+ // The value of each added byte is the number of bytes that are added
+ withPadding += Array<UInt8>(repeating: padding, count: Int(padding))
+ }
+ return withPadding
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ guard !bytes.isEmpty, let lastByte = bytes.last else {
+ return bytes
+ }
+
+ assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+ let padding = Int(lastByte) // last byte
+ let finalLength = bytes.count - padding
+
+ if finalLength < 0 {
+ return bytes
+ }
+
+ if padding >= 1 {
+ return Array(bytes[0..<finalLength])
+ }
+ return bytes
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public protocol PaddingProtocol {
+ func add(to: Array<UInt8>, blockSize: Int) -> Array<UInt8>
+ func remove(from: Array<UInt8>, blockSize: Int?) -> Array<UInt8>
+}
+
+public enum Padding: PaddingProtocol {
+ case noPadding, zeroPadding, pkcs7, pkcs5, eme_pkcs1v15, emsa_pkcs1v15, iso78164, iso10126
+
+ public func add(to: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ switch self {
+ case .noPadding:
+ return to // NoPadding().add(to: to, blockSize: blockSize)
+ case .zeroPadding:
+ return ZeroPadding().add(to: to, blockSize: blockSize)
+ case .pkcs7:
+ return PKCS7.Padding().add(to: to, blockSize: blockSize)
+ case .pkcs5:
+ return PKCS5.Padding().add(to: to, blockSize: blockSize)
+ case .eme_pkcs1v15:
+ return EMEPKCS1v15Padding().add(to: to, blockSize: blockSize)
+ case .emsa_pkcs1v15:
+ return EMSAPKCS1v15Padding().add(to: to, blockSize: blockSize)
+ case .iso78164:
+ return ISO78164Padding().add(to: to, blockSize: blockSize)
+ case .iso10126:
+ return ISO10126Padding().add(to: to, blockSize: blockSize)
+ }
+ }
+
+ public func remove(from: Array<UInt8>, blockSize: Int?) -> Array<UInt8> {
+ switch self {
+ case .noPadding:
+ return from //NoPadding().remove(from: from, blockSize: blockSize)
+ case .zeroPadding:
+ return ZeroPadding().remove(from: from, blockSize: blockSize)
+ case .pkcs7:
+ return PKCS7.Padding().remove(from: from, blockSize: blockSize)
+ case .pkcs5:
+ return PKCS5.Padding().remove(from: from, blockSize: blockSize)
+ case .eme_pkcs1v15:
+ return EMEPKCS1v15Padding().remove(from: from, blockSize: blockSize)
+ case .emsa_pkcs1v15:
+ return EMSAPKCS1v15Padding().remove(from: from, blockSize: blockSize)
+ case .iso78164:
+ return ISO78164Padding().remove(from: from, blockSize: blockSize)
+ case .iso10126:
+ return ISO10126Padding().remove(from: from, blockSize: blockSize)
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-4
+// nacl/crypto_onetimeauth/poly1305/ref/auth.c
+//
+/// Poly1305 takes a 32-byte, one-time key and a message and produces a 16-byte tag that authenticates the
+/// message such that an attacker has a negligible chance of producing a valid tag for an inauthentic message.
+
+public final class Poly1305: Authenticator {
+ public enum Error: Swift.Error {
+ case authenticateError
+ }
+
+ public static let blockSize: Int = 16
+
+ private let key: SecureBytes
+
+ /// - parameter key: 32-byte key
+ public init(key: Array<UInt8>) {
+ self.key = SecureBytes(bytes: key)
+ }
+
+ private func squeeze(h: inout Array<UInt32>) {
+ assert(h.count == 17)
+ var u: UInt32 = 0
+ for j in 0..<16 {
+ u = u &+ h[j]
+ h[j] = u & 255
+ u = u >> 8
+ }
+
+ u = u &+ h[16]
+ h[16] = u & 3
+ u = 5 * (u >> 2)
+
+ for j in 0..<16 {
+ u = u &+ h[j]
+ h[j] = u & 255
+ u = u >> 8
+ }
+
+ u = u &+ h[16]
+ h[16] = u
+ }
+
+ private func add(h: inout Array<UInt32>, c: Array<UInt32>) {
+ assert(h.count == 17 && c.count == 17)
+
+ var u: UInt32 = 0
+ for j in 0..<17 {
+ u = u &+ (h[j] &+ c[j])
+ h[j] = u & 255
+ u = u >> 8
+ }
+ }
+
+ private func mulmod(h: inout Array<UInt32>, r: Array<UInt32>) {
+ var hr = Array<UInt32>(repeating: 0, count: 17)
+ var u: UInt32 = 0
+ for i in 0..<17 {
+ u = 0
+ for j in 0...i {
+ u = u &+ (h[j] * r[i &- j])
+ }
+ for j in (i + 1)..<17 {
+ u = u &+ (320 * h[j] * r[i &+ 17 &- j])
+ }
+ hr[i] = u
+ }
+ h = hr
+ self.squeeze(h: &h)
+ }
+
+ private func freeze(h: inout Array<UInt32>) {
+ let horig = h
+ self.add(h: &h, c: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252])
+ let negative = UInt32(bitPattern: -Int32(h[16] >> 7))
+ for j in 0..<17 {
+ h[j] ^= negative & (horig[j] ^ h[j])
+ }
+ }
+
+ /// the key is partitioned into two parts, called "r" and "s"
+ fileprivate func onetimeauth(message input: Array<UInt8>, key k: Array<UInt8>) -> Array<UInt8> {
+ // clamp
+ var r = Array<UInt32>(repeating: 0, count: 17)
+ var h = Array<UInt32>(repeating: 0, count: 17)
+ var c = Array<UInt32>(repeating: 0, count: 17)
+
+ r[0] = UInt32(k[0])
+ r[1] = UInt32(k[1])
+ r[2] = UInt32(k[2])
+ r[3] = UInt32(k[3] & 15)
+ r[4] = UInt32(k[4] & 252)
+ r[5] = UInt32(k[5])
+ r[6] = UInt32(k[6])
+ r[7] = UInt32(k[7] & 15)
+ r[8] = UInt32(k[8] & 252)
+ r[9] = UInt32(k[9])
+ r[10] = UInt32(k[10])
+ r[11] = UInt32(k[11] & 15)
+ r[12] = UInt32(k[12] & 252)
+ r[13] = UInt32(k[13])
+ r[14] = UInt32(k[14])
+ r[15] = UInt32(k[15] & 15)
+ r[16] = 0
+
+ var inlen = input.count
+ var inpos = 0
+ while inlen > 0 {
+ for j in 0..<c.count {
+ c[j] = 0
+ }
+
+ let maxj = min(inlen, 16)
+ for j in 0..<maxj {
+ c[j] = UInt32(input[inpos + j])
+ }
+ c[maxj] = 1
+ inpos = inpos + maxj
+ inlen = inlen - maxj
+ self.add(h: &h, c: c)
+ self.mulmod(h: &h, r: r)
+ }
+
+ self.freeze(h: &h)
+
+ for j in 0..<16 {
+ c[j] = UInt32(k[j + 16])
+ }
+ c[16] = 0
+ self.add(h: &h, c: c)
+
+ return h[0..<16].map {
+ UInt8($0 & 0xff)
+ }
+ }
+
+ // MARK: - Authenticator
+
+ /**
+ Calculate Message Authentication Code (MAC) for message.
+ Calculation context is discarder on instance deallocation.
+
+ - parameter bytes: Message
+
+ - returns: 16-byte tag that authenticates the message
+ */
+ public func authenticate(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ self.onetimeauth(message: bytes, key: Array(self.key))
+ }
+}
--- /dev/null
+//
+// Prime Test.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-04.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+/// The first several [prime numbers][primes].
+///
+/// [primes]: https://oeis.org/A000040
+let primes: [CS.BigUInt.Word] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
+
+/// The ith element in this sequence is the smallest composite number that passes the strong probable prime test
+/// for all of the first (i+1) primes.
+///
+/// This is sequence [A014233](http://oeis.org/A014233) on the [Online Encyclopaedia of Integer Sequences](http://oeis.org).
+let pseudoPrimes: [CS.BigUInt] = [
+ /* 2 */ 2_047,
+ /* 3 */ 1_373_653,
+ /* 5 */ 25_326_001,
+ /* 7 */ 3_215_031_751,
+ /* 11 */ 2_152_302_898_747,
+ /* 13 */ 3_474_749_660_383,
+ /* 17 */ 341_550_071_728_321,
+ /* 19 */ 341_550_071_728_321,
+ /* 23 */ 3_825_123_056_546_413_051,
+ /* 29 */ 3_825_123_056_546_413_051,
+ /* 31 */ 3_825_123_056_546_413_051,
+ /* 37 */ "318665857834031151167461",
+ /* 41 */ "3317044064679887385961981",
+]
+
+extension CS.BigUInt {
+ //MARK: Primality Testing
+
+ /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base.
+ ///
+ /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime
+ public func isStrongProbablePrime(_ base: CS.BigUInt) -> Bool {
+ precondition(base > (1 as CS.BigUInt))
+ precondition(self > (0 as CS.BigUInt))
+ let dec = self - 1
+
+ let r = dec.trailingZeroBitCount
+ let d = dec >> r
+
+ var test = base.power(d, modulus: self)
+ if test == 1 || test == dec { return true }
+
+ if r > 0 {
+ let shift = self.leadingZeroBitCount
+ let normalized = self << shift
+ for _ in 1 ..< r {
+ test *= test
+ test.formRemainder(dividingBy: normalized, normalizedBy: shift)
+ if test == 1 {
+ return false
+ }
+ if test == dec { return true }
+ }
+ }
+ return false
+ }
+
+ /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime.
+ ///
+ /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations,
+ /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default,
+ /// but you may specify your own choice.
+ ///
+ /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before
+ /// diving into (slower) Miller-Rabin testing.
+ ///
+ /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to
+ /// return a correct result.
+ ///
+ /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
+ public func isPrime(rounds: Int = 10) -> Bool {
+ if count <= 1 && self[0] < 2 { return false }
+ if count == 1 && self[0] < 4 { return true }
+
+ // Even numbers above 2 aren't prime.
+ if self[0] & 1 == 0 { return false }
+
+ // Quickly check for small primes.
+ for i in 1 ..< primes.count {
+ let p = primes[i]
+ if self.count == 1 && self[0] == p {
+ return true
+ }
+ if self.quotientAndRemainder(dividingByWord: p).remainder == 0 {
+ return false
+ }
+ }
+
+ /// Give an exact answer when we can.
+ if self < pseudoPrimes.last! {
+ for i in 0 ..< pseudoPrimes.count {
+ guard isStrongProbablePrime(CS.BigUInt(primes[i])) else {
+ break
+ }
+ if self < pseudoPrimes[i] {
+ // `self` is below the lowest pseudoprime corresponding to the prime bases we tested. It's a prime!
+ return true
+ }
+ }
+ return false
+ }
+
+ /// Otherwise do as many rounds of random SPPT as required.
+ for _ in 0 ..< rounds {
+ let random = CS.BigUInt.randomInteger(lessThan: self - 2) + 2
+ guard isStrongProbablePrime(random) else {
+ return false
+ }
+ }
+
+ // Well, it smells primey to me.
+ return true
+ }
+}
+
+extension CS.BigInt {
+ //MARK: Primality Testing
+
+ /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base.
+ ///
+ /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime
+ public func isStrongProbablePrime(_ base: CS.BigInt) -> Bool {
+ precondition(base.sign == .plus)
+ if self.sign == .minus { return false }
+ return self.magnitude.isStrongProbablePrime(base.magnitude)
+ }
+
+ /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime.
+ ///
+ /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations,
+ /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default,
+ /// but you may specify your own choice.
+ ///
+ /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before
+ /// diving into (slower) Miller-Rabin testing.
+ ///
+ /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to
+ /// return a correct result.
+ ///
+ /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
+ public func isPrime(rounds: Int = 10) -> Bool {
+ if self.sign == .minus { return false }
+ return self.magnitude.isPrime(rounds: rounds)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+// MARK: Cipher
+
+extension RSA: Cipher {
+
+ @inlinable
+ public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ return try self.encrypt(Array<UInt8>(bytes), variant: .pksc1v15)
+ }
+
+ @inlinable
+ public func encrypt(_ bytes: Array<UInt8>, variant: RSAEncryptionVariant) throws -> Array<UInt8> {
+ // Prepare the data for the specified variant
+ let preparedData = try variant.prepare(bytes, blockSize: self.keySizeBytes)
+
+ // Encrypt the prepared data
+ return try variant.formatEncryptedBytes(self.encryptPreparedBytes(preparedData), blockSize: self.keySizeBytes)
+ }
+
+ @inlinable
+ internal func encryptPreparedBytes(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ // Calculate encrypted data
+ return BigUInteger(Data(bytes)).power(self.e, modulus: self.n).serialize().bytes
+ }
+
+ @inlinable
+ public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ return try self.decrypt(Array<UInt8>(bytes), variant: .pksc1v15)
+ }
+
+ @inlinable
+ public func decrypt(_ bytes: Array<UInt8>, variant: RSAEncryptionVariant) throws -> Array<UInt8> {
+ // Decrypt the data
+ let decrypted = try self.decryptPreparedBytes(bytes)
+
+ // Remove padding / unstructure data and return the raw plaintext
+ return variant.removePadding(decrypted, blockSize: self.keySizeBytes)
+ }
+
+ @inlinable
+ internal func decryptPreparedBytes(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ // Check for Private Exponent presence
+ guard let d = d else { throw RSA.Error.noPrivateKey }
+
+ // Calculate decrypted data
+ return BigUInteger(Data(bytes)).power(d, modulus: self.n).serialize().bytes
+ }
+}
+
+extension RSA {
+ /// RSA Encryption Block Types
+ /// - [RFC2313 8.1 - Encryption block formatting](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1)
+ @frozen
+ public enum RSAEncryptionVariant {
+ /// The `unsafe` encryption variant, is fully deterministic and doesn't format the inbound data in any way.
+ ///
+ /// - Warning: This is considered an unsafe method of encryption.
+ case unsafe
+ /// The `raw` encryption variant formats the inbound data with a deterministic padding scheme.
+ ///
+ /// - Warning: This is also considered to be an unsafe method of encryption, but matches the `Security` frameworks functionality.
+ case raw
+ /// The `pkcs1v15` encryption variant formats the inbound data with a non deterministic pseudo random padding scheme.
+ ///
+ /// [EME PKCS1v1.5 Padding Scheme Spec](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1)
+ case pksc1v15
+
+ @inlinable
+ internal func prepare(_ bytes: Array<UInt8>, blockSize: Int) throws -> Array<UInt8> {
+ switch self {
+ case .unsafe:
+ return bytes
+ case .raw:
+ // We need at least 11 bytes of padding in order to safely encrypt messages
+ // - block types 1 and 2 have this minimum padding requirement, block type 0 isn't specified, but we enforce the minimum padding length here to be safe.
+ guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption }
+ return Array(repeating: 0x00, count: blockSize - bytes.count) + bytes
+ case .pksc1v15:
+ // The `Security` framework refuses to encrypt a zero byte message using the pkcs1v15 padding scheme, so we do the same
+ guard !bytes.isEmpty else { throw RSA.Error.invalidMessageLengthForEncryption }
+ // We need at least 11 bytes of random padding in order to safely encrypt messages (RFC2313 Section 8.1 - Note 6)
+ guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption }
+ return Padding.eme_pkcs1v15.add(to: bytes, blockSize: blockSize)
+ }
+ }
+
+ @inlinable
+ internal func formatEncryptedBytes(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ switch self {
+ case .unsafe:
+ return bytes
+ case .raw, .pksc1v15:
+ // Format the encrypted bytes before returning
+ return Array<UInt8>(repeating: 0x00, count: blockSize - bytes.count) + bytes
+ }
+ }
+
+ @inlinable
+ internal func removePadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ switch self {
+ case .unsafe:
+ return bytes
+ case .raw:
+ return bytes
+ case .pksc1v15:
+ // Convert the Octet String into an Integer Primitive using the BigInteger `serialize` method
+ // (this effectively just prefixes the data with a 0x00 byte indicating that its a positive integer)
+ return Padding.eme_pkcs1v15.remove(from: [0x00] + bytes, blockSize: blockSize)
+ }
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+// MARK: Signatures & Verification
+
+extension RSA: Signature {
+ public func sign(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ try self.sign(Array(bytes), variant: .message_pkcs1v15_SHA256)
+ }
+
+ /// Signs the data using the Private key and the specified signature variant
+ /// - Parameters:
+ /// - bytes: The data to be signed
+ /// - variant: The variant to use (`digest` variants expect a pre-hashed digest matching that of the specified hash function, `message` variants will hash the data using the specified hash function before signing it)
+ /// - Returns: The signature of the data
+ public func sign(_ bytes: Array<UInt8>, variant: SignatureVariant) throws -> Array<UInt8> {
+ // Check for Private Exponent presence
+ guard let d = d else { throw RSA.Error.noPrivateKey }
+
+ // Hash & Encode Message
+ let hashedAndEncoded = try RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySizeBytes)
+
+ /// Calculate the Signature
+ let signedData = BigUInteger(Data(hashedAndEncoded)).power(d, modulus: self.n).serialize().bytes
+
+ return variant.formatSignedBytes(signedData, blockSize: self.keySizeBytes)
+ }
+
+ public func verify(signature: ArraySlice<UInt8>, for expectedData: ArraySlice<UInt8>) throws -> Bool {
+ try self.verify(signature: Array(signature), for: Array(expectedData), variant: .message_pkcs1v15_SHA256)
+ }
+
+ /// Verifies whether a Signature is valid for the provided data
+ /// - Parameters:
+ /// - signature: The signature to verify
+ /// - expectedData: The original data that you expected to have been signed
+ /// - variant: The variant used to sign the data
+ /// - Returns: `True` when the signature is valid for the expected data, `False` otherwise.
+ ///
+ /// [IETF Verification Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-8.2.2)
+ public func verify(signature: Array<UInt8>, for bytes: Array<UInt8>, variant: SignatureVariant) throws -> Bool {
+ /// Step 1: Ensure the signature is the same length as the key's modulus
+ guard signature.count == self.keySizeBytes else { throw Error.invalidSignatureLength }
+
+ /// Prepare the expected message for signature comparison
+ var expectedData = try RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySizeBytes)
+ if expectedData.count == self.keySizeBytes && expectedData.prefix(1) == [0x00] { expectedData = Array(expectedData.dropFirst()) }
+
+ /// Step 2: 'Decrypt' the signature
+ let signatureResult = BigUInteger(Data(signature)).power(self.e, modulus: self.n).serialize().bytes
+
+ /// Step 3: Compare the 'decrypted' signature with the prepared / encoded expected message....
+ guard signatureResult == expectedData else { return false }
+
+ return true
+ }
+
+ /// Hashes and Encodes a message for signing and verifying
+ ///
+ /// - Note: [EMSA-PKCS1-v1_5](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2)
+ fileprivate static func hashedAndEncoded(_ bytes: [UInt8], variant: SignatureVariant, keySizeInBytes: Int) throws -> Array<UInt8> {
+ /// 1. Apply the hash function to the message M to produce a hash
+ let hashedMessage = variant.calculateHash(bytes)
+
+ guard variant.enforceLength(hashedMessage, keySizeInBytes: keySizeInBytes) else { throw RSA.Error.invalidMessageLengthForSigning }
+
+ /// 2. Encode the algorithm ID for the hash function and the hash value into an ASN.1 value of type DigestInfo
+ /// PKCS#1_15 DER Structure (OID == sha256WithRSAEncryption)
+ let t = variant.encode(hashedMessage)
+
+ if case .raw = variant { return t }
+
+ /// 3. If emLen < tLen + 11, output "intended encoded message length too short" and stop
+ if keySizeInBytes < t.count + 11 { throw RSA.Error.invalidMessageLengthForSigning }
+
+ /// 4. Generate an octet string PS consisting of emLen - tLen - 3
+ /// octets with hexadecimal value 0xff. The length of PS will be
+ /// at least 8 octets.
+ /// 5. Concatenate PS, the DER encoding T, and other padding to form
+ /// the encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T.
+ let padded = variant.pad(bytes: t, to: keySizeInBytes)
+
+ /// Ensure the signature is of the correct length
+ guard padded.count == keySizeInBytes else { throw RSA.Error.invalidMessageLengthForSigning }
+
+ return padded
+ }
+}
+
+extension RSA {
+ public enum SignatureVariant {
+ /// rsaSignatureRaw
+ case raw
+ /// Hashes the raw message using MD5 before signing the data
+ case message_pkcs1v15_MD5
+ /// Hashes the raw message using SHA1 before signing the data
+ case message_pkcs1v15_SHA1
+ /// Hashes the raw message using SHA224 before signing the data
+ case message_pkcs1v15_SHA224
+ /// Hashes the raw message using SHA256 before signing the data
+ case message_pkcs1v15_SHA256
+ /// Hashes the raw message using SHA384 before signing the data
+ case message_pkcs1v15_SHA384
+ /// Hashes the raw message using SHA512 before signing the data
+ case message_pkcs1v15_SHA512
+ /// Hashes the raw message using SHA512-224 before signing the data
+ case message_pkcs1v15_SHA512_224
+ /// Hashes the raw message using SHA512-256 before signing the data
+ case message_pkcs1v15_SHA512_256
+ /// This variant isn't supported yet
+ case digest_pkcs1v15_RAW
+ /// This variant expects that the data to be signed is a valid MD5 Hash Digest
+ case digest_pkcs1v15_MD5
+ /// This variant expects that the data to be signed is a valid SHA1 Hash Digest
+ case digest_pkcs1v15_SHA1
+ /// This variant expects that the data to be signed is a valid SHA224 Hash Digest
+ case digest_pkcs1v15_SHA224
+ /// This variant expects that the data to be signed is a valid SHA256 Hash Digest
+ case digest_pkcs1v15_SHA256
+ /// This variant expects that the data to be signed is a valid SHA384 Hash Digest
+ case digest_pkcs1v15_SHA384
+ /// This variant expects that the data to be signed is a valid SHA512 Hash Digest
+ case digest_pkcs1v15_SHA512
+ /// This variant expects that the data to be signed is a valid SHA512-224 Hash Digest
+ case digest_pkcs1v15_SHA512_224
+ /// This variant expects that the data to be signed is a valid SHA512-256 Hash Digest
+ case digest_pkcs1v15_SHA512_256
+
+ internal var identifier: Array<UInt8> {
+ switch self {
+ case .raw, .digest_pkcs1v15_RAW: return []
+ case .message_pkcs1v15_MD5, .digest_pkcs1v15_MD5: return Array<UInt8>(arrayLiteral: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05)
+ case .message_pkcs1v15_SHA1, .digest_pkcs1v15_SHA1: return Array<UInt8>(arrayLiteral: 0x2b, 0x0e, 0x03, 0x02, 0x1a)
+ case .message_pkcs1v15_SHA256, .digest_pkcs1v15_SHA256: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01)
+ case .message_pkcs1v15_SHA384, .digest_pkcs1v15_SHA384: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02)
+ case .message_pkcs1v15_SHA512, .digest_pkcs1v15_SHA512: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03)
+ case .message_pkcs1v15_SHA224, .digest_pkcs1v15_SHA224: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04)
+ case .message_pkcs1v15_SHA512_224, .digest_pkcs1v15_SHA512_224: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05)
+ case .message_pkcs1v15_SHA512_256, .digest_pkcs1v15_SHA512_256: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06)
+ }
+ }
+
+ internal func calculateHash(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ switch self {
+ case .message_pkcs1v15_MD5:
+ return Digest.md5(bytes)
+ case .message_pkcs1v15_SHA1:
+ return Digest.sha1(bytes)
+ case .message_pkcs1v15_SHA224:
+ return Digest.sha224(bytes)
+ case .message_pkcs1v15_SHA256:
+ return Digest.sha256(bytes)
+ case .message_pkcs1v15_SHA384:
+ return Digest.sha384(bytes)
+ case .message_pkcs1v15_SHA512:
+ return Digest.sha512(bytes)
+ case .message_pkcs1v15_SHA512_224:
+ return Digest.sha2(bytes, variant: .sha224)
+ case .message_pkcs1v15_SHA512_256:
+ return Digest.sha2(bytes, variant: .sha256)
+ case .raw,
+ .digest_pkcs1v15_RAW,
+ .digest_pkcs1v15_MD5,
+ .digest_pkcs1v15_SHA1,
+ .digest_pkcs1v15_SHA224,
+ .digest_pkcs1v15_SHA256,
+ .digest_pkcs1v15_SHA384,
+ .digest_pkcs1v15_SHA512,
+ .digest_pkcs1v15_SHA512_224,
+ .digest_pkcs1v15_SHA512_256:
+ return bytes
+ }
+ }
+
+ internal func enforceLength(_ bytes: Array<UInt8>, keySizeInBytes: Int) -> Bool {
+ switch self {
+ case .raw, .digest_pkcs1v15_RAW:
+ return bytes.count <= keySizeInBytes
+ case .digest_pkcs1v15_MD5:
+ return bytes.count <= 16
+ case .digest_pkcs1v15_SHA1:
+ return bytes.count <= 20
+ case .digest_pkcs1v15_SHA224:
+ return bytes.count <= 28
+ case .digest_pkcs1v15_SHA256:
+ return bytes.count <= 32
+ case .digest_pkcs1v15_SHA384:
+ return bytes.count <= 48
+ case .digest_pkcs1v15_SHA512:
+ return bytes.count <= 64
+ case .digest_pkcs1v15_SHA512_224:
+ return bytes.count <= 28
+ case .digest_pkcs1v15_SHA512_256:
+ return bytes.count <= 32
+ case .message_pkcs1v15_MD5,
+ .message_pkcs1v15_SHA1,
+ .message_pkcs1v15_SHA224,
+ .message_pkcs1v15_SHA256,
+ .message_pkcs1v15_SHA384,
+ .message_pkcs1v15_SHA512,
+ .message_pkcs1v15_SHA512_224,
+ .message_pkcs1v15_SHA512_256:
+ return true
+ }
+ }
+
+ internal func encode(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ switch self {
+ case .raw, .digest_pkcs1v15_RAW:
+ return bytes
+
+ default:
+ let asn: ASN1.Node = .sequence(nodes: [
+ .sequence(nodes: [
+ .objectIdentifier(data: Data(self.identifier)),
+ .null
+ ]),
+ .octetString(data: Data(bytes))
+ ])
+
+ return ASN1.Encoder.encode(asn)
+ }
+ }
+
+ /// Right now the only Padding Scheme supported is [EMCS-PKCS1v15](https://www.rfc-editor.org/rfc/rfc8017#section-9.2) (others include [EMSA-PSS](https://www.rfc-editor.org/rfc/rfc8017#section-9.1))
+ internal func pad(bytes: Array<UInt8>, to blockSize: Int) -> Array<UInt8> {
+ switch self {
+ case .raw:
+ return bytes
+ default:
+ return Padding.emsa_pkcs1v15.add(to: bytes, blockSize: blockSize)
+ }
+ }
+
+ /// Zero pads a signature to the specified block size
+ /// - Parameters:
+ /// - bytes: The signed bytes
+ /// - blockSize: The block size to pad until
+ /// - Returns: A zero padded (prepended) bytes array of length blockSize
+ internal func formatSignedBytes(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ switch self {
+ default:
+ // Format the encrypted bytes before returning
+ return Array<UInt8>(repeating: 0x00, count: blockSize - bytes.count) + bytes
+ }
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Foundation is required for `Data` to be found
+import Foundation
+
+// Note: The `BigUInt` struct was copied from:
+// https://github.com/attaswift/BigInt
+// It allows fast calculation for RSA big numbers
+
+public final class RSA: DERCodable {
+ /// RSA CookiesKey Errors
+ public enum Error: Swift.Error {
+ /// No private key specified
+ case noPrivateKey
+ /// Failed to calculate the inverse e and phi
+ case invalidInverseNotCoprimes
+ /// We only support Version 0 RSA keys (we don't support Version 1 introduced in RFC 3447)
+ case unsupportedRSAVersion
+ /// Failed to verify primes during initialiization (the provided primes don't reproduce the provided private exponent)
+ case invalidPrimes
+ /// We attempted to export a private key without our underlying primes
+ case noPrimes
+ /// Unable to calculate the coefficient during a private key import / export
+ case unableToCalculateCoefficient
+ /// The signature to verify is of an invalid length
+ case invalidSignatureLength
+ /// The message to be signed is of an invalid length
+ case invalidMessageLengthForSigning
+ /// The message to be encrypted is of an invalid length
+ case invalidMessageLengthForEncryption
+ /// The error thrown when Decryption fails
+ case invalidDecryption
+ }
+
+ /// RSA Modulus
+ public let n: BigUInteger
+
+ /// RSA Public Exponent
+ public let e: BigUInteger
+
+ /// RSA Private Exponent
+ public let d: BigUInteger?
+
+ /// The size of the modulus, in bits
+ public let keySize: Int
+
+ /// The size of the modulus, in bytes (rounded up to the nearest full byte)
+ public let keySizeBytes: Int
+
+ /// The underlying primes used to generate the Private Exponent
+ private let primes: (p: BigUInteger, q: BigUInteger)?
+
+ /// Initialize with RSA parameters
+ /// - Parameters:
+ /// - n: The RSA Modulus
+ /// - e: The RSA Public Exponent
+ /// - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
+ public init(n: BigUInteger, e: BigUInteger, d: BigUInteger? = nil) {
+ self.n = n
+ self.e = e
+ self.d = d
+ self.primes = nil
+
+ self.keySize = n.bitWidth
+ self.keySizeBytes = n.byteWidth
+ }
+
+ /// Initialize with RSA parameters
+ /// - Parameters:
+ /// - n: The RSA Modulus
+ /// - e: The RSA Public Exponent
+ /// - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
+ public convenience init(n: Array<UInt8>, e: Array<UInt8>, d: Array<UInt8>? = nil) {
+ if let d = d {
+ self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)), d: BigUInteger(Data(d)))
+ } else {
+ self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)))
+ }
+ }
+
+ /// Initialize with a generated key pair
+ /// - Parameter keySize: The size of the modulus
+ public convenience init(keySize: Int) throws {
+ // Generate prime numbers
+ let p = BigUInteger.generatePrime(keySize / 2)
+ let q = BigUInteger.generatePrime(keySize / 2)
+
+ // Calculate modulus
+ let n = p * q
+
+ // Calculate public and private exponent
+ let e: BigUInteger = 65537
+ let phi = (p - 1) * (q - 1)
+ guard let d = e.inverse(phi) else {
+ throw RSA.Error.invalidInverseNotCoprimes
+ }
+
+ // Initialize
+ self.init(n: n, e: e, d: d, p: p, q: q)
+ }
+
+ /// Initialize with RSA parameters
+ /// - Parameters:
+ /// - n: The RSA Modulus
+ /// - e: The RSA Public Exponent
+ /// - d: The RSA Private Exponent
+ /// - p: The 1st Prime used to generate the Private Exponent
+ /// - q: The 2nd Prime used to generate the Private Exponent
+ private init(n: BigUInteger, e: BigUInteger, d: BigUInteger, p: BigUInteger, q: BigUInteger) {
+ self.n = n
+ self.e = e
+ self.d = d
+ self.primes = (p, q)
+
+ self.keySize = n.bitWidth
+ self.keySizeBytes = n.byteWidth
+ }
+}
+
+// MARK: BigUInt Extension
+
+internal extension CS.BigUInt {
+ /// The minimum number of bytes required to represent this integer in binary.
+ var byteWidth: Int {
+ let bytes = self.bitWidth / 8
+ return self.bitWidth % 8 == 0 ? bytes : bytes + 1
+ }
+}
+
+// MARK: DER Initializers (See #892)
+
+extension RSA {
+ /// Decodes the provided data into a Public RSA CookiesKey
+ ///
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1)
+ /// ```
+ /// =========================
+ /// RSA PublicKey Structure
+ /// =========================
+ ///
+ /// RSAPublicKey ::= SEQUENCE {
+ /// modulus INTEGER, -- n
+ /// publicExponent INTEGER, -- e
+ /// }
+ /// ```
+ internal convenience init(publicDER der: Array<UInt8>) throws {
+ let asn = try ASN1.Decoder.decode(data: Data(der))
+
+ // Enforce the above ASN Structure
+ guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat }
+ guard params.count == 2 else { throw DER.Error.invalidDERFormat }
+
+ guard case .integer(let modulus) = params[0] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let publicExponent) = params[1] else { throw DER.Error.invalidDERFormat }
+
+ self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent))
+ }
+
+ /// Decodes the provided data into a Private RSA CookiesKey
+ ///
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2)
+ /// ```
+ /// ==========================
+ /// RSA PrivateKey Structure
+ /// ==========================
+ ///
+ /// RSAPrivateKey ::= SEQUENCE {
+ /// version Version,
+ /// modulus INTEGER, -- n
+ /// publicExponent INTEGER, -- e
+ /// privateExponent INTEGER, -- d
+ /// prime1 INTEGER, -- p
+ /// prime2 INTEGER, -- q
+ /// exponent1 INTEGER, -- d mod (p-1)
+ /// exponent2 INTEGER, -- d mod (q-1)
+ /// coefficient INTEGER, -- (inverse of q) mod p
+ /// }
+ /// ```
+ internal convenience init(privateDER der: Array<UInt8>) throws {
+ let asn = try ASN1.Decoder.decode(data: Data(der))
+
+ // Enforce the above ASN Structure (do we need to extract and verify the eponents and coefficients?)
+ guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat }
+ guard params.count == 9 else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let version) = params[0] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let modulus) = params[1] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let publicExponent) = params[2] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let privateExponent) = params[3] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let prime1) = params[4] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let prime2) = params[5] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let exponent1) = params[6] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let exponent2) = params[7] else { throw DER.Error.invalidDERFormat }
+ guard case .integer(let coefficient) = params[8] else { throw DER.Error.invalidDERFormat }
+
+ // We only support version 0x00 == RFC2313 at the moment
+ // - TODO: Support multiple primes 0x01 version defined in [RFC3447](https://www.rfc-editor.org/rfc/rfc3447#appendix-A.1.2)
+ guard version == Data(hex: "0x00") else { throw Error.unsupportedRSAVersion }
+
+ // Ensure the supplied parameters are correct...
+ // Calculate modulus
+ guard BigUInteger(modulus) == BigUInteger(prime1) * BigUInteger(prime2) else { throw Error.invalidPrimes }
+
+ // Calculate public and private exponent
+ let phi = (BigUInteger(prime1) - 1) * (BigUInteger(prime2) - 1)
+ guard let d = BigUInteger(publicExponent).inverse(phi) else { throw Error.invalidPrimes }
+ guard BigUInteger(privateExponent) == d else { throw Error.invalidPrimes }
+
+ // Ensure the provided coefficient is correct (derived from the primes)
+ guard let calculatedCoefficient = BigUInteger(prime2).inverse(BigUInteger(prime1)) else { throw RSA.Error.unableToCalculateCoefficient }
+ guard calculatedCoefficient == BigUInteger(coefficient) else { throw RSA.Error.invalidPrimes }
+
+ // Ensure the provided exponents are correct as well
+ guard (d % (BigUInteger(prime1) - 1)) == BigUInteger(exponent1) else { throw RSA.Error.invalidPrimes }
+ guard (d % (BigUInteger(prime2) - 1)) == BigUInteger(exponent2) else { throw RSA.Error.invalidPrimes }
+
+ // Proceed with regular initialization
+ self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent), d: BigUInteger(privateExponent), p: BigUInteger(prime1), q: BigUInteger(prime2))
+ }
+
+ /// Attempts to instantiate an RSA CookiesKey when given the ASN1 DER encoded external representation of the CookiesKey
+ ///
+ /// An example of importing a SecKey RSA key (from Apple's `Security` framework) for use within CryptoSwift
+ /// ```
+ /// /// Starting with a SecKey RSA CookiesKey
+ /// let rsaSecKey:SecKey
+ ///
+ /// /// Copy the External Representation
+ /// var externalRepError:Unmanaged<CFError>?
+ /// guard let externalRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+ /// /// Failed to copy external representation for RSA SecKey
+ /// return
+ /// }
+ ///
+ /// /// Instantiate the RSA CookiesKey from the raw external representation
+ /// let rsaKey = try RSA(rawRepresentation: externalRep)
+ ///
+ /// /// You now have a CryptoSwift RSA CookiesKey
+ /// // rsaKey.encrypt(...)
+ /// // rsaKey.decrypt(...)
+ /// // rsaKey.sign(...)
+ /// // rsaKey.verify(...)
+ /// ```
+ public convenience init(rawRepresentation raw: Data) throws {
+ do { try self.init(privateDER: raw.bytes) } catch {
+ try self.init(publicDER: raw.bytes)
+ }
+ }
+}
+
+// MARK: DER Exports (See #892)
+
+extension RSA {
+ /// The DER representation of this public key
+ ///
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1)
+ /// ```
+ /// =========================
+ /// RSA PublicKey Structure
+ /// =========================
+ ///
+ /// RSAPublicKey ::= SEQUENCE {
+ /// modulus INTEGER, -- n
+ /// publicExponent INTEGER -- e
+ /// }
+ /// ```
+ func publicKeyDER() throws -> Array<UInt8> {
+ let mod = self.n.serialize()
+ let pubKeyAsnNode: ASN1.Node =
+ .sequence(nodes: [
+ .integer(data: DER.i2ospData(x: mod.bytes, size: self.keySizeBytes)),
+ .integer(data: DER.i2ospData(x: self.e.serialize().bytes, size: 3))
+ ])
+ return ASN1.Encoder.encode(pubKeyAsnNode)
+ }
+
+ /// The DER representation of this private key
+ ///
+ /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2)
+ /// ```
+ /// ==========================
+ /// RSA PrivateKey Structure
+ /// ==========================
+ ///
+ /// RSAPrivateKey ::= SEQUENCE {
+ /// version Version,
+ /// modulus INTEGER, -- n
+ /// publicExponent INTEGER, -- e
+ /// privateExponent INTEGER, -- d
+ /// prime1 INTEGER, -- p
+ /// prime2 INTEGER, -- q
+ /// exponent1 INTEGER, -- d mod (p-1)
+ /// exponent2 INTEGER, -- d mod (q-1)
+ /// coefficient INTEGER, -- (inverse of q) mod p
+ /// }
+ /// ```
+ func privateKeyDER() throws -> Array<UInt8> {
+ // Make sure we have a private key
+ guard let d = d else { throw RSA.Error.noPrivateKey }
+ // Make sure we have access to our primes
+ guard let primes = primes else { throw RSA.Error.noPrimes }
+ // Make sure we can calculate our coefficient (inverse of q mod p)
+ guard let coefficient = primes.q.inverse(primes.p) else { throw RSA.Error.unableToCalculateCoefficient }
+
+ let paramWidth = self.keySizeBytes / 2
+ // Structure the data (according to RFC2313, version 0x00 RSA Private CookiesKey Syntax)
+ let mod = self.n.serialize()
+ let privateKeyAsnNode: ASN1.Node =
+ .sequence(nodes: [
+ .integer(data: Data(hex: "0x00")),
+ .integer(data: DER.i2ospData(x: mod.bytes, size: self.keySizeBytes)),
+ .integer(data: DER.i2ospData(x: self.e.serialize().bytes, size: 3)),
+ .integer(data: DER.i2ospData(x: d.serialize().bytes, size: self.keySizeBytes)),
+ .integer(data: DER.i2ospData(x: primes.p.serialize().bytes, size: paramWidth)),
+ .integer(data: DER.i2ospData(x: primes.q.serialize().bytes, size: paramWidth)),
+ .integer(data: DER.i2ospData(x: (d % (primes.p - 1)).serialize().bytes, size: paramWidth)),
+ .integer(data: DER.i2ospData(x: (d % (primes.q - 1)).serialize().bytes, size: paramWidth)),
+ .integer(data: DER.i2ospData(x: coefficient.serialize().bytes, size: paramWidth))
+ ])
+
+ // Encode and return the data
+ return ASN1.Encoder.encode(privateKeyAsnNode)
+ }
+
+ /// This method returns the DER encoding of the RSA CookiesKey.
+ ///
+ /// - Returns: The ASN1 DER Encoding of the Public or Private RSA CookiesKey
+ /// - Note: If the RSA CookiesKey is a private key, the private key representation is returned
+ /// - Note: If the RSA CookiesKey is a public key, the public key representation is returned
+ /// - Note: If you'd like to only export the public DER of an RSA CookiesKey call the `publicKeyExternalRepresentation()` method
+ /// - Note: This method returns the same data as Apple's `SecKeyCopyExternalRepresentation` method.
+ ///
+ /// An example of converting a CryptoSwift RSA key to a SecKey RSA key
+ /// ```
+ /// /// Starting with a CryptoSwift RSA CookiesKey
+ /// let rsaKey = try RSA(keySize: 1024)
+ ///
+ /// /// Define your Keys attributes
+ /// let attributes: [String:Any] = [
+ /// kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+ /// kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // or kSecAttrKeyClassPublic
+ /// kSecAttrKeySizeInBits as String: 1024, // The appropriate bits
+ /// kSecAttrIsPermanent as String: false
+ /// ]
+ /// var error:Unmanaged<CFError>? = nil
+ /// guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else {
+ /// /// Error constructing SecKey from raw key data
+ /// return
+ /// }
+ ///
+ /// /// You now have an RSA SecKey for use with Apple's Security framework
+ /// ```
+ ///
+ /// An example of converting a SecKey RSA key to a CryptoSwift RSA key
+ /// ```
+ /// /// Starting with a SecKey RSA CookiesKey
+ /// let rsaSecKey:SecKey
+ ///
+ /// /// Copy External Representation
+ /// var externalRepError:Unmanaged<CFError>?
+ /// guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else {
+ /// /// Failed to copy external representation for RSA SecKey
+ /// return
+ /// }
+ ///
+ /// /// Instantiate the RSA CookiesKey from the raw external representation
+ /// let rsaKey = try RSA(rawRepresentation: cfdata as Data)
+ ///
+ /// /// You now have a CryptoSwift RSA CookiesKey
+ /// ```
+ ///
+ public func externalRepresentation() throws -> Data {
+ if self.primes != nil {
+ return try Data(self.privateKeyDER())
+ } else {
+ return try Data(self.publicKeyDER())
+ }
+ }
+
+ public func publicKeyExternalRepresentation() throws -> Data {
+ return try Data(self.publicKeyDER())
+ }
+}
+
+// MARK: CS.BigUInt extension
+
+extension BigUInteger {
+
+ public static func generatePrime(_ width: Int) -> BigUInteger {
+ // Note: Need to find a better way to generate prime numbers
+ while true {
+ var random = BigUInteger.randomInteger(withExactWidth: width)
+ random |= BigUInteger(1)
+ if random.isPrime() {
+ return random
+ }
+ }
+ }
+}
+
+// MARK: CustomStringConvertible Conformance
+
+extension RSA: CustomStringConvertible {
+ public var description: String {
+ if self.d != nil {
+ return "CryptoSwift.RSA.PrivateKey<\(self.keySize)>"
+ } else {
+ return "CryptoSwift.RSA.PublicKey<\(self.keySize)>"
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension Rabbit {
+ public convenience init(key: String) throws {
+ try self.init(key: key.bytes)
+ }
+
+ public convenience init(key: String, iv: String) throws {
+ try self.init(key: key.bytes, iv: iv.bytes)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public final class Rabbit: BlockCipher {
+ public enum Error: Swift.Error {
+ case invalidKeyOrInitializationVector
+ }
+
+ /// Size of IV in bytes
+ public static let ivSize = 64 / 8
+
+ /// Size of key in bytes
+ public static let keySize = 128 / 8
+
+ /// Size of block in bytes
+ public static let blockSize = 128 / 8
+
+ public var keySize: Int {
+ self.key.count
+ }
+
+ /// CookiesKey
+ private let key: CookiesKey
+
+ /// IV (optional)
+ private let iv: Array<UInt8>?
+
+ /// State variables
+ private var x = Array<UInt32>(repeating: 0, count: 8)
+
+ /// Counter variables
+ private var c = Array<UInt32>(repeating: 0, count: 8)
+
+ /// Counter carry
+ private var p7: UInt32 = 0
+
+ /// 'a' constants
+ private var a: Array<UInt32> = [
+ 0x4d34d34d,
+ 0xd34d34d3,
+ 0x34d34d34,
+ 0x4d34d34d,
+ 0xd34d34d3,
+ 0x34d34d34,
+ 0x4d34d34d,
+ 0xd34d34d3
+ ]
+
+ // MARK: - Initializers
+
+ public convenience init(key: Array<UInt8>) throws {
+ try self.init(key: key, iv: nil)
+ }
+
+ public init(key: Array<UInt8>, iv: Array<UInt8>?) throws {
+ self.key = CookiesKey(bytes: key)
+ self.iv = iv
+
+ guard key.count == Rabbit.keySize && (iv == nil || iv!.count == Rabbit.ivSize) else {
+ throw Error.invalidKeyOrInitializationVector
+ }
+ }
+
+ // MARK: -
+
+ fileprivate func setup() {
+ self.p7 = 0
+
+ // CookiesKey divided into 8 subkeys
+ let k = Array<UInt32>(unsafeUninitializedCapacity: 8) { buf, count in
+ for j in 0..<8 {
+ buf[j] = UInt32(self.key[Rabbit.blockSize - (2 * j + 1)]) | (UInt32(self.key[Rabbit.blockSize - (2 * j + 2)]) << 8)
+ }
+ count = 8
+ }
+
+ // Initialize state and counter variables from subkeys
+ for j in 0..<8 {
+ if j % 2 == 0 {
+ self.x[j] = (k[(j + 1) % 8] << 16) | k[j]
+ self.c[j] = (k[(j + 4) % 8] << 16) | k[(j + 5) % 8]
+ } else {
+ self.x[j] = (k[(j + 5) % 8] << 16) | k[(j + 4) % 8]
+ self.c[j] = (k[j] << 16) | k[(j + 1) % 8]
+ }
+ }
+
+ // Iterate system four times
+ self.nextState()
+ self.nextState()
+ self.nextState()
+ self.nextState()
+
+ // Reinitialize counter variables
+ for j in 0..<8 {
+ self.c[j] = self.c[j] ^ self.x[(j + 4) % 8]
+ }
+
+ if let iv = iv {
+ self.setupIV(iv)
+ }
+ }
+
+ private func setupIV(_ iv: Array<UInt8>) {
+ // 63...56 55...48 47...40 39...32 31...24 23...16 15...8 7...0 IV bits
+ // 0 1 2 3 4 5 6 7 IV bytes in array
+ let iv0 = UInt32(bytes: [iv[4], iv[5], iv[6], iv[7]])
+ let iv1 = UInt32(bytes: [iv[0], iv[1], iv[4], iv[5]])
+ let iv2 = UInt32(bytes: [iv[0], iv[1], iv[2], iv[3]])
+ let iv3 = UInt32(bytes: [iv[2], iv[3], iv[6], iv[7]])
+
+ // Modify the counter state as function of the IV
+ c[0] = self.c[0] ^ iv0
+ self.c[1] = self.c[1] ^ iv1
+ self.c[2] = self.c[2] ^ iv2
+ self.c[3] = self.c[3] ^ iv3
+ self.c[4] = self.c[4] ^ iv0
+ self.c[5] = self.c[5] ^ iv1
+ self.c[6] = self.c[6] ^ iv2
+ self.c[7] = self.c[7] ^ iv3
+
+ // Iterate system four times
+ self.nextState()
+ self.nextState()
+ self.nextState()
+ self.nextState()
+ }
+
+ private func nextState() {
+ // Before an iteration the counters are incremented
+ var carry = self.p7
+ for j in 0..<8 {
+ let prev = self.c[j]
+ self.c[j] = prev &+ self.a[j] &+ carry
+ carry = prev > self.c[j] ? 1 : 0 // detect overflow
+ }
+ self.p7 = carry // save last carry bit
+
+ // Iteration of the system
+ self.x = Array<UInt32>(unsafeUninitializedCapacity: 8) { newX, count in
+ newX[0] = self.g(0) &+ rotateLeft(self.g(7), by: 16) &+ rotateLeft(self.g(6), by: 16)
+ newX[1] = self.g(1) &+ rotateLeft(self.g(0), by: 8) &+ self.g(7)
+ newX[2] = self.g(2) &+ rotateLeft(self.g(1), by: 16) &+ rotateLeft(self.g(0), by: 16)
+ newX[3] = self.g(3) &+ rotateLeft(self.g(2), by: 8) &+ self.g(1)
+ newX[4] = self.g(4) &+ rotateLeft(self.g(3), by: 16) &+ rotateLeft(self.g(2), by: 16)
+ newX[5] = self.g(5) &+ rotateLeft(self.g(4), by: 8) &+ self.g(3)
+ newX[6] = self.g(6) &+ rotateLeft(self.g(5), by: 16) &+ rotateLeft(self.g(4), by: 16)
+ newX[7] = self.g(7) &+ rotateLeft(self.g(6), by: 8) &+ self.g(5)
+ count = 8
+ }
+ }
+
+ private func g(_ j: Int) -> UInt32 {
+ let sum = self.x[j] &+ self.c[j]
+ let square = UInt64(sum) * UInt64(sum)
+ return UInt32(truncatingIfNeeded: square ^ (square >> 32))
+ }
+
+ fileprivate func nextOutput() -> Array<UInt8> {
+ self.nextState()
+
+ var output16 = Array<UInt16>(repeating: 0, count: Rabbit.blockSize / 2)
+ output16[7] = UInt16(truncatingIfNeeded: self.x[0]) ^ UInt16(truncatingIfNeeded: self.x[5] >> 16)
+ output16[6] = UInt16(truncatingIfNeeded: self.x[0] >> 16) ^ UInt16(truncatingIfNeeded: self.x[3])
+ output16[5] = UInt16(truncatingIfNeeded: self.x[2]) ^ UInt16(truncatingIfNeeded: self.x[7] >> 16)
+ output16[4] = UInt16(truncatingIfNeeded: self.x[2] >> 16) ^ UInt16(truncatingIfNeeded: self.x[5])
+ output16[3] = UInt16(truncatingIfNeeded: self.x[4]) ^ UInt16(truncatingIfNeeded: self.x[1] >> 16)
+ output16[2] = UInt16(truncatingIfNeeded: self.x[4] >> 16) ^ UInt16(truncatingIfNeeded: self.x[7])
+ output16[1] = UInt16(truncatingIfNeeded: self.x[6]) ^ UInt16(truncatingIfNeeded: self.x[3] >> 16)
+ output16[0] = UInt16(truncatingIfNeeded: self.x[6] >> 16) ^ UInt16(truncatingIfNeeded: self.x[1])
+
+ var output8 = Array<UInt8>(repeating: 0, count: Rabbit.blockSize)
+ for j in 0..<output16.count {
+ output8[j * 2] = UInt8(truncatingIfNeeded: output16[j] >> 8)
+ output8[j * 2 + 1] = UInt8(truncatingIfNeeded: output16[j])
+ }
+ return output8
+ }
+}
+
+// MARK: Cipher
+
+extension Rabbit: Cipher {
+ public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ self.setup()
+
+ return Array<UInt8>(unsafeUninitializedCapacity: bytes.count) { result, count in
+ var output = self.nextOutput()
+ var byteIdx = 0
+ var outputIdx = 0
+ while byteIdx < bytes.count {
+ if outputIdx == Rabbit.blockSize {
+ output = self.nextOutput()
+ outputIdx = 0
+ }
+
+ result[byteIdx] = bytes[byteIdx] ^ output[outputIdx]
+
+ byteIdx += 1
+ outputIdx += 1
+ }
+ count = bytes.count
+ }
+ }
+
+ public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ try self.encrypt(bytes)
+ }
+}
--- /dev/null
+//
+// Random.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-04.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ /// Create a big unsigned integer consisting of `width` uniformly distributed random bits.
+ ///
+ /// - Parameter width: The maximum number of one bits in the result.
+ /// - Parameter generator: The source of randomness.
+ /// - Returns: A big unsigned integer less than `1 << width`.
+ public static func randomInteger<RNG: RandomNumberGenerator>(withMaximumWidth width: Int, using generator: inout RNG) -> CS.BigUInt {
+ var result = CS.BigUInt.zero
+ var bitsLeft = width
+ var i = 0
+ let wordsNeeded = (width + Word.bitWidth - 1) / Word.bitWidth
+ if wordsNeeded > 2 {
+ result.reserveCapacity(wordsNeeded)
+ }
+ while bitsLeft >= Word.bitWidth {
+ result[i] = generator.next()
+ i += 1
+ bitsLeft -= Word.bitWidth
+ }
+ if bitsLeft > 0 {
+ let mask: Word = (1 << bitsLeft) - 1
+ result[i] = (generator.next() as Word) & mask
+ }
+ return result
+ }
+
+ /// Create a big unsigned integer consisting of `width` uniformly distributed random bits.
+ ///
+ /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
+ ///
+ /// - Parameter width: The maximum number of one bits in the result.
+ /// - Returns: A big unsigned integer less than `1 << width`.
+ public static func randomInteger(withMaximumWidth width: Int) -> CS.BigUInt {
+ var rng = SystemRandomNumberGenerator()
+ return randomInteger(withMaximumWidth: width, using: &rng)
+ }
+
+ /// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit.
+ ///
+ /// - Note: If `width` is zero, the result is zero.
+ ///
+ /// - Parameter width: The number of bits required to represent the answer.
+ /// - Parameter generator: The source of randomness.
+ /// - Returns: A random big unsigned integer whose width is `width`.
+ public static func randomInteger<RNG: RandomNumberGenerator>(withExactWidth width: Int, using generator: inout RNG) -> CS.BigUInt {
+ // width == 0 -> return 0 because there is no room for a one bit.
+ // width == 1 -> return 1 because there is no room for any random bits.
+ guard width > 1 else { return CS.BigUInt(width) }
+ var result = randomInteger(withMaximumWidth: width - 1, using: &generator)
+ result[(width - 1) / Word.bitWidth] |= 1 << Word((width - 1) % Word.bitWidth)
+ return result
+ }
+
+ /// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit.
+ ///
+ /// - Note: If `width` is zero, the result is zero.
+ /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
+ ///
+ /// - Returns: A random big unsigned integer whose width is `width`.
+ public static func randomInteger(withExactWidth width: Int) -> CS.BigUInt {
+ var rng = SystemRandomNumberGenerator()
+ return randomInteger(withExactWidth: width, using: &rng)
+ }
+
+ /// Create a uniformly distributed random unsigned integer that's less than the specified limit.
+ ///
+ /// - Precondition: `limit > 0`.
+ ///
+ /// - Parameter limit: The upper bound on the result.
+ /// - Parameter generator: The source of randomness.
+ /// - Returns: A random big unsigned integer that is less than `limit`.
+ public static func randomInteger<RNG: RandomNumberGenerator>(lessThan limit: CS.BigUInt, using generator: inout RNG) -> CS.BigUInt {
+ precondition(limit > 0, "\(#function): 0 is not a valid limit")
+ let width = limit.bitWidth
+ var random = randomInteger(withMaximumWidth: width, using: &generator)
+ while random >= limit {
+ random = randomInteger(withMaximumWidth: width, using: &generator)
+ }
+ return random
+ }
+
+ /// Create a uniformly distributed random unsigned integer that's less than the specified limit.
+ ///
+ /// - Precondition: `limit > 0`.
+ /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness.
+ ///
+ /// - Parameter limit: The upper bound on the result.
+ /// - Returns: A random big unsigned integer that is less than `limit`.
+ public static func randomInteger(lessThan limit: CS.BigUInt) -> CS.BigUInt {
+ var rng = SystemRandomNumberGenerator()
+ return randomInteger(lessThan: limit, using: &rng)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public final class SHA1: DigestType {
+
+ @usableFromInline
+ static let digestLength: Int = 20 // 160 / 8
+
+ @usableFromInline
+ static let blockSize: Int = 64
+
+ @usableFromInline
+ static let hashInitialValue: ContiguousArray<UInt32> = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]
+
+ @usableFromInline
+ var accumulated = Array<UInt8>()
+
+ @usableFromInline
+ var processedBytesTotalCount: Int = 0
+
+ @usableFromInline
+ var accumulatedHash: ContiguousArray<UInt32> = SHA1.hashInitialValue
+
+ public init() {
+ }
+
+ @inlinable
+ public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
+ do {
+ return try update(withBytes: bytes.slice, isLast: true)
+ } catch {
+ return []
+ }
+ }
+
+ public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ calculate(for: bytes)
+ }
+
+ @usableFromInline
+ func process(block chunk: ArraySlice<UInt8>, currentHash hh: inout ContiguousArray<UInt32>) {
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian
+ // Extend the sixteen 32-bit words into eighty 32-bit words:
+ let M = UnsafeMutablePointer<UInt32>.allocate(capacity: 80)
+ M.initialize(repeating: 0, count: 80)
+ defer {
+ M.deinitialize(count: 80)
+ M.deallocate()
+ }
+
+ for x in 0..<80 {
+ switch x {
+ case 0...15:
+ let start = chunk.startIndex.advanced(by: x * 4) // * MemoryLayout<UInt32>.size
+ M[x] = UInt32(bytes: chunk, fromIndex: start)
+ default:
+ M[x] = rotateLeft(M[x - 3] ^ M[x - 8] ^ M[x - 14] ^ M[x - 16], by: 1)
+ }
+ }
+
+ var A = hh[0]
+ var B = hh[1]
+ var C = hh[2]
+ var D = hh[3]
+ var E = hh[4]
+
+ // Main loop
+ for j in 0...79 {
+ var f: UInt32 = 0
+ var k: UInt32 = 0
+
+ switch j {
+ case 0...19:
+ f = (B & C) | ((~B) & D)
+ k = 0x5a827999
+ case 20...39:
+ f = B ^ C ^ D
+ k = 0x6ed9eba1
+ case 40...59:
+ f = (B & C) | (B & D) | (C & D)
+ k = 0x8f1bbcdc
+ case 60...79:
+ f = B ^ C ^ D
+ k = 0xca62c1d6
+ default:
+ break
+ }
+
+ let temp = rotateLeft(A, by: 5) &+ f &+ E &+ M[j] &+ k
+ E = D
+ D = C
+ C = rotateLeft(B, by: 30)
+ B = A
+ A = temp
+ }
+
+ hh[0] = hh[0] &+ A
+ hh[1] = hh[1] &+ B
+ hh[2] = hh[2] &+ C
+ hh[3] = hh[3] &+ D
+ hh[4] = hh[4] &+ E
+ }
+}
+
+extension SHA1: Updatable {
+ @discardableResult @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ if isLast {
+ let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8
+ let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b
+
+ // Step 1. Append padding
+ bitPadding(to: &self.accumulated, blockSize: SHA1.blockSize, allowance: 64 / 8)
+
+ // Step 2. Append Length a 64-bit representation of lengthInBits
+ self.accumulated += lengthBytes
+ }
+
+ var processedBytes = 0
+ for chunk in self.accumulated.batched(by: SHA1.blockSize) {
+ if isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize {
+ self.process(block: chunk, currentHash: &self.accumulatedHash)
+ processedBytes += chunk.count
+ }
+ }
+ self.accumulated.removeFirst(processedBytes)
+ self.processedBytesTotalCount += processedBytes
+
+ // output current hash
+ var result = Array<UInt8>(repeating: 0, count: SHA1.digestLength)
+ var pos = 0
+ for idx in 0..<self.accumulatedHash.count {
+ let h = self.accumulatedHash[idx]
+ result[pos + 0] = UInt8((h >> 24) & 0xff)
+ result[pos + 1] = UInt8((h >> 16) & 0xff)
+ result[pos + 2] = UInt8((h >> 8) & 0xff)
+ result[pos + 3] = UInt8(h & 0xff)
+ pos += 4
+ }
+
+ // reset hash value for instance
+ if isLast {
+ self.accumulatedHash = SHA1.hashInitialValue
+ }
+
+ return result
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// TODO: generic for process32/64 (UInt32/UInt64)
+//
+
+public final class SHA2: DigestType {
+ @usableFromInline
+ let variant: Variant
+
+ @usableFromInline
+ let size: Int
+
+ @usableFromInline
+ let blockSize: Int
+
+ @usableFromInline
+ let digestLength: Int
+
+ private let k: Array<UInt64>
+
+ @usableFromInline
+ var accumulated = Array<UInt8>()
+
+ @usableFromInline
+ var processedBytesTotalCount: Int = 0
+
+ @usableFromInline
+ var accumulatedHash32 = Array<UInt32>()
+
+ @usableFromInline
+ var accumulatedHash64 = Array<UInt64>()
+
+ @frozen
+ public enum Variant: RawRepresentable {
+ case sha224, sha256, sha384, sha512
+
+ public var digestLength: Int {
+ self.rawValue / 8
+ }
+
+ public var blockSize: Int {
+ switch self {
+ case .sha224, .sha256:
+ return 64
+ case .sha384, .sha512:
+ return 128
+ }
+ }
+
+ public typealias RawValue = Int
+ public var rawValue: RawValue {
+ switch self {
+ case .sha224:
+ return 224
+ case .sha256:
+ return 256
+ case .sha384:
+ return 384
+ case .sha512:
+ return 512
+ }
+ }
+
+ public init?(rawValue: RawValue) {
+ switch rawValue {
+ case 224:
+ self = .sha224
+ case 256:
+ self = .sha256
+ case 384:
+ self = .sha384
+ case 512:
+ self = .sha512
+ default:
+ return nil
+ }
+ }
+
+ @usableFromInline
+ var h: Array<UInt64> {
+ switch self {
+ case .sha224:
+ return [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]
+ case .sha256:
+ return [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
+ case .sha384:
+ return [0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4]
+ case .sha512:
+ return [0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179]
+ }
+ }
+
+ @usableFromInline
+ var finalLength: Int {
+ switch self {
+ case .sha224:
+ return 7
+ case .sha384:
+ return 6
+ default:
+ return Int.max
+ }
+ }
+ }
+
+ public init(variant: SHA2.Variant) {
+ self.variant = variant
+ switch self.variant {
+ case .sha224, .sha256:
+ self.accumulatedHash32 = variant.h.map { UInt32($0) } // FIXME: UInt64 for process64
+ self.blockSize = variant.blockSize
+ self.size = variant.rawValue
+ self.digestLength = variant.digestLength
+ self.k = [
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ ]
+ case .sha384, .sha512:
+ self.accumulatedHash64 = variant.h
+ self.blockSize = variant.blockSize
+ self.size = variant.rawValue
+ self.digestLength = variant.digestLength
+ self.k = [
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
+ 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
+ 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
+ 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
+ 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
+ 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
+ 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
+ 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
+ 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
+ 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
+ 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
+ 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
+ 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+ ]
+ }
+ }
+
+ @inlinable
+ public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
+ do {
+ return try update(withBytes: bytes.slice, isLast: true)
+ } catch {
+ return []
+ }
+ }
+
+ public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ calculate(for: bytes)
+ }
+
+ @usableFromInline
+ func process64(block chunk: ArraySlice<UInt8>, currentHash hh: inout Array<UInt64>) {
+ // break chunk into sixteen 64-bit words M[j], 0 ≤ j ≤ 15, big-endian
+ // Extend the sixteen 64-bit words into eighty 64-bit words:
+ let M = UnsafeMutablePointer<UInt64>.allocate(capacity: self.k.count)
+ M.initialize(repeating: 0, count: self.k.count)
+ defer {
+ M.deinitialize(count: self.k.count)
+ M.deallocate()
+ }
+ for x in 0..<self.k.count {
+ switch x {
+ case 0...15:
+ let start = chunk.startIndex.advanced(by: x * 8) // * MemoryLayout<UInt64>.size
+ M[x] = UInt64(bytes: chunk, fromIndex: start)
+ default:
+ let s0 = rotateRight(M[x - 15], by: 1) ^ rotateRight(M[x - 15], by: 8) ^ (M[x - 15] >> 7)
+ let s1 = rotateRight(M[x - 2], by: 19) ^ rotateRight(M[x - 2], by: 61) ^ (M[x - 2] >> 6)
+ M[x] = M[x - 16] &+ s0 &+ M[x - 7] &+ s1
+ }
+ }
+
+ var A = hh[0]
+ var B = hh[1]
+ var C = hh[2]
+ var D = hh[3]
+ var E = hh[4]
+ var F = hh[5]
+ var G = hh[6]
+ var H = hh[7]
+
+ // Main loop
+ for j in 0..<self.k.count {
+ let s0 = rotateRight(A, by: 28) ^ rotateRight(A, by: 34) ^ rotateRight(A, by: 39)
+ let maj = (A & B) ^ (A & C) ^ (B & C)
+ let t2 = s0 &+ maj
+ let s1 = rotateRight(E, by: 14) ^ rotateRight(E, by: 18) ^ rotateRight(E, by: 41)
+ let ch = (E & F) ^ ((~E) & G)
+ let t1 = H &+ s1 &+ ch &+ self.k[j] &+ UInt64(M[j])
+
+ H = G
+ G = F
+ F = E
+ E = D &+ t1
+ D = C
+ C = B
+ B = A
+ A = t1 &+ t2
+ }
+
+ hh[0] = (hh[0] &+ A)
+ hh[1] = (hh[1] &+ B)
+ hh[2] = (hh[2] &+ C)
+ hh[3] = (hh[3] &+ D)
+ hh[4] = (hh[4] &+ E)
+ hh[5] = (hh[5] &+ F)
+ hh[6] = (hh[6] &+ G)
+ hh[7] = (hh[7] &+ H)
+ }
+
+ // mutating currentHash in place is way faster than returning new result
+ @usableFromInline
+ func process32(block chunk: ArraySlice<UInt8>, currentHash hh: inout Array<UInt32>) {
+ // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian
+ // Extend the sixteen 32-bit words into sixty-four 32-bit words:
+ let M = UnsafeMutablePointer<UInt32>.allocate(capacity: self.k.count)
+ M.initialize(repeating: 0, count: self.k.count)
+ defer {
+ M.deinitialize(count: self.k.count)
+ M.deallocate()
+ }
+
+ for x in 0..<self.k.count {
+ switch x {
+ case 0...15:
+ let start = chunk.startIndex.advanced(by: x * 4) // * MemoryLayout<UInt32>.size
+ M[x] = UInt32(bytes: chunk, fromIndex: start)
+ default:
+ let s0 = rotateRight(M[x - 15], by: 7) ^ rotateRight(M[x - 15], by: 18) ^ (M[x - 15] >> 3)
+ let s1 = rotateRight(M[x - 2], by: 17) ^ rotateRight(M[x - 2], by: 19) ^ (M[x - 2] >> 10)
+ M[x] = M[x - 16] &+ s0 &+ M[x - 7] &+ s1
+ }
+ }
+
+ var A = hh[0]
+ var B = hh[1]
+ var C = hh[2]
+ var D = hh[3]
+ var E = hh[4]
+ var F = hh[5]
+ var G = hh[6]
+ var H = hh[7]
+
+ // Main loop
+ for j in 0..<self.k.count {
+ let s0 = rotateRight(A, by: 2) ^ rotateRight(A, by: 13) ^ rotateRight(A, by: 22)
+ let maj = (A & B) ^ (A & C) ^ (B & C)
+ let t2 = s0 &+ maj
+ let s1 = rotateRight(E, by: 6) ^ rotateRight(E, by: 11) ^ rotateRight(E, by: 25)
+ let ch = (E & F) ^ ((~E) & G)
+ let t1 = H &+ s1 &+ ch &+ UInt32(self.k[j]) &+ M[j]
+
+ H = G
+ G = F
+ F = E
+ E = D &+ t1
+ D = C
+ C = B
+ B = A
+ A = t1 &+ t2
+ }
+
+ hh[0] = hh[0] &+ A
+ hh[1] = hh[1] &+ B
+ hh[2] = hh[2] &+ C
+ hh[3] = hh[3] &+ D
+ hh[4] = hh[4] &+ E
+ hh[5] = hh[5] &+ F
+ hh[6] = hh[6] &+ G
+ hh[7] = hh[7] &+ H
+ }
+}
+
+extension SHA2: Updatable {
+
+ @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ if isLast {
+ let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8
+ let lengthBytes = lengthInBits.bytes(totalBytes: self.blockSize / 8) // A 64-bit/128-bit representation of b. blockSize fit by accident.
+
+ // Step 1. Append padding
+ bitPadding(to: &self.accumulated, blockSize: self.blockSize, allowance: self.blockSize / 8)
+
+ // Step 2. Append Length a 64-bit representation of lengthInBits
+ self.accumulated += lengthBytes
+ }
+
+ var processedBytes = 0
+ for chunk in self.accumulated.batched(by: self.blockSize) {
+ if isLast || (self.accumulated.count - processedBytes) >= self.blockSize {
+ switch self.variant {
+ case .sha224, .sha256:
+ self.process32(block: chunk, currentHash: &self.accumulatedHash32)
+ case .sha384, .sha512:
+ self.process64(block: chunk, currentHash: &self.accumulatedHash64)
+ }
+ processedBytes += chunk.count
+ }
+ }
+ self.accumulated.removeFirst(processedBytes)
+ self.processedBytesTotalCount += processedBytes
+
+ // output current hash
+ var result = Array<UInt8>(repeating: 0, count: variant.digestLength)
+ switch self.variant {
+ case .sha224, .sha256:
+ var pos = 0
+ for idx in 0..<self.accumulatedHash32.count where idx < self.variant.finalLength {
+ let h = accumulatedHash32[idx]
+ result[pos + 0] = UInt8((h >> 24) & 0xff)
+ result[pos + 1] = UInt8((h >> 16) & 0xff)
+ result[pos + 2] = UInt8((h >> 8) & 0xff)
+ result[pos + 3] = UInt8(h & 0xff)
+ pos += 4
+ }
+ case .sha384, .sha512:
+ var pos = 0
+ for idx in 0..<self.accumulatedHash64.count where idx < self.variant.finalLength {
+ let h = accumulatedHash64[idx]
+ result[pos + 0] = UInt8((h >> 56) & 0xff)
+ result[pos + 1] = UInt8((h >> 48) & 0xff)
+ result[pos + 2] = UInt8((h >> 40) & 0xff)
+ result[pos + 3] = UInt8((h >> 32) & 0xff)
+ result[pos + 4] = UInt8((h >> 24) & 0xff)
+ result[pos + 5] = UInt8((h >> 16) & 0xff)
+ result[pos + 6] = UInt8((h >> 8) & 0xff)
+ result[pos + 7] = UInt8(h & 0xff)
+ pos += 8
+ }
+ }
+
+ // reset hash value for instance
+ if isLast {
+ switch self.variant {
+ case .sha224, .sha256:
+ self.accumulatedHash32 = self.variant.h.lazy.map { UInt32($0) } // FIXME: UInt64 for process64
+ case .sha384, .sha512:
+ self.accumulatedHash64 = self.variant.h
+ }
+ }
+
+ return result
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
+// http://keccak.noekeon.org/specs_summary.html
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#endif
+
+public final class SHA3: DigestType {
+ let round_constants: Array<UInt64> = [
+ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
+ 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+ 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
+ 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
+ 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
+ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+ ]
+
+ public let blockSize: Int
+ public let digestLength: Int
+ public let markByte: UInt8
+
+ @usableFromInline
+ var accumulated = Array<UInt8>()
+
+
+ @usableFromInline
+ var accumulatedHash: Array<UInt64>
+
+ public enum Variant {
+ case sha224, sha256, sha384, sha512, keccak224, keccak256, keccak384, keccak512
+
+ var digestLength: Int {
+ 100 - (self.blockSize / 2)
+ }
+
+ var blockSize: Int {
+ (1600 - self.outputLength * 2) / 8
+ }
+
+ var markByte: UInt8 {
+ switch self {
+ case .sha224, .sha256, .sha384, .sha512:
+ return 0x06 // 0x1F for SHAKE
+ case .keccak224, .keccak256, .keccak384, .keccak512:
+ return 0x01
+ }
+ }
+
+ public var outputLength: Int {
+ switch self {
+ case .sha224, .keccak224:
+ return 224
+ case .sha256, .keccak256:
+ return 256
+ case .sha384, .keccak384:
+ return 384
+ case .sha512, .keccak512:
+ return 512
+ }
+ }
+ }
+
+ public init(variant: SHA3.Variant) {
+ self.blockSize = variant.blockSize
+ self.digestLength = variant.digestLength
+ self.markByte = variant.markByte
+ self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.digestLength)
+ }
+
+ @inlinable
+ public func calculate(for bytes: Array<UInt8>) -> Array<UInt8> {
+ do {
+ return try update(withBytes: bytes.slice, isLast: true)
+ } catch {
+ return []
+ }
+ }
+
+ public func callAsFunction(_ bytes: Array<UInt8>) -> Array<UInt8> {
+ calculate(for: bytes)
+ }
+
+ /// 1. For all pairs (x,z) such that 0≤x<5 and 0≤z<w, let
+ /// C[x,z]=A[x, 0,z] ⊕ A[x, 1,z] ⊕ A[x, 2,z] ⊕ A[x, 3,z] ⊕ A[x, 4,z].
+ /// 2. For all pairs (x, z) such that 0≤x<5 and 0≤z<w let
+ /// D[x,z]=C[(x1) mod 5, z] ⊕ C[(x+1) mod 5, (z –1) mod w].
+ /// 3. For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
+ /// A′[x, y,z] = A[x, y,z] ⊕ D[x,z].
+ private func θ(_ a: inout Array<UInt64>) {
+ let c = UnsafeMutablePointer<UInt64>.allocate(capacity: 5)
+ c.initialize(repeating: 0, count: 5)
+ defer {
+ c.deinitialize(count: 5)
+ c.deallocate()
+ }
+ let d = UnsafeMutablePointer<UInt64>.allocate(capacity: 5)
+ d.initialize(repeating: 0, count: 5)
+ defer {
+ d.deinitialize(count: 5)
+ d.deallocate()
+ }
+
+ for i in 0..<5 {
+ c[i] = a[i] ^ a[i &+ 5] ^ a[i &+ 10] ^ a[i &+ 15] ^ a[i &+ 20]
+ }
+
+ d[0] = rotateLeft(c[1], by: 1) ^ c[4]
+ d[1] = rotateLeft(c[2], by: 1) ^ c[0]
+ d[2] = rotateLeft(c[3], by: 1) ^ c[1]
+ d[3] = rotateLeft(c[4], by: 1) ^ c[2]
+ d[4] = rotateLeft(c[0], by: 1) ^ c[3]
+
+ for i in 0..<5 {
+ a[i] ^= d[i]
+ a[i &+ 5] ^= d[i]
+ a[i &+ 10] ^= d[i]
+ a[i &+ 15] ^= d[i]
+ a[i &+ 20] ^= d[i]
+ }
+ }
+
+ /// A′[x, y, z]=A[(x &+ 3y) mod 5, x, z]
+ private func π(_ a: inout Array<UInt64>) {
+ let a1 = a[1]
+ a[1] = a[6]
+ a[6] = a[9]
+ a[9] = a[22]
+ a[22] = a[14]
+ a[14] = a[20]
+ a[20] = a[2]
+ a[2] = a[12]
+ a[12] = a[13]
+ a[13] = a[19]
+ a[19] = a[23]
+ a[23] = a[15]
+ a[15] = a[4]
+ a[4] = a[24]
+ a[24] = a[21]
+ a[21] = a[8]
+ a[8] = a[16]
+ a[16] = a[5]
+ a[5] = a[3]
+ a[3] = a[18]
+ a[18] = a[17]
+ a[17] = a[11]
+ a[11] = a[7]
+ a[7] = a[10]
+ a[10] = a1
+ }
+
+ /// For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z<w, let
+ /// A′[x, y,z] = A[x, y,z] ⊕ ((A[(x+1) mod 5, y, z] ⊕ 1) ⋅ A[(x+2) mod 5, y, z])
+ private func χ(_ a: inout Array<UInt64>) {
+ for i in stride(from: 0, to: 25, by: 5) {
+ let a0 = a[0 &+ i]
+ let a1 = a[1 &+ i]
+ a[0 &+ i] ^= ~a1 & a[2 &+ i]
+ a[1 &+ i] ^= ~a[2 &+ i] & a[3 &+ i]
+ a[2 &+ i] ^= ~a[3 &+ i] & a[4 &+ i]
+ a[3 &+ i] ^= ~a[4 &+ i] & a0
+ a[4 &+ i] ^= ~a0 & a1
+ }
+ }
+
+ private func ι(_ a: inout Array<UInt64>, round: Int) {
+ a[0] ^= self.round_constants[round]
+ }
+
+ @usableFromInline
+ func process(block chunk: ArraySlice<UInt64>, currentHash hh: inout Array<UInt64>) {
+ // expand
+ hh[0] ^= chunk[0].littleEndian
+ hh[1] ^= chunk[1].littleEndian
+ hh[2] ^= chunk[2].littleEndian
+ hh[3] ^= chunk[3].littleEndian
+ hh[4] ^= chunk[4].littleEndian
+ hh[5] ^= chunk[5].littleEndian
+ hh[6] ^= chunk[6].littleEndian
+ hh[7] ^= chunk[7].littleEndian
+ hh[8] ^= chunk[8].littleEndian
+ if self.blockSize > 72 { // 72 / 8, sha-512
+ hh[9] ^= chunk[9].littleEndian
+ hh[10] ^= chunk[10].littleEndian
+ hh[11] ^= chunk[11].littleEndian
+ hh[12] ^= chunk[12].littleEndian
+ if self.blockSize > 104 { // 104 / 8, sha-384
+ hh[13] ^= chunk[13].littleEndian
+ hh[14] ^= chunk[14].littleEndian
+ hh[15] ^= chunk[15].littleEndian
+ hh[16] ^= chunk[16].littleEndian
+ if self.blockSize > 136 { // 136 / 8, sha-256
+ hh[17] ^= chunk[17].littleEndian
+ // FULL_SHA3_FAMILY_SUPPORT
+ if self.blockSize > 144 { // 144 / 8, sha-224
+ hh[18] ^= chunk[18].littleEndian
+ hh[19] ^= chunk[19].littleEndian
+ hh[20] ^= chunk[20].littleEndian
+ hh[21] ^= chunk[21].littleEndian
+ hh[22] ^= chunk[22].littleEndian
+ hh[23] ^= chunk[23].littleEndian
+ hh[24] ^= chunk[24].littleEndian
+ }
+ }
+ }
+ }
+
+ // Keccak-f
+ for round in 0..<24 {
+ self.θ(&hh)
+
+ hh[1] = rotateLeft(hh[1], by: 1)
+ hh[2] = rotateLeft(hh[2], by: 62)
+ hh[3] = rotateLeft(hh[3], by: 28)
+ hh[4] = rotateLeft(hh[4], by: 27)
+ hh[5] = rotateLeft(hh[5], by: 36)
+ hh[6] = rotateLeft(hh[6], by: 44)
+ hh[7] = rotateLeft(hh[7], by: 6)
+ hh[8] = rotateLeft(hh[8], by: 55)
+ hh[9] = rotateLeft(hh[9], by: 20)
+ hh[10] = rotateLeft(hh[10], by: 3)
+ hh[11] = rotateLeft(hh[11], by: 10)
+ hh[12] = rotateLeft(hh[12], by: 43)
+ hh[13] = rotateLeft(hh[13], by: 25)
+ hh[14] = rotateLeft(hh[14], by: 39)
+ hh[15] = rotateLeft(hh[15], by: 41)
+ hh[16] = rotateLeft(hh[16], by: 45)
+ hh[17] = rotateLeft(hh[17], by: 15)
+ hh[18] = rotateLeft(hh[18], by: 21)
+ hh[19] = rotateLeft(hh[19], by: 8)
+ hh[20] = rotateLeft(hh[20], by: 18)
+ hh[21] = rotateLeft(hh[21], by: 2)
+ hh[22] = rotateLeft(hh[22], by: 61)
+ hh[23] = rotateLeft(hh[23], by: 56)
+ hh[24] = rotateLeft(hh[24], by: 14)
+
+ self.π(&hh)
+ self.χ(&hh)
+ self.ι(&hh, round: round)
+ }
+ }
+}
+
+extension SHA3: Updatable {
+
+ @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ if isLast {
+ // Add padding
+ let markByteIndex = self.accumulated.count
+
+ // We need to always pad the input. Even if the input is a multiple of blockSize.
+ let r = self.blockSize * 8
+ let q = (r / 8) - (accumulated.count % (r / 8))
+ self.accumulated += Array<UInt8>(repeating: 0, count: q)
+
+ self.accumulated[markByteIndex] |= self.markByte
+ self.accumulated[self.accumulated.count - 1] |= 0x80
+ }
+
+ var processedBytes = 0
+ for chunk in self.accumulated.batched(by: self.blockSize) {
+ if isLast || (self.accumulated.count - processedBytes) >= self.blockSize {
+ self.process(block: chunk.toUInt64Array().slice, currentHash: &self.accumulatedHash)
+ processedBytes += chunk.count
+ }
+ }
+ self.accumulated.removeFirst(processedBytes)
+
+ // TODO: verify performance, reduce vs for..in
+ let result = self.accumulatedHash.reduce(into: Array<UInt8>()) { (result, value) in
+ result += value.bigEndian.bytes()
+ }
+
+ // reset hash value for instance
+ if isLast {
+ self.accumulatedHash = Array<UInt64>(repeating: 0, count: self.digestLength)
+ }
+
+ return Array(result[0..<self.digestLength])
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+//
+// https://tools.ietf.org/html/rfc7914
+//
+
+/// Implementation of the scrypt key derivation function.
+public final class Scrypt {
+ enum Error: Swift.Error {
+ case nIsTooLarge
+ case rIsTooLarge
+ case nMustBeAPowerOf2GreaterThan1
+ case invalidInput
+ }
+
+ /// Configuration parameters.
+ private let salt: SecureBytes
+ private let password: SecureBytes
+ private let blocksize: Int // 128 * r
+ private let salsaBlock = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 64)
+ private let dkLen: Int
+ private let N: Int
+ private let r: Int
+ private let p: Int
+
+ /// - parameters:
+ /// - password: password
+ /// - salt: salt
+ /// - dkLen: output length
+ /// - N: determines extra memory used
+ /// - r: determines a block size
+ /// - p: determines parallelicity degree
+ public init(password: Array<UInt8>, salt: Array<UInt8>, dkLen: Int, N: Int, r: Int, p: Int) throws {
+ precondition(dkLen > 0)
+ precondition(N > 0)
+ precondition(r > 0)
+ precondition(p > 0)
+
+ guard !(N < 2 || (N & (N - 1)) != 0) else { throw Error.nMustBeAPowerOf2GreaterThan1 }
+
+ guard N <= .max / 128 / r else { throw Error.nIsTooLarge }
+ guard r <= .max / 128 / p else { throw Error.rIsTooLarge }
+
+ guard !salt.isEmpty else {
+ throw Error.invalidInput
+ }
+
+ self.blocksize = 128 * r
+ self.N = N
+ self.r = r
+ self.p = p
+ self.password = SecureBytes(bytes: password)
+ self.salt = SecureBytes(bytes: salt)
+ self.dkLen = dkLen
+ }
+
+ /// Runs the key derivation function with a specific password.
+ public func calculate() throws -> [UInt8] {
+ // Allocate memory (as bytes for now) for further use in mixing steps
+ let B = UnsafeMutableRawPointer.allocate(byteCount: 128 * self.r * self.p, alignment: 64)
+ let XY = UnsafeMutableRawPointer.allocate(byteCount: 256 * self.r + 64, alignment: 64)
+ let V = UnsafeMutableRawPointer.allocate(byteCount: 128 * self.r * self.N, alignment: 64)
+
+ // Deallocate memory when done
+ defer {
+ B.deallocate()
+ XY.deallocate()
+ V.deallocate()
+ }
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+ // Expand the initial key
+ let barray = try PKCS5.PBKDF2(password: Array(self.password), salt: Array(self.salt), iterations: 1, keyLength: self.p * 128 * self.r, variant: .sha2(.sha256)).calculate()
+ barray.withUnsafeBytes { p in
+ B.copyMemory(from: p.baseAddress!, byteCount: barray.count)
+ }
+
+ /* 2: for i = 0 to p - 1 do */
+ // do the mixing
+ for i in 0 ..< self.p {
+ /* 3: B_i <-- MF(B_i, N) */
+ smix(B + i * 128 * self.r, V.assumingMemoryBound(to: UInt32.self), XY.assumingMemoryBound(to: UInt32.self))
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+ let pointer = B.assumingMemoryBound(to: UInt8.self)
+ let bufferPointer = UnsafeBufferPointer(start: pointer, count: p * 128 * self.r)
+ let block = [UInt8](bufferPointer)
+ return try PKCS5.PBKDF2(password: Array(self.password), salt: block, iterations: 1, keyLength: self.dkLen, variant: .sha2(.sha256)).calculate()
+ }
+
+ public func callAsFunction() throws -> Array<UInt8> {
+ try calculate()
+ }
+
+}
+
+private extension Scrypt {
+ /// Computes `B = SMix_r(B, N)`.
+ ///
+ /// The input `block` must be `128*r` bytes in length; the temporary storage `v` must be `128*r*n` bytes in length;
+ /// the temporary storage `xy` must be `256*r + 64` bytes in length. The arrays `block`, `v`, and `xy` must be
+ /// aligned to a multiple of 64 bytes.
+ @inline(__always) func smix(_ block: UnsafeMutableRawPointer, _ v: UnsafeMutablePointer<UInt32>, _ xy: UnsafeMutablePointer<UInt32>) {
+ let X = xy
+ let Y = xy + 32 * self.r
+ let Z = xy + 64 * self.r
+
+ /* 1: X <-- B */
+ let typedBlock = block.assumingMemoryBound(to: UInt32.self)
+ X.assign(from: typedBlock, count: 32 * self.r)
+
+ /* 2: for i = 0 to N - 1 do */
+ for i in stride(from: 0, to: self.N, by: 2) {
+ /* 3: V_i <-- X */
+ UnsafeMutableRawPointer(v + i * (32 * self.r)).copyMemory(from: X, byteCount: 128 * self.r)
+
+ /* 4: X <-- H(X) */
+ self.blockMixSalsa8(X, Y, Z)
+
+ /* 3: V_i <-- X */
+ UnsafeMutableRawPointer(v + (i + 1) * (32 * self.r)).copyMemory(from: Y, byteCount: 128 * self.r)
+
+ /* 4: X <-- H(X) */
+ self.blockMixSalsa8(Y, X, Z)
+ }
+
+ /* 6: for i = 0 to N - 1 do */
+ for _ in stride(from: 0, to: self.N, by: 2) {
+ /*
+ 7: j <-- Integerify (X) mod N
+ where Integerify (B[0] ... B[2 * r - 1]) is defined
+ as the result of interpreting B[2 * r - 1] as a little-endian integer.
+ */
+ var j = Int(integerify(X) & UInt64(self.N - 1))
+
+ /* 8: X <-- H(X \xor V_j) */
+ self.blockXor(X, v + j * 32 * self.r, 128 * self.r)
+ self.blockMixSalsa8(X, Y, Z)
+
+ /* 7: j <-- Integerify(X) mod N */
+ j = Int(self.integerify(Y) & UInt64(self.N - 1))
+
+ /* 8: X <-- H(X \xor V_j) */
+ self.blockXor(Y, v + j * 32 * self.r, 128 * self.r)
+ self.blockMixSalsa8(Y, X, Z)
+ }
+
+ /* 10: B' <-- X */
+ for k in 0 ..< 32 * self.r {
+ UnsafeMutableRawPointer(block + 4 * k).storeBytes(of: X[k], as: UInt32.self)
+ }
+ }
+
+ /// Returns the result of parsing `B_{2r-1}` as a little-endian integer.
+ @inline(__always) func integerify(_ block: UnsafeRawPointer) -> UInt64 {
+ let bi = block + (2 * self.r - 1) * 64
+ return bi.load(as: UInt64.self).littleEndian
+ }
+
+ /// Compute `bout = BlockMix_{salsa20/8, r}(bin)`.
+ ///
+ /// The input `bin` must be `128*r` bytes in length; the output `bout` must also be the same size. The temporary
+ /// space `x` must be 64 bytes.
+ @inline(__always) func blockMixSalsa8(_ bin: UnsafePointer<UInt32>, _ bout: UnsafeMutablePointer<UInt32>, _ x: UnsafeMutablePointer<UInt32>) {
+ /* 1: X <-- B_{2r - 1} */
+ UnsafeMutableRawPointer(x).copyMemory(from: bin + (2 * self.r - 1) * 16, byteCount: 64)
+
+ /* 2: for i = 0 to 2r - 1 do */
+ for i in stride(from: 0, to: 2 * self.r, by: 2) {
+ /* 3: X <-- H(X \xor B_i) */
+ self.blockXor(x, bin + i * 16, 64)
+ self.salsa20_8_typed(x)
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ UnsafeMutableRawPointer(bout + i * 8).copyMemory(from: x, byteCount: 64)
+
+ /* 3: X <-- H(X \xor B_i) */
+ self.blockXor(x, bin + i * 16 + 16, 64)
+ self.salsa20_8_typed(x)
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ UnsafeMutableRawPointer(bout + i * 8 + self.r * 16).copyMemory(from: x, byteCount: 64)
+ }
+ }
+
+ @inline(__always) func salsa20_8_typed(_ block: UnsafeMutablePointer<UInt32>) {
+ self.salsaBlock.copyMemory(from: UnsafeRawPointer(block), byteCount: 64)
+ let salsaBlockTyped = self.salsaBlock.assumingMemoryBound(to: UInt32.self)
+
+ for _ in stride(from: 0, to: 8, by: 2) {
+ salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[12], by: 7)
+ salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[0], by: 9)
+ salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[4], by: 13)
+ salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[8], by: 18)
+
+ salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[1], by: 7)
+ salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[5], by: 9)
+ salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[9], by: 13)
+ salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[13], by: 18)
+
+ salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[6], by: 7)
+ salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[10], by: 9)
+ salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[14], by: 13)
+ salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[2], by: 18)
+
+ salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[11], by: 7)
+ salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[15], by: 9)
+ salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[3], by: 13)
+ salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[7], by: 18)
+
+ salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[3], by: 7)
+ salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[0], by: 9)
+ salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[1], by: 13)
+ salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[2], by: 18)
+
+ salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[4], by: 7)
+ salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[5], by: 9)
+ salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[6], by: 13)
+ salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[7], by: 18)
+
+ salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[9], by: 7)
+ salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[10], by: 9)
+ salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[11], by: 13)
+ salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[8], by: 18)
+
+ salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[14], by: 7)
+ salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[15], by: 9)
+ salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[12], by: 13)
+ salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[13], by: 18)
+ }
+ for i in 0 ..< 16 {
+ block[i] = block[i] &+ salsaBlockTyped[i]
+ }
+ }
+
+ @inline(__always) func blockXor(_ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer, _ len: Int) {
+ let D = dest.assumingMemoryBound(to: UInt64.self)
+ let S = src.assumingMemoryBound(to: UInt64.self)
+ let L = len / MemoryLayout<UInt64>.size
+
+ for i in 0 ..< L {
+ D[i] ^= S[i]
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(WinSDK)
+import WinSDK
+#endif
+
+typealias CookiesKey = SecureBytes
+
+/// Keeps bytes in memory. Because this is class, bytes are not copied
+/// and memory area is locked as long as referenced, then unlocked on deinit
+final class SecureBytes {
+ private let bytes: Array<UInt8>
+ let count: Int
+
+ init(bytes: Array<UInt8>) {
+ self.bytes = bytes
+ self.count = bytes.count
+ self.bytes.withUnsafeBufferPointer { (pointer) -> Void in
+ #if os(Windows)
+ VirtualLock(UnsafeMutableRawPointer(mutating: pointer.baseAddress), SIZE_T(pointer.count))
+ #elseif os(WASI)
+ // not supported on WASI
+ #else
+ mlock(pointer.baseAddress, pointer.count)
+ #endif
+ }
+ }
+
+ deinit {
+ self.bytes.withUnsafeBufferPointer { (pointer) -> Void in
+ #if os(Windows)
+ VirtualUnlock(UnsafeMutableRawPointer(mutating: pointer.baseAddress), SIZE_T(pointer.count))
+ #elseif os(WASI)
+ // not supported on WASI
+ #else
+ munlock(pointer.baseAddress, pointer.count)
+ #endif
+ }
+ }
+}
+
+extension SecureBytes: Collection {
+ typealias Index = Int
+
+ var endIndex: Int {
+ self.bytes.endIndex
+ }
+
+ var startIndex: Int {
+ self.bytes.startIndex
+ }
+
+ subscript(position: Index) -> UInt8 {
+ self.bytes[position]
+ }
+
+ subscript(bounds: Range<Index>) -> ArraySlice<UInt8> {
+ self.bytes[bounds]
+ }
+
+ func formIndex(after i: inout Int) {
+ self.bytes.formIndex(after: &i)
+ }
+
+ func index(after i: Int) -> Int {
+ self.bytes.index(after: i)
+ }
+}
+
+extension SecureBytes: ExpressibleByArrayLiteral {
+ public convenience init(arrayLiteral elements: UInt8...) {
+ self.init(bytes: elements)
+ }
+}
--- /dev/null
+//
+// Shifts.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+
+ //MARK: Shift Operators
+
+ internal func shiftedLeft(by amount: Word) -> CS.BigUInt {
+ guard amount > 0 else { return self }
+
+ let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
+ let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
+ let down = Word(Word.bitWidth) - up
+
+ var result = CS.BigUInt()
+ if up > 0 {
+ var i = 0
+ var lowbits: Word = 0
+ while i < self.count || lowbits > 0 {
+ let word = self[i]
+ result[i + ext] = word << up | lowbits
+ lowbits = word >> down
+ i += 1
+ }
+ }
+ else {
+ for i in 0 ..< self.count {
+ result[i + ext] = self[i]
+ }
+ }
+ return result
+ }
+
+ internal mutating func shiftLeft(by amount: Word) {
+ guard amount > 0 else { return }
+
+ let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
+ let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
+ let down = Word(Word.bitWidth) - up
+
+ if up > 0 {
+ var i = 0
+ var lowbits: Word = 0
+ while i < self.count || lowbits > 0 {
+ let word = self[i]
+ self[i] = word << up | lowbits
+ lowbits = word >> down
+ i += 1
+ }
+ }
+ if ext > 0 && self.count > 0 {
+ self.shiftLeft(byWords: ext)
+ }
+ }
+
+ internal func shiftedRight(by amount: Word) -> CS.BigUInt {
+ guard amount > 0 else { return self }
+ guard amount < self.bitWidth else { return 0 }
+
+ let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
+ let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
+ let up = Word(Word.bitWidth) - down
+
+ var result = CS.BigUInt()
+ if down > 0 {
+ var highbits: Word = 0
+ for i in (ext ..< self.count).reversed() {
+ let word = self[i]
+ result[i - ext] = highbits | word >> down
+ highbits = word << up
+ }
+ }
+ else {
+ for i in (ext ..< self.count).reversed() {
+ result[i - ext] = self[i]
+ }
+ }
+ return result
+ }
+
+ internal mutating func shiftRight(by amount: Word) {
+ guard amount > 0 else { return }
+ guard amount < self.bitWidth else { self.clear(); return }
+
+ let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words)
+ let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift)
+ let up = Word(Word.bitWidth) - down
+
+ if ext > 0 {
+ self.shiftRight(byWords: ext)
+ }
+ if down > 0 {
+ var i = self.count - 1
+ var highbits: Word = 0
+ while i >= 0 {
+ let word = self[i]
+ self[i] = highbits | word >> down
+ highbits = word << up
+ i -= 1
+ }
+ }
+ }
+
+ public static func >>=<Other: BinaryInteger>(lhs: inout CS.BigUInt, rhs: Other) {
+ if rhs < (0 as Other) {
+ lhs <<= (0 - rhs)
+ }
+ else if rhs >= lhs.bitWidth {
+ lhs.clear()
+ }
+ else {
+ lhs.shiftRight(by: UInt(rhs))
+ }
+ }
+
+ public static func <<=<Other: BinaryInteger>(lhs: inout CS.BigUInt, rhs: Other) {
+ if rhs < (0 as Other) {
+ lhs >>= (0 - rhs)
+ return
+ }
+ lhs.shiftLeft(by: Word(exactly: rhs)!)
+ }
+
+ public static func >><Other: BinaryInteger>(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt {
+ if rhs < (0 as Other) {
+ return lhs << (0 - rhs)
+ }
+ if rhs > Word.max {
+ return 0
+ }
+ return lhs.shiftedRight(by: UInt(rhs))
+ }
+
+ public static func <<<Other: BinaryInteger>(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt {
+ if rhs < (0 as Other) {
+ return lhs >> (0 - rhs)
+ }
+ return lhs.shiftedLeft(by: Word(exactly: rhs)!)
+ }
+}
+
+extension CS.BigInt {
+ func shiftedLeft(by amount: Word) -> CS.BigInt {
+ return CS.BigInt(sign: self.sign, magnitude: self.magnitude.shiftedLeft(by: amount))
+ }
+
+ mutating func shiftLeft(by amount: Word) {
+ self.magnitude.shiftLeft(by: amount)
+ }
+
+ func shiftedRight(by amount: Word) -> CS.BigInt {
+ let m = self.magnitude.shiftedRight(by: amount)
+ return CS.BigInt(sign: self.sign, magnitude: self.sign == .minus && m.isZero ? 1 : m)
+ }
+
+ mutating func shiftRight(by amount: Word) {
+ magnitude.shiftRight(by: amount)
+ if sign == .minus, magnitude.isZero {
+ magnitude.load(1)
+ }
+ }
+
+ public static func &<<(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt {
+ return left.shiftedLeft(by: right.words[0])
+ }
+
+ public static func &<<=(left: inout CS.BigInt, right: CS.BigInt) {
+ left.shiftLeft(by: right.words[0])
+ }
+
+ public static func &>>(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt {
+ return left.shiftedRight(by: right.words[0])
+ }
+
+ public static func &>>=(left: inout CS.BigInt, right: CS.BigInt) {
+ left.shiftRight(by: right.words[0])
+ }
+
+ public static func <<<Other: BinaryInteger>(lhs: CS.BigInt, rhs: Other) -> CS.BigInt {
+ guard rhs >= (0 as Other) else { return lhs >> (0 - rhs) }
+ return lhs.shiftedLeft(by: Word(rhs))
+ }
+
+ public static func <<=<Other: BinaryInteger>(lhs: inout CS.BigInt, rhs: Other) {
+ if rhs < (0 as Other) {
+ lhs >>= (0 - rhs)
+ }
+ else {
+ lhs.shiftLeft(by: Word(rhs))
+ }
+ }
+
+ public static func >><Other: BinaryInteger>(lhs: CS.BigInt, rhs: Other) -> CS.BigInt {
+ guard rhs >= (0 as Other) else { return lhs << (0 - rhs) }
+ return lhs.shiftedRight(by: Word(rhs))
+ }
+
+ public static func >>=<Other: BinaryInteger>(lhs: inout CS.BigInt, rhs: Other) {
+ if rhs < (0 as Other) {
+ lhs <<= (0 - rhs)
+ }
+ else {
+ lhs.shiftRight(by: Word(rhs))
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public enum SignatureError: Error {
+ case sign
+ case verify
+}
+
+public protocol Signature: AnyObject {
+ var keySize: Int { get }
+
+ /// Sign the given bytes at once
+ ///
+ /// - parameter bytes: Plaintext data to be signed
+ /// - returns: The signed data
+ func sign(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
+ /// Sign the given bytes at once
+ ///
+ /// - parameter bytes: Plaintext data to be signed
+ /// - returns: The signed data
+ func sign(_ bytes: Array<UInt8>) throws -> Array<UInt8>
+
+ /// Verify the given bytes against the expected data
+ ///
+ /// - parameter signature: Signature data
+ /// - parameter expectedData: The original data that you expected to be signed
+ /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+ func verify(signature: ArraySlice<UInt8>, for expectedData: ArraySlice<UInt8>) throws -> Bool
+ /// Verify the given bytes against the expected data
+ ///
+ /// - parameter signature: Signature data
+ /// - parameter expectedData: The original data that you expected to be signed
+ /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+ func verify(signature: Array<UInt8>, for expectedData: Array<UInt8>) throws -> Bool
+}
+
+extension Signature {
+ /// Sign the given bytes at once
+ ///
+ /// - parameter bytes: Plaintext data to be signed
+ /// - returns: The signed data
+ public func sign(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+ try self.sign(bytes.slice)
+ }
+
+ /// Verify the given bytes against the expected data
+ ///
+ /// - parameter signature: Signature data
+ /// - parameter expectedData: The original data that you expected to be signed
+ /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+ public func verify(signature: Array<UInt8>, for expectedData: Array<UInt8>) throws -> Bool {
+ try self.verify(signature: signature.slice, for: expectedData.slice)
+ }
+}
--- /dev/null
+//
+// Square Root.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+//MARK: Square Root
+
+extension CS.BigUInt {
+ /// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`.
+ ///
+ /// - Returns: floor(sqrt(self))
+ public func squareRoot() -> CS.BigUInt {
+ // This implementation uses Newton's method.
+ guard !self.isZero else { return CS.BigUInt() }
+ var x = CS.BigUInt(1) << ((self.bitWidth + 1) / 2)
+ var y: CS.BigUInt = 0
+ while true {
+ y.load(self)
+ y /= x
+ y += x
+ y >>= 1
+ if x == y || x == y - 1 { break }
+ x = y
+ }
+ return x
+ }
+}
+
+extension CS.BigInt {
+ /// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`.
+ ///
+ /// - Requires: self >= 0
+ /// - Returns: floor(sqrt(self))
+ public func squareRoot() -> CS.BigInt {
+ precondition(self.sign == .plus)
+ return CS.BigInt(sign: .plus, magnitude: self.magnitude.squareRoot())
+ }
+}
--- /dev/null
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@usableFromInline
+final class StreamDecryptor: Cryptor, Updatable {
+
+ @usableFromInline
+ internal let blockSize: Int
+
+ @usableFromInline
+ internal var worker: CipherModeWorker
+
+ @usableFromInline
+ internal let padding: Padding
+
+ @usableFromInline
+ internal var accumulated = Array<UInt8>()
+
+ @usableFromInline
+ internal var lastBlockRemainder = 0
+
+ @usableFromInline
+ init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+ self.blockSize = blockSize
+ self.padding = padding
+ self.worker = worker
+ }
+
+ // MARK: Updatable
+
+ @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+ self.accumulated += bytes
+
+ let toProcess = self.accumulated.prefix(max(self.accumulated.count - self.worker.additionalBufferSize, 0))
+
+ if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
+ // will truncate suffix if needed
+ try finalizingWorker.willDecryptLast(bytes: self.accumulated.slice)
+ }
+
+ var processedBytesCount = 0
+ var plaintext = Array<UInt8>(reserveCapacity: bytes.count + self.worker.additionalBufferSize)
+ for chunk in toProcess.batched(by: self.blockSize) {
+ plaintext += self.worker.decrypt(block: chunk)
+ processedBytesCount += chunk.count
+ }
+
+ if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
+ plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice))
+ }
+
+ // omit unecessary calculation if not needed
+ if self.padding != .noPadding {
+ self.lastBlockRemainder = plaintext.count.quotientAndRemainder(dividingBy: self.blockSize).remainder
+ }
+
+ if isLast {
+ // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't.
+ plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize - self.lastBlockRemainder)
+ }
+
+ self.accumulated.removeFirst(processedBytesCount) // super-slow
+
+ if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true {
+ plaintext = Array(try finalizingWorker.finalize(decrypt: plaintext.slice))
+ }
+
+ return plaintext
+ }
+
+ @inlinable
+ public func seek(to position: Int) throws {
+ guard var worker = self.worker as? SeekableModeWorker else {
+ fatalError("Not supported")
+ }
+
+ try worker.seek(to: position)
+ self.worker = worker
+ }
+}
--- /dev/null
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@usableFromInline
+final class StreamEncryptor: Cryptor, Updatable {
+
+ @usableFromInline
+ internal let blockSize: Int
+
+ @usableFromInline
+ internal var worker: CipherModeWorker
+
+ @usableFromInline
+ internal let padding: Padding
+
+ @usableFromInline
+ internal var lastBlockRemainder = 0
+
+ @usableFromInline
+ init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws {
+ self.blockSize = blockSize
+ self.padding = padding
+ self.worker = worker
+ }
+
+ // MARK: Updatable
+
+ @inlinable
+ public func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8> {
+ var accumulated = Array(bytes)
+ if isLast {
+ // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't.
+ accumulated = self.padding.add(to: accumulated, blockSize: self.blockSize - self.lastBlockRemainder)
+ }
+
+ var encrypted = Array<UInt8>(reserveCapacity: bytes.count)
+ for chunk in accumulated.batched(by: self.blockSize) {
+ encrypted += self.worker.encrypt(block: chunk)
+ }
+
+ // omit unecessary calculation if not needed
+ if self.padding != .noPadding {
+ self.lastBlockRemainder = encrypted.count.quotientAndRemainder(dividingBy: self.blockSize).remainder
+ }
+
+ if var finalizingWorker = worker as? FinalizingEncryptModeWorker, isLast == true {
+ encrypted = Array(try finalizingWorker.finalize(encrypt: encrypted.slice))
+ }
+
+ return encrypted
+ }
+
+ @usableFromInline
+ func seek(to: Int) throws {
+ fatalError("Not supported")
+ }
+}
--- /dev/null
+//
+// Strideable.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2017-08-11.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt: Strideable {
+ /// A type that can represent the distance between two values ofa `BigUInt`.
+ public typealias Stride = CS.BigInt
+
+ /// Adds `n` to `self` and returns the result. Traps if the result would be less than zero.
+ public func advanced(by n: CS.BigInt) -> CS.BigUInt {
+ return n.sign == .minus ? self - n.magnitude : self + n.magnitude
+ }
+
+ /// Returns the (potentially negative) difference between `self` and `other` as a `BigInt`. Never traps.
+ public func distance(to other: CS.BigUInt) -> CS.BigInt {
+ return CS.BigInt(other) - CS.BigInt(self)
+ }
+}
+
+extension CS.BigInt: Strideable {
+ public typealias Stride = CS.BigInt
+
+ /// Returns `self + n`.
+ public func advanced(by n: Stride) -> CS.BigInt {
+ return self + n
+ }
+
+ /// Returns `other - self`.
+ public func distance(to other: CS.BigInt) -> Stride {
+ return other - self
+ }
+}
+
+
--- /dev/null
+//
+// String Conversion.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+
+ //MARK: String Conversion
+
+ /// Calculates the number of numerals in a given radix that fit inside a single `Word`.
+ ///
+ /// - Returns: (chars, power) where `chars` is highest that satisfy `radix^chars <= 2^Word.bitWidth`. `power` is zero
+ /// if radix is a power of two; otherwise `power == radix^chars`.
+ fileprivate static func charsPerWord(forRadix radix: Int) -> (chars: Int, power: Word) {
+ var power: Word = 1
+ var overflow = false
+ var count = 0
+ while !overflow {
+ let (high,low) = power.multipliedFullWidth(by: Word(radix))
+ if high > 0 {
+ overflow = true
+ }
+
+ if !overflow || (high == 1 && low == 0) {
+ count += 1
+ power = low
+ }
+ }
+ return (count, power)
+ }
+
+ /// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by
+ /// letters from the English alphabet.
+ ///
+ /// - Requires: `radix > 1 && radix < 36`
+ /// - Parameter `text`: A string consisting of characters corresponding to numerals in the given radix. (0-9, a-z, A-Z)
+ /// - Parameter `radix`: The base of the number system to use, or 10 if unspecified.
+ /// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`.
+ public init?<S: StringProtocol>(_ text: S, radix: Int = 10) {
+ precondition(radix > 1 && radix < 36)
+ guard !text.isEmpty else { return nil }
+ let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix)
+
+ var words: [Word] = []
+ var end = text.endIndex
+ var start = end
+ var count = 0
+ while start != text.startIndex {
+ start = text.index(before: start)
+ count += 1
+ if count == charsPerWord {
+ guard let d = Word.init(text[start ..< end], radix: radix) else { return nil }
+ words.append(d)
+ end = start
+ count = 0
+ }
+ }
+ if start != end {
+ guard let d = Word.init(text[start ..< end], radix: radix) else { return nil }
+ words.append(d)
+ }
+
+ if power == 0 {
+ self.init(words: words)
+ }
+ else {
+ self.init()
+ for d in words.reversed() {
+ self.multiply(byWord: power)
+ self.addWord(d)
+ }
+ }
+ }
+}
+
+extension CS.BigInt {
+ /// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by
+ /// letters from the English alphabet.
+ ///
+ /// - Requires: `radix > 1 && radix < 36`
+ /// - Parameter `text`: A string optionally starting with "-" or "+" followed by characters corresponding to numerals in the given radix. (0-9, a-z, A-Z)
+ /// - Parameter `radix`: The base of the number system to use, or 10 if unspecified.
+ /// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`.
+ public init?<S: StringProtocol>(_ text: S, radix: Int = 10) {
+ var magnitude: CS.BigUInt?
+ var sign: Sign = .plus
+ if text.first == "-" {
+ sign = .minus
+ let text = text.dropFirst()
+ magnitude = CS.BigUInt(text, radix: radix)
+ }
+ else if text.first == "+" {
+ let text = text.dropFirst()
+ magnitude = CS.BigUInt(text, radix: radix)
+ }
+ else {
+ magnitude = CS.BigUInt(text, radix: radix)
+ }
+ guard let m = magnitude else { return nil }
+ self.magnitude = m
+ self.sign = m.isZero ? .plus : sign
+ }
+}
+
+extension String {
+ /// Initialize a new string with the base-10 representation of an unsigned big integer.
+ ///
+ /// - Complexity: O(v.count^2)
+ public init(_ v: CS.BigUInt) { self.init(v, radix: 10, uppercase: false) }
+
+ /// Initialize a new string representing an unsigned big integer in the given radix (base).
+ ///
+ /// Numerals greater than 9 are represented as letters from the English alphabet,
+ /// starting with `a` if `uppercase` is false or `A` otherwise.
+ ///
+ /// - Requires: radix > 1 && radix <= 36
+ /// - Complexity: O(count) when radix is a power of two; otherwise O(count^2).
+ public init(_ v: CS.BigUInt, radix: Int, uppercase: Bool = false) {
+ precondition(radix > 1)
+ let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix)
+
+ guard !v.isZero else { self = "0"; return }
+
+ var parts: [String]
+ if power == 0 {
+ parts = v.words.map { String($0, radix: radix, uppercase: uppercase) }
+ }
+ else {
+ parts = []
+ var rest = v
+ while !rest.isZero {
+ let mod = rest.divide(byWord: power)
+ parts.append(String(mod, radix: radix, uppercase: uppercase))
+ }
+ }
+ assert(!parts.isEmpty)
+
+ self = ""
+ var first = true
+ for part in parts.reversed() {
+ let zeroes = charsPerWord - part.count
+ assert(zeroes >= 0)
+ if !first && zeroes > 0 {
+ // Insert leading zeroes for mid-Words
+ self += String(repeating: "0", count: zeroes)
+ }
+ first = false
+ self += part
+ }
+ }
+
+ /// Initialize a new string representing a signed big integer in the given radix (base).
+ ///
+ /// Numerals greater than 9 are represented as letters from the English alphabet,
+ /// starting with `a` if `uppercase` is false or `A` otherwise.
+ ///
+ /// - Requires: radix > 1 && radix <= 36
+ /// - Complexity: O(count) when radix is a power of two; otherwise O(count^2).
+ public init(_ value: CS.BigInt, radix: Int = 10, uppercase: Bool = false) {
+ self = String(value.magnitude, radix: radix, uppercase: uppercase)
+ if value.sign == .minus {
+ self = "-" + self
+ }
+ }
+}
+
+extension CS.BigUInt: ExpressibleByStringLiteral {
+ /// Initialize a new big integer from a Unicode scalar.
+ /// The scalar must represent a decimal digit.
+ public init(unicodeScalarLiteral value: UnicodeScalar) {
+ self = CS.BigUInt(String(value), radix: 10)!
+ }
+
+ /// Initialize a new big integer from an extended grapheme cluster.
+ /// The cluster must consist of a decimal digit.
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self = CS.BigUInt(value, radix: 10)!
+ }
+
+ /// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length.
+ /// The string must contain only decimal digits.
+ public init(stringLiteral value: StringLiteralType) {
+ self = CS.BigUInt(value, radix: 10)!
+ }
+}
+
+extension CS.BigInt: ExpressibleByStringLiteral {
+ /// Initialize a new big integer from a Unicode scalar.
+ /// The scalar must represent a decimal digit.
+ public init(unicodeScalarLiteral value: UnicodeScalar) {
+ self = CS.BigInt(String(value), radix: 10)!
+ }
+
+ /// Initialize a new big integer from an extended grapheme cluster.
+ /// The cluster must consist of a decimal digit.
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self = CS.BigInt(value, radix: 10)!
+ }
+
+ /// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length.
+ /// The string must contain only decimal digits.
+ public init(stringLiteral value: StringLiteralType) {
+ self = CS.BigInt(value, radix: 10)!
+ }
+}
+
+extension CS.BigUInt: CustomStringConvertible {
+ /// Return the decimal representation of this integer.
+ public var description: String {
+ return String(self, radix: 10)
+ }
+}
+
+extension CS.BigInt: CustomStringConvertible {
+ /// Return the decimal representation of this integer.
+ public var description: String {
+ return String(self, radix: 10)
+ }
+}
+
+extension CS.BigUInt: CustomPlaygroundDisplayConvertible {
+
+ /// Return the playground quick look representation of this integer.
+ public var playgroundDescription: Any {
+ let text = String(self)
+ return text + " (\(self.bitWidth) bits)"
+ }
+}
+
+extension CS.BigInt: CustomPlaygroundDisplayConvertible {
+
+ /// Return the playground quick look representation of this integer.
+ public var playgroundDescription: Any {
+ let text = String(self)
+ return text + " (\(self.magnitude.bitWidth) bits)"
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/** String extension */
+extension String {
+
+ @inlinable
+ public var bytes: Array<UInt8> {
+ data(using: String.Encoding.utf8, allowLossyConversion: true)?.bytes ?? Array(utf8)
+ }
+
+ @inlinable
+ public func md5() -> String {
+ self.bytes.md5().toHexString()
+ }
+
+ @inlinable
+ public func sha1() -> String {
+ self.bytes.sha1().toHexString()
+ }
+
+ @inlinable
+ public func sha224() -> String {
+ self.bytes.sha224().toHexString()
+ }
+
+ @inlinable
+ public func sha256() -> String {
+ self.bytes.sha256().toHexString()
+ }
+
+ @inlinable
+ public func sha384() -> String {
+ self.bytes.sha384().toHexString()
+ }
+
+ @inlinable
+ public func sha512() -> String {
+ self.bytes.sha512().toHexString()
+ }
+
+ @inlinable
+ public func sha3(_ variant: SHA3.Variant) -> String {
+ self.bytes.sha3(variant).toHexString()
+ }
+
+ @inlinable
+ public func crc32(seed: UInt32? = nil, reflect: Bool = true) -> String {
+ self.bytes.crc32(seed: seed, reflect: reflect).bytes().toHexString()
+ }
+
+ @inlinable
+ public func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> String {
+ self.bytes.crc32c(seed: seed, reflect: reflect).bytes().toHexString()
+ }
+
+ @inlinable
+ public func crc16(seed: UInt16? = nil) -> String {
+ self.bytes.crc16(seed: seed).bytes().toHexString()
+ }
+
+ /// - parameter cipher: Instance of `Cipher`
+ /// - returns: hex string of bytes
+ @inlinable
+ public func encrypt(cipher: Cipher) throws -> String {
+ try self.bytes.encrypt(cipher: cipher).toHexString()
+ }
+
+ /// - parameter cipher: Instance of `Cipher`
+ /// - returns: base64 encoded string of encrypted bytes
+ @inlinable
+ public func encryptToBase64(cipher: Cipher) throws -> String {
+ try self.bytes.encrypt(cipher: cipher).toBase64()
+ }
+
+ // decrypt() does not make sense for String
+
+ /// - parameter authenticator: Instance of `Authenticator`
+ /// - returns: hex string of string
+ @inlinable
+ public func authenticate<A: Authenticator>(with authenticator: A) throws -> String {
+ try self.bytes.authenticate(with: authenticator).toHexString()
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+extension String {
+ /// Return Base64 back to String
+ public func decryptBase64ToString(cipher: Cipher) throws -> String {
+ guard let decodedData = Data(base64Encoded: self, options: []) else {
+ throw CipherError.decrypt
+ }
+
+ let decrypted = try decodedData.decrypt(cipher: cipher)
+
+ if let decryptedString = String(data: decrypted, encoding: String.Encoding.utf8) {
+ return decryptedString
+ }
+
+ throw CipherError.decrypt
+ }
+
+ public func decryptBase64(cipher: Cipher) throws -> Array<UInt8> {
+ guard let decodedData = Data(base64Encoded: self, options: []) else {
+ throw CipherError.decrypt
+ }
+
+ return try decodedData.decrypt(cipher: cipher).bytes
+ }
+}
--- /dev/null
+//
+// Subtraction.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2016-01-03.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension CS.BigUInt {
+ //MARK: Subtraction
+
+ /// Subtract `word` from this integer in place, returning a flag indicating if the operation
+ /// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted.
+ ///
+ /// - Note: If the result indicates an overflow, then `self` becomes the two's complement of the absolute difference.
+ /// - Complexity: O(count)
+ internal mutating func subtractWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> Bool {
+ precondition(shift >= 0)
+ var carry: Word = word
+ var i = shift
+ let count = self.count
+ while carry > 0 && i < count {
+ let (d, c) = self[i].subtractingReportingOverflow(carry)
+ self[i] = d
+ carry = (c ? 1 : 0)
+ i += 1
+ }
+ return carry > 0
+ }
+
+ /// Subtract `word` from this integer, returning the difference and a flag that is true if the operation
+ /// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted.
+ ///
+ /// - Note: If `overflow` is true, then the returned value is the two's complement of the absolute difference.
+ /// - Complexity: O(count)
+ internal func subtractingWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> (partialValue: CS.BigUInt, overflow: Bool) {
+ var result = self
+ let overflow = result.subtractWordReportingOverflow(word, shiftedBy: shift)
+ return (result, overflow)
+ }
+
+ /// Subtract a digit `d` from this integer in place.
+ /// `d` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Requires: self >= d * 2^shift
+ /// - Complexity: O(count)
+ internal mutating func subtractWord(_ word: Word, shiftedBy shift: Int = 0) {
+ let overflow = subtractWordReportingOverflow(word, shiftedBy: shift)
+ precondition(!overflow)
+ }
+
+ /// Subtract a digit `d` from this integer and return the result.
+ /// `d` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Requires: self >= d * 2^shift
+ /// - Complexity: O(count)
+ internal func subtractingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt {
+ var result = self
+ result.subtractWord(word, shiftedBy: shift)
+ return result
+ }
+
+ /// Subtract `other` from this integer in place, and return a flag indicating if the operation caused an
+ /// arithmetic overflow. `other` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Note: If the result indicates an overflow, then `self` becomes the twos' complement of the absolute difference.
+ /// - Complexity: O(count)
+ public mutating func subtractReportingOverflow(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> Bool {
+ precondition(shift >= 0)
+ var carry = false
+ var bi = 0
+ let bc = b.count
+ let count = self.count
+ while bi < bc || (shift + bi < count && carry) {
+ let ai = shift + bi
+ let (d, c) = self[ai].subtractingReportingOverflow(b[bi])
+ if carry {
+ let (d2, c2) = d.subtractingReportingOverflow(1)
+ self[ai] = d2
+ carry = c || c2
+ }
+ else {
+ self[ai] = d
+ carry = c
+ }
+ bi += 1
+ }
+ return carry
+ }
+
+ /// Subtract `other` from this integer, returning the difference and a flag indicating arithmetic overflow.
+ /// `other` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Note: If `overflow` is true, then the result value is the twos' complement of the absolute value of the difference.
+ /// - Complexity: O(count)
+ public func subtractingReportingOverflow(_ other: CS.BigUInt, shiftedBy shift: Int) -> (partialValue: CS.BigUInt, overflow: Bool) {
+ var result = self
+ let overflow = result.subtractReportingOverflow(other, shiftedBy: shift)
+ return (result, overflow)
+ }
+
+ /// Subtracts `other` from `self`, returning the result and a flag indicating arithmetic overflow.
+ ///
+ /// - Note: When the operation overflows, then `partialValue` is the twos' complement of the absolute value of the difference.
+ /// - Complexity: O(count)
+ public func subtractingReportingOverflow(_ other: CS.BigUInt) -> (partialValue: CS.BigUInt, overflow: Bool) {
+ return self.subtractingReportingOverflow(other, shiftedBy: 0)
+ }
+
+ /// Subtract `other` from this integer in place.
+ /// `other` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Requires: self >= other * 2^shift
+ /// - Complexity: O(count)
+ public mutating func subtract(_ other: CS.BigUInt, shiftedBy shift: Int = 0) {
+ let overflow = subtractReportingOverflow(other, shiftedBy: shift)
+ precondition(!overflow)
+ }
+
+ /// Subtract `b` from this integer, and return the difference.
+ /// `b` is shifted `shift` digits to the left before being subtracted.
+ ///
+ /// - Requires: self >= b * 2^shift
+ /// - Complexity: O(count)
+ public func subtracting(_ other: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt {
+ var result = self
+ result.subtract(other, shiftedBy: shift)
+ return result
+ }
+
+ /// Decrement this integer by one.
+ ///
+ /// - Requires: !isZero
+ /// - Complexity: O(count)
+ public mutating func decrement(shiftedBy shift: Int = 0) {
+ self.subtract(1, shiftedBy: shift)
+ }
+
+ /// Subtract `b` from `a` and return the result.
+ ///
+ /// - Requires: a >= b
+ /// - Complexity: O(a.count)
+ public static func -(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt {
+ return a.subtracting(b)
+ }
+
+ /// Subtract `b` from `a` and store the result in `a`.
+ ///
+ /// - Requires: a >= b
+ /// - Complexity: O(a.count)
+ public static func -=(a: inout CS.BigUInt, b: CS.BigUInt) {
+ a.subtract(b)
+ }
+}
+
+extension CS.BigInt {
+ public mutating func negate() {
+ guard !magnitude.isZero else { return }
+ self.sign = self.sign == .plus ? .minus : .plus
+ }
+
+ /// Subtract `b` from `a` and return the result.
+ public static func -(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt {
+ return a + -b
+ }
+
+ /// Subtract `b` from `a` in place.
+ public static func -=(a: inout CS.BigInt, b: CS.BigInt) { a = a - b }
+}
--- /dev/null
+//
+// UInt128.swift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+struct UInt128: Equatable, ExpressibleByIntegerLiteral {
+ let i: (a: UInt64, b: UInt64)
+
+ typealias IntegerLiteralType = UInt64
+
+ init(integerLiteral value: IntegerLiteralType) {
+ self = UInt128(value)
+ }
+
+ init(_ raw: Array<UInt8>) {
+ self = raw.prefix(MemoryLayout<UInt128>.stride).withUnsafeBytes({ (rawBufferPointer) -> UInt128 in
+ let arr = rawBufferPointer.bindMemory(to: UInt64.self)
+ return UInt128((arr[0].bigEndian, arr[1].bigEndian))
+ })
+ }
+
+ init(_ raw: ArraySlice<UInt8>) {
+ self.init(Array(raw))
+ }
+
+ init(_ i: (a: UInt64, b: UInt64)) {
+ self.i = i
+ }
+
+ init(a: UInt64, b: UInt64) {
+ self.init((a, b))
+ }
+
+ init(_ b: UInt64) {
+ self.init((0, b))
+ }
+
+ // Bytes
+ var bytes: Array<UInt8> {
+ var at = self.i.a.bigEndian
+ var bt = self.i.b.bigEndian
+
+ let ar = Data(bytes: &at, count: MemoryLayout.size(ofValue: at))
+ let br = Data(bytes: &bt, count: MemoryLayout.size(ofValue: bt))
+
+ var result = Data()
+ result.append(ar)
+ result.append(br)
+ return result.bytes
+ }
+
+ static func ^ (n1: UInt128, n2: UInt128) -> UInt128 {
+ UInt128((n1.i.a ^ n2.i.a, n1.i.b ^ n2.i.b))
+ }
+
+ static func & (n1: UInt128, n2: UInt128) -> UInt128 {
+ UInt128((n1.i.a & n2.i.a, n1.i.b & n2.i.b))
+ }
+
+ static func >> (value: UInt128, by: Int) -> UInt128 {
+ var result = value
+ for _ in 0..<by {
+ let a = result.i.a >> 1
+ let b = result.i.b >> 1 + ((result.i.a & 1) << 63)
+ result = UInt128((a, b))
+ }
+ return result
+ }
+
+ // Equatable.
+ static func == (lhs: UInt128, rhs: UInt128) -> Bool {
+ lhs.i == rhs.i
+ }
+
+ static func != (lhs: UInt128, rhs: UInt128) -> Bool {
+ !(lhs == rhs)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/** array of bytes */
+extension UInt16 {
+ @_specialize(where T == ArraySlice<UInt8>)
+ init<T: Collection>(bytes: T) where T.Element == UInt8, T.Index == Int {
+ self = UInt16(bytes: bytes, fromIndex: bytes.startIndex)
+ }
+
+ @_specialize(where T == ArraySlice<UInt8>)
+ init<T: Collection>(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int {
+ if bytes.isEmpty {
+ self = 0
+ return
+ }
+
+ let count = bytes.count
+
+ let val0 = count > 0 ? UInt16(bytes[index.advanced(by: 0)]) << 8 : 0
+ let val1 = count > 1 ? UInt16(bytes[index.advanced(by: 1)]) : 0
+
+ self = val0 | val1
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#endif
+
+protocol _UInt32Type {}
+extension UInt32: _UInt32Type {}
+
+/** array of bytes */
+extension UInt32 {
+ @_specialize(where T == ArraySlice<UInt8>)
+ init<T: Collection>(bytes: T) where T.Element == UInt8, T.Index == Int {
+ self = UInt32(bytes: bytes, fromIndex: bytes.startIndex)
+ }
+
+ @_specialize(where T == ArraySlice<UInt8>)
+ @inlinable
+ init<T: Collection>(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int {
+ if bytes.isEmpty {
+ self = 0
+ return
+ }
+
+ let count = bytes.count
+
+ let val0 = count > 0 ? UInt32(bytes[index.advanced(by: 0)]) << 24 : 0
+ let val1 = count > 1 ? UInt32(bytes[index.advanced(by: 1)]) << 16 : 0
+ let val2 = count > 2 ? UInt32(bytes[index.advanced(by: 2)]) << 8 : 0
+ let val3 = count > 3 ? UInt32(bytes[index.advanced(by: 3)]) : 0
+
+ self = val0 | val1 | val2 | val3
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/** array of bytes */
+extension UInt64 {
+ @_specialize(where T == ArraySlice<UInt8>)
+ init<T: Collection>(bytes: T) where T.Element == UInt8, T.Index == Int {
+ self = UInt64(bytes: bytes, fromIndex: bytes.startIndex)
+ }
+
+ @_specialize(where T == ArraySlice<UInt8>)
+ @inlinable
+ init<T: Collection>(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int {
+ if bytes.isEmpty {
+ self = 0
+ return
+ }
+
+ let count = bytes.count
+
+ let val0 = count > 0 ? UInt64(bytes[index.advanced(by: 0)]) << 56 : 0
+ let val1 = count > 1 ? UInt64(bytes[index.advanced(by: 1)]) << 48 : 0
+ let val2 = count > 2 ? UInt64(bytes[index.advanced(by: 2)]) << 40 : 0
+ let val3 = count > 3 ? UInt64(bytes[index.advanced(by: 3)]) << 32 : 0
+ let val4 = count > 4 ? UInt64(bytes[index.advanced(by: 4)]) << 24 : 0
+ let val5 = count > 5 ? UInt64(bytes[index.advanced(by: 5)]) << 16 : 0
+ let val6 = count > 6 ? UInt64(bytes[index.advanced(by: 6)]) << 8 : 0
+ let val7 = count > 7 ? UInt64(bytes[index.advanced(by: 7)]) : 0
+
+ self = val0 | val1 | val2 | val3 | val4 | val5 | val6 | val7
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Darwin)
+import Darwin
+#elseif canImport(Glibc)
+import Glibc
+#elseif canImport(ucrt)
+import ucrt
+#endif
+
+public protocol _UInt8Type {}
+extension UInt8: _UInt8Type {}
+
+/** casting */
+extension UInt8 {
+ /** cast because UInt8(<UInt32>) because std initializer crash if value is > byte */
+ static func with(value: UInt64) -> UInt8 {
+ let tmp = value & 0xff
+ return UInt8(tmp)
+ }
+
+ static func with(value: UInt32) -> UInt8 {
+ let tmp = value & 0xff
+ return UInt8(tmp)
+ }
+
+ static func with(value: UInt16) -> UInt8 {
+ let tmp = value & 0xff
+ return UInt8(tmp)
+ }
+}
+
+/** Bits */
+extension UInt8 {
+ /** array of bits */
+ public func bits() -> [Bit] {
+ let totalBitsCount = MemoryLayout<UInt8>.size * 8
+
+ var bitsArray = [Bit](repeating: Bit.zero, count: totalBitsCount)
+
+ for j in 0..<totalBitsCount {
+ let bitVal: UInt8 = 1 << UInt8(totalBitsCount - 1 - j)
+ let check = self & bitVal
+
+ if check != 0 {
+ bitsArray[j] = Bit.one
+ }
+ }
+ return bitsArray
+ }
+
+ public func bits() -> String {
+ var s = String()
+ let arr: [Bit] = self.bits()
+ for idx in arr.indices {
+ s += (arr[idx] == Bit.one ? "1" : "0")
+ if idx.advanced(by: 1) % 8 == 0 { s += " " }
+ }
+ return s
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// A type that supports incremental updates. For example Digest or Cipher may be updatable
+/// and calculate result incerementally.
+public protocol Updatable {
+ /// Update given bytes in chunks.
+ ///
+ /// - parameter bytes: Bytes to process.
+ /// - parameter isLast: Indicate if given chunk is the last one. No more updates after this call.
+ /// - returns: Processed partial result data or empty array.
+ mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool) throws -> Array<UInt8>
+
+ /// Update given bytes in chunks.
+ ///
+ /// - Parameters:
+ /// - bytes: Bytes to process.
+ /// - isLast: Indicate if given chunk is the last one. No more updates after this call.
+ /// - output: Resulting bytes callback.
+ /// - Returns: Processed partial result data or empty array.
+ mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool, output: (_ bytes: Array<UInt8>) -> Void) throws
+}
+
+extension Updatable {
+ @inlinable
+ public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false, output: (_ bytes: Array<UInt8>) -> Void) throws {
+ let processed = try update(withBytes: bytes, isLast: isLast)
+ if !processed.isEmpty {
+ output(processed)
+ }
+ }
+
+ @inlinable
+ public mutating func update(withBytes bytes: ArraySlice<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ try self.update(withBytes: bytes, isLast: isLast)
+ }
+
+ @inlinable
+ public mutating func update(withBytes bytes: Array<UInt8>, isLast: Bool = false) throws -> Array<UInt8> {
+ try self.update(withBytes: bytes.slice, isLast: isLast)
+ }
+
+ @inlinable
+ public mutating func update(withBytes bytes: Array<UInt8>, isLast: Bool = false, output: (_ bytes: Array<UInt8>) -> Void) throws {
+ try self.update(withBytes: bytes.slice, isLast: isLast, output: output)
+ }
+
+ /// Finish updates. This may apply padding.
+ /// - parameter bytes: Bytes to process
+ /// - returns: Processed data.
+ @inlinable
+ public mutating func finish(withBytes bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+ try self.update(withBytes: bytes, isLast: true)
+ }
+
+ @inlinable
+ public mutating func finish(withBytes bytes: Array<UInt8>) throws -> Array<UInt8> {
+ try self.finish(withBytes: bytes.slice)
+ }
+
+ /// Finish updates. May add padding.
+ ///
+ /// - Returns: Processed data
+ /// - Throws: Error
+ @inlinable
+ public mutating func finish() throws -> Array<UInt8> {
+ try self.update(withBytes: [], isLast: true)
+ }
+
+ /// Finish updates. This may apply padding.
+ /// - parameter bytes: Bytes to process
+ /// - parameter output: Resulting data
+ /// - returns: Processed data.
+ @inlinable
+ public mutating func finish(withBytes bytes: ArraySlice<UInt8>, output: (_ bytes: Array<UInt8>) -> Void) throws {
+ let processed = try update(withBytes: bytes, isLast: true)
+ if !processed.isEmpty {
+ output(processed)
+ }
+ }
+
+ @inlinable
+ public mutating func finish(withBytes bytes: Array<UInt8>, output: (_ bytes: Array<UInt8>) -> Void) throws {
+ try self.finish(withBytes: bytes.slice, output: output)
+ }
+
+ /// Finish updates. May add padding.
+ ///
+ /// - Parameter output: Processed data
+ /// - Throws: Error
+ @inlinable
+ public mutating func finish(output: (Array<UInt8>) -> Void) throws {
+ try self.finish(withBytes: [], output: output)
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+func perf(_ text: String, closure: () -> Void) {
+ let measurementStart = Date()
+
+ closure()
+
+ let measurementStop = Date()
+ let executionTime = measurementStop.timeIntervalSince(measurementStart)
+
+ print("\(text) \(executionTime)")
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+@inlinable
+func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
+ ((value << by) & 0xff) | (value >> (8 - by))
+}
+
+@inlinable
+func rotateLeft(_ value: UInt16, by: UInt16) -> UInt16 {
+ ((value << by) & 0xffff) | (value >> (16 - by))
+}
+
+@inlinable
+func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
+ ((value << by) & 0xffffffff) | (value >> (32 - by))
+}
+
+@inlinable
+func rotateLeft(_ value: UInt64, by: UInt64) -> UInt64 {
+ (value << by) | (value >> (64 - by))
+}
+
+@inlinable
+func rotateRight(_ value: UInt16, by: UInt16) -> UInt16 {
+ (value >> by) | (value << (16 - by))
+}
+
+@inlinable
+func rotateRight(_ value: UInt32, by: UInt32) -> UInt32 {
+ (value >> by) | (value << (32 - by))
+}
+
+@inlinable
+func rotateRight(_ value: UInt64, by: UInt64) -> UInt64 {
+ ((value >> by) | (value << (64 - by)))
+}
+
+@inlinable
+func reversed(_ uint8: UInt8) -> UInt8 {
+ var v = uint8
+ v = (v & 0xf0) >> 4 | (v & 0x0f) << 4
+ v = (v & 0xcc) >> 2 | (v & 0x33) << 2
+ v = (v & 0xaa) >> 1 | (v & 0x55) << 1
+ return v
+}
+
+@inlinable
+func reversed(_ uint32: UInt32) -> UInt32 {
+ var v = uint32
+ v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1)
+ v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2)
+ v = ((v >> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4)
+ v = ((v >> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8)
+ v = ((v >> 16) & 0xffff) | ((v & 0xffff) << 16)
+ return v
+}
+
+@inlinable
+func xor<T, V>(_ left: T, _ right: V) -> ArraySlice<UInt8> where T: RandomAccessCollection, V: RandomAccessCollection, T.Element == UInt8, T.Index == Int, V.Element == UInt8, V.Index == Int {
+ return xor(left, right).slice
+}
+
+@inlinable
+func xor<T, V>(_ left: T, _ right: V) -> Array<UInt8> where T: RandomAccessCollection, V: RandomAccessCollection, T.Element == UInt8, T.Index == Int, V.Element == UInt8, V.Index == Int {
+ let length = Swift.min(left.count, right.count)
+
+ let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: length)
+ buf.initialize(repeating: 0, count: length)
+ defer {
+ buf.deinitialize(count: length)
+ buf.deallocate()
+ }
+
+ // xor
+ for i in 0..<length {
+ buf[i] = left[left.startIndex.advanced(by: i)] ^ right[right.startIndex.advanced(by: i)]
+ }
+
+ return Array(UnsafeBufferPointer(start: buf, count: length))
+}
+
+/**
+ ISO/IEC 9797-1 Padding method 2.
+ Add a single bit with value 1 to the end of the data.
+ If necessary add bits with value 0 to the end of the data until the padded data is a multiple of blockSize.
+ - parameters:
+ - blockSize: Padding size in bytes.
+ - allowance: Excluded trailing number of bytes.
+ */
+@inline(__always) @inlinable
+func bitPadding(to data: inout Array<UInt8>, blockSize: Int, allowance: Int = 0) {
+ let msgLength = data.count
+ // Step 1. Append Padding Bits
+ // append one bit (UInt8 with one bit) to message
+ data.append(0x80)
+
+ // Step 2. append "0" bit until message length in bits ≡ 448 (mod 512)
+ let max = blockSize - allowance // 448, 986
+ if msgLength % blockSize < max { // 448
+ data += Array<UInt8>(repeating: 0, count: max - 1 - (msgLength % blockSize))
+ } else {
+ data += Array<UInt8>(repeating: 0, count: blockSize + max - 1 - (msgLength % blockSize))
+ }
+}
--- /dev/null
+//
+// Words and Bits.swift
+// CS.BigInt
+//
+// Created by Károly Lőrentey on 2017-08-11.
+// Copyright © 2016-2017 Károly Lőrentey.
+//
+
+extension Array where Element == UInt {
+ mutating func twosComplement() {
+ var increment = true
+ for i in 0 ..< self.count {
+ if increment {
+ (self[i], increment) = (~self[i]).addingReportingOverflow(1)
+ }
+ else {
+ self[i] = ~self[i]
+ }
+ }
+ }
+}
+
+extension CS.BigUInt {
+ public subscript(bitAt index: Int) -> Bool {
+ get {
+ precondition(index >= 0)
+ let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth)
+ return self[i] & (1 << j) != 0
+ }
+ set {
+ precondition(index >= 0)
+ let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth)
+ if newValue {
+ self[i] |= 1 << j
+ }
+ else {
+ self[i] &= ~(1 << j)
+ }
+ }
+ }
+}
+
+extension CS.BigUInt {
+ /// The minimum number of bits required to represent this integer in binary.
+ ///
+ /// - Returns: floor(log2(2 * self + 1))
+ /// - Complexity: O(1)
+ public var bitWidth: Int {
+ guard count > 0 else { return 0 }
+ return count * Word.bitWidth - self[count - 1].leadingZeroBitCount
+ }
+
+ /// The number of leading zero bits in the binary representation of this integer in base `2^(Word.bitWidth)`.
+ /// This is useful when you need to normalize a `BigUInt` such that the top bit of its most significant word is 1.
+ ///
+ /// - Note: 0 is considered to have zero leading zero bits.
+ /// - Returns: A value in `0...(Word.bitWidth - 1)`.
+ /// - SeeAlso: width
+ /// - Complexity: O(1)
+ public var leadingZeroBitCount: Int {
+ guard count > 0 else { return 0 }
+ return self[count - 1].leadingZeroBitCount
+ }
+
+ /// The number of trailing zero bits in the binary representation of this integer.
+ ///
+ /// - Note: 0 is considered to have zero trailing zero bits.
+ /// - Returns: A value in `0...width`.
+ /// - Complexity: O(count)
+ public var trailingZeroBitCount: Int {
+ guard count > 0 else { return 0 }
+ let i = self.words.firstIndex { $0 != 0 }!
+ return i * Word.bitWidth + self[i].trailingZeroBitCount
+ }
+}
+
+extension CS.BigInt {
+ public var bitWidth: Int {
+ guard !magnitude.isZero else { return 0 }
+ return magnitude.bitWidth + 1
+ }
+
+ public var trailingZeroBitCount: Int {
+ // Amazingly, this works fine for negative numbers
+ return magnitude.trailingZeroBitCount
+ }
+}
+
+extension CS.BigUInt {
+ public struct Words: RandomAccessCollection {
+ private let value: CS.BigUInt
+
+ fileprivate init(_ value: CS.BigUInt) { self.value = value }
+
+ public var startIndex: Int { return 0 }
+ public var endIndex: Int { return value.count }
+
+ public subscript(_ index: Int) -> Word {
+ return value[index]
+ }
+ }
+
+ public var words: Words { return Words(self) }
+
+ public init<Words: Sequence>(words: Words) where Words.Element == Word {
+ let uc = words.underestimatedCount
+ if uc > 2 {
+ self.init(words: Array(words))
+ }
+ else {
+ var it = words.makeIterator()
+ guard let w0 = it.next() else {
+ self.init()
+ return
+ }
+ guard let w1 = it.next() else {
+ self.init(word: w0)
+ return
+ }
+ if let w2 = it.next() {
+ var words: [UInt] = []
+ words.reserveCapacity(Swift.max(3, uc))
+ words.append(w0)
+ words.append(w1)
+ words.append(w2)
+ while let word = it.next() {
+ words.append(word)
+ }
+ self.init(words: words)
+ }
+ else {
+ self.init(low: w0, high: w1)
+ }
+ }
+ }
+}
+
+extension CS.BigInt {
+ public struct Words: RandomAccessCollection {
+ public typealias Indices = CountableRange<Int>
+
+ private let value: CS.BigInt
+ private let decrementLimit: Int
+
+ fileprivate init(_ value: CS.BigInt) {
+ self.value = value
+ switch value.sign {
+ case .plus:
+ self.decrementLimit = 0
+ case .minus:
+ assert(!value.magnitude.isZero)
+ self.decrementLimit = value.magnitude.words.firstIndex(where: { $0 != 0 })!
+ }
+ }
+
+ public var count: Int {
+ switch value.sign {
+ case .plus:
+ if let high = value.magnitude.words.last, high >> (Word.bitWidth - 1) != 0 {
+ return value.magnitude.count + 1
+ }
+ return value.magnitude.count
+ case .minus:
+ let high = value.magnitude.words.last!
+ if high >> (Word.bitWidth - 1) != 0 {
+ return value.magnitude.count + 1
+ }
+ return value.magnitude.count
+ }
+ }
+
+ public var indices: Indices { return 0 ..< count }
+ public var startIndex: Int { return 0 }
+ public var endIndex: Int { return count }
+
+ public subscript(_ index: Int) -> UInt {
+ // Note that indices above `endIndex` are accepted.
+ if value.sign == .plus {
+ return value.magnitude[index]
+ }
+ if index <= decrementLimit {
+ return ~(value.magnitude[index] &- 1)
+ }
+ return ~value.magnitude[index]
+ }
+ }
+
+ public var words: Words {
+ return Words(self)
+ }
+
+ public init<S: Sequence>(words: S) where S.Element == Word {
+ var words = Array(words)
+ if (words.last ?? 0) >> (Word.bitWidth - 1) == 0 {
+ self.init(sign: .plus, magnitude: CS.BigUInt(words: words))
+ }
+ else {
+ words.twosComplement()
+ self.init(sign: .minus, magnitude: CS.BigUInt(words: words))
+ }
+ }
+}
--- /dev/null
+//
+// CryptoSwift
+//
+// Copyright (C) 2014-2022 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+
+/// All the bytes that are required to be padded are padded with zero.
+/// Zero padding may not be reversible if the original file ends with one or more zero bytes.
+struct ZeroPadding: PaddingProtocol {
+ init() {
+ }
+
+ @inlinable
+ func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+ let paddingCount = blockSize - (bytes.count % blockSize)
+ if paddingCount > 0 {
+ return bytes + Array<UInt8>(repeating: 0, count: paddingCount)
+ }
+ return bytes
+ }
+
+ @inlinable
+ func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+ for (idx, value) in bytes.reversed().enumerated() {
+ if value != 0 {
+ return Array(bytes[0..<bytes.count - idx])
+ }
+ }
+ return bytes
+ }
+}
--- /dev/null
+//
+// IotMQTT.h
+// AliCloudIotKit
+//
+// Created by cookies on 2023/8/10.
+//
+
+#ifndef iotMQTT_h
+#define iotMQTT_h
+
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+typedef NS_ENUM(NSUInteger, MqttConnectStatus_C) {
+DISCONNECTED=0,
+CONNECTED=1
+};
+
+@interface IotMQTT : NSObject
+
+
+/**
+ 初始化接口,适用于通过productKey, deviceName, deviceSecret实现mqtt连云的情况
+ 返回值:0表示成功,-1表示失败
+ */
+-(int32_t)mqttInit:(NSString* _Nonnull)productKey deviceName:(NSString* _Nonnull)deviceName deviceSecret:(NSString* _Nonnull)deviceSecret
+ hostUrl:(NSString* _Nonnull)hostUrl
+ port:(int)port;
+
+/**
+初始化接口,适用于通过clientId,username, password实现mqtt连云的情况
+返回值:0表示成功,-1表示失败
+*/
+-(int32_t)mqttLegacyInit:(NSString* _Nonnull)clientId userName:(NSString* _Nonnull)userName passWord:(NSString* _Nonnull)passWord
+ hostUrl:(NSString* _Nonnull)hostUrl
+ port:(int)port;
+
+
+/**
+接听连接状态的回调,要在调用mqttInit之前完成设置
+返回值:void
+*/
+-(void)setConnectStatusCallback:(void(^_Nonnull)(MqttConnectStatus_C ConnectStatus)) callback;
+
+/**
+下行消息到达的回调,建议在调用mqttInit之前完成设置
+返回值:void
+*/
+-(void)setMessageArrivalCallback:(void(^_Nonnull)(NSString* _Nonnull topic, NSString * _Nonnull payload))callback;
+
+/**
+订阅mqtt topic
+ 返回值:0表示发送订阅报文成功
+*/
+-(int32_t) mqttSub:(NSString* _Nonnull)topic qos:(int)qos;
+
+/**
+反订阅mqtt topic
+返回值:0表示发送反订阅报文成功
+*/
+-(int32_t)mqttUnSub:(NSString* _Nonnull)topic;
+
+/**
+发送mqtt报文
+返回值:
+ 对于qos=0的报文, 返回0表示发送成功,负数表示发送失败
+ 对于qos=1,返回的>=0的数值表示消息id,返回负数表示失败
+*/
+-(int32_t)mqttPub:(NSString* _Nonnull)topic payload:(NSString* _Nonnull)payload qos:(int)qos;
+
+/**
+反初始化接口
+返回值:0表示成功,负数表示失败
+*/
+-(int32_t)mqttDeinit;
+
++(instancetype _Nonnull ) shareInstance;
+
+@property (nonatomic, strong) NSString * _Nonnull productKey;
+@property (nonatomic, strong) NSString * _Nonnull deviceName;
+@property (nonatomic, strong) NSString * _Nonnull deviceSecret;
+
+@property (nonatomic, strong) NSString * _Nonnull clientId;
+@property (nonatomic, strong) NSString * _Nonnull userName;
+@property (nonatomic, strong) NSString * _Nonnull password;
+
+
+@property (nonatomic, strong) NSString * _Nonnull hostUrl;
+@property (nonatomic) int port;
+
+@property(nonatomic,weak) void(^ _Nullable gConnectStatusCallback)(MqttConnectStatus_C ConnectStatus);
+@property(nonatomic,weak) void(^ _Nullable gMessageArrivaldCallback)(NSString* _Nonnull topic, NSString * _Nonnull payload);
+
+@end
+#endif /* IotMQTT_h */
--- /dev/null
+//
+// iot_rtc.h
+// RtcSample
+//
+// Created by xicai.cxc on 2022/7/5.
+// Copyright © 2022 tiantian. All rights reserved.
+//
+
+#ifndef iot_mqtt_h
+#define iot_mqtt_h
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+typedef NS_ENUM(NSUInteger, MqttConnectStatus_C) {
+DISCONNECTED=0,
+CONNECTED=1
+};
+
+@interface IotMQTT : NSObject
+
+-(int32_t)mqttInit:(NSString* _Nonnull)productKey deviceName:(NSString* _Nonnull)deviceName deviceSecret:(NSString* _Nonnull)deviceSecret
+ hostUrl:(NSString* _Nonnull)hostUrl
+ port:(int)port;
+
+-(int32_t)mqttLegacyInit:(NSString* _Nonnull)clientId userName:(NSString* _Nonnull)userName passWord:(NSString* _Nonnull)passWord
+ hostUrl:(NSString* _Nonnull)hostUrl
+ port:(int)port;
+
+-(int32_t)mqttPub:(NSString* _Nonnull)topic payload:(NSString* _Nonnull)payload qos:(int)qos;
+
+-(int32_t)mqttPub:(NSString* _Nonnull)topic payloadByBytes:(uint8_t* _Nonnull)payload length:(uint32_t)length qos:(int)qos;
+
+-(int32_t)mqttDeinit;
+
+-(void)setConnectStatusCallback:(void(^_Nonnull)(MqttConnectStatus_C ConnectStatus)) callback;
+
+-(void)setMessageArrivalCallback:(void(^_Nonnull)(NSString* _Nonnull topic, NSString * _Nonnull payload))callback;
+
+-(int32_t) mqttSub:(NSString* _Nonnull)topic qos:(int)qos;
+
+-(int32_t)mqttUnSub:(NSString* _Nonnull)topic;
+
++(instancetype _Nonnull ) shareInstance;
+
+@property (nonatomic, strong) NSString * _Nonnull productKey;
+@property (nonatomic, strong) NSString * _Nonnull deviceName;
+@property (nonatomic, strong) NSString * _Nonnull deviceSecret;
+
+@property (nonatomic, strong) NSString * _Nonnull clientId;
+@property (nonatomic, strong) NSString * _Nonnull userName;
+@property (nonatomic, strong) NSString * _Nonnull password;
+
+
+@property (nonatomic, strong) NSString * _Nonnull hostUrl;
+@property (nonatomic) int port;
+
+@property(nonatomic,weak) void(^ _Nullable gConnectStatusCallback)(MqttConnectStatus_C ConnectStatus);
+@property(nonatomic,weak) void(^ _Nullable gMessageArrivaldCallback)(NSString* _Nonnull topic, NSString * _Nonnull payload);
+
+@end
+
+#endif /* iot_mqtt_h */
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Convenience class which helps reading advertisement data
+public struct AdvertisementData {
+ /// Advertisement data dictionary that contains all information advertised by peripheral.
+ public let advertisementData: [String: Any]
+
+ /// Creates advertisement data based on CoreBluetooth's dictionary
+ /// - parameter advertisementData: Core Bluetooth's advertisement data
+ public init(advertisementData: [String: Any]) {
+ self.advertisementData = advertisementData
+ }
+
+ /// A string containing the local name of a peripheral.
+ public var localName: String? {
+ return advertisementData[CBAdvertisementDataLocalNameKey] as? String
+ }
+
+ /// A Data object containing the manufacturer data of a peripheral.
+ public var manufacturerData: Data? {
+ return advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
+ }
+
+ /// A dictionary containing service-specific advertisement data.
+ /// The keys are CBUUID objects, representing CBService UUIDs. The values are Data objects,
+ /// representing service-specific data.
+ public var serviceData: [CBUUID: Data]? {
+ return advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data]
+ }
+
+ /// An array of service UUIDs.
+ public var serviceUUIDs: [CBUUID]? {
+ return advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
+ }
+
+ /// An array of one or more CBUUID objects, representing CBService UUIDs that were found in the “overflow”
+ /// area of the advertisement data.
+ public var overflowServiceUUIDs: [CBUUID]? {
+ return advertisementData[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID]
+ }
+
+ /// A number (an instance of NSNumber) containing the transmit power of a peripheral.
+ /// This key and value are available if the broadcaster (peripheral)
+ /// provides its Tx power level in its advertising packet.
+ /// Using the RSSI value and the Tx power level, it is possible to calculate path loss.
+ public var txPowerLevel: NSNumber? {
+ return advertisementData[CBAdvertisementDataTxPowerLevelKey] as? NSNumber
+ }
+
+ /// A Boolean value that indicates whether the advertising event type is connectable.
+ /// The value for this key is an NSNumber object. You can use this value to determine whether
+ /// a peripheral is connectable at a particular moment.
+ public var isConnectable: Bool? {
+ return advertisementData[CBAdvertisementDataIsConnectable] as? Bool
+ }
+
+ /// An array of one or more CBUUID objects, representing CBService UUIDs.
+ public var solicitedServiceUUIDs: [CBUUID]? {
+ return advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID]
+ }
+}
--- /dev/null
+import Foundation
+
+extension Array where Element: Equatable {
+ @discardableResult mutating func remove(object: Element) -> Bool {
+ if let index = firstIndex(of: object) {
+ self.remove(at: index)
+ return true
+ }
+ return false
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Bluetooth error which can be emitted by RxBluetoothKit created observables.
+public enum BluetoothError: Error {
+ /// Emitted when the object that is the source of Observable was destroyed and event was emitted nevertheless.
+ /// To mitigate it dispose all of your subscriptions before deinitializing
+ /// object that created Observables that subscriptions are made to.
+ case destroyed
+ // Emitted when `CentralManager.scanForPeripherals` called and there is already ongoing scan
+ case scanInProgress
+ // Emitted when `PeripheralManager.startAdvertising` called and there is already ongoing advertisement
+ case advertisingInProgress
+ case advertisingStartFailed(Error)
+ // States
+ case bluetoothUnsupported
+ case bluetoothUnauthorized
+ case bluetoothPoweredOff
+ case bluetoothInUnknownState
+ case bluetoothResetting
+ // Peripheral
+ case peripheralIsAlreadyObservingConnection(Peripheral)
+ @available(*, deprecated, renamed: "BluetoothError.peripheralIsAlreadyObservingConnection")
+ case peripheralIsConnectingOrAlreadyConnected(Peripheral)
+ case peripheralConnectionFailed(Peripheral, Error?)
+ case peripheralDisconnected(Peripheral, Error?)
+ case peripheralRSSIReadFailed(Peripheral, Error?)
+ // Services
+ case servicesDiscoveryFailed(Peripheral, Error?)
+ case includedServicesDiscoveryFailed(Peripheral, Error?)
+ case addingServiceFailed(CBService, Error?)
+ // Characteristics
+ case characteristicsDiscoveryFailed(Service, Error?)
+ case characteristicWriteFailed(Characteristic, Error?)
+ case characteristicReadFailed(Characteristic, Error?)
+ case characteristicNotifyChangeFailed(Characteristic, Error?)
+ case characteristicSetNotifyValueFailed(Characteristic, Error?)
+ // Descriptors
+ case descriptorsDiscoveryFailed(Characteristic, Error?)
+ case descriptorWriteFailed(Descriptor, Error?)
+ case descriptorReadFailed(Descriptor, Error?)
+ // L2CAP
+ case openingL2CAPChannelFailed(Peripheral, Error?)
+ case publishingL2CAPChannelFailed(CBL2CAPPSM, Error?)
+ // Unknown
+ case unknownWriteType
+}
+
+extension BluetoothError: CustomStringConvertible {
+
+ /// Human readable description of bluetooth error
+ public var description: String {
+ switch self {
+ case .destroyed:
+ return """
+ The object that is the source of this Observable was destroyed.
+ It's programmer's error, please check documentation of error for more details
+ """
+ case .scanInProgress:
+ return """
+ Tried to scan for peripheral when there is already ongoing scan.
+ You can have only 1 ongoing scanning, please check documentation of CentralManager for more details
+ """
+ case .advertisingInProgress:
+ return """
+ Tried to advertise when there is already advertising ongoing.
+ You can have only 1 ongoing advertising, please check documentation of PeripheralManager for more details
+ """
+ case let .advertisingStartFailed(err):
+ return "Start advertising error occured: \(err.localizedDescription)"
+ case .bluetoothUnsupported:
+ return "Bluetooth is unsupported"
+ case .bluetoothUnauthorized:
+ return "Bluetooth is unauthorized"
+ case .bluetoothPoweredOff:
+ return "Bluetooth is powered off"
+ case .bluetoothInUnknownState:
+ return "Bluetooth is in unknown state"
+ case .bluetoothResetting:
+ return "Bluetooth is resetting"
+ // Peripheral
+ case .peripheralIsAlreadyObservingConnection, .peripheralIsConnectingOrAlreadyConnected:
+ return """
+ Peripheral connection is already being observed.
+ You cannot try to establishConnection to peripheral when you have ongoing
+ connection (previously establishConnection subscription was not disposed).
+ """
+ case let .peripheralConnectionFailed(_, err):
+ return "Connection error has occured: \(err?.localizedDescription ?? "-")"
+ case let .peripheralDisconnected(_, err):
+ return "Connection error has occured: \(err?.localizedDescription ?? "-")"
+ case let .peripheralRSSIReadFailed(_, err):
+ return "RSSI read failed : \(err?.localizedDescription ?? "-")"
+ // Services
+ case let .servicesDiscoveryFailed(_, err):
+ return "Services discovery error has occured: \(err?.localizedDescription ?? "-")"
+ case let .includedServicesDiscoveryFailed(_, err):
+ return "Included services discovery error has occured: \(err?.localizedDescription ?? "-")"
+ case let .addingServiceFailed(_, err):
+ return "Adding PeripheralManager service error has occured: \(err?.localizedDescription ?? "-")"
+ // Characteristics
+ case let .characteristicsDiscoveryFailed(_, err):
+ return "Characteristics discovery error has occured: \(err?.localizedDescription ?? "-")"
+ case let .characteristicWriteFailed(_, err):
+ return "Characteristic write error has occured: \(err?.localizedDescription ?? "-")"
+ case let .characteristicReadFailed(_, err):
+ return "Characteristic read error has occured: \(err?.localizedDescription ?? "-")"
+ case let .characteristicNotifyChangeFailed(_, err):
+ return "Characteristic notify change error has occured: \(err?.localizedDescription ?? "-")"
+ case let .characteristicSetNotifyValueFailed(_, err):
+ return "Characteristic isNotyfing value change error has occured: \(err?.localizedDescription ?? "-")"
+ // Descriptors
+ case let .descriptorsDiscoveryFailed(_, err):
+ return "Descriptor discovery error has occured: \(err?.localizedDescription ?? "-")"
+ case let .descriptorWriteFailed(_, err):
+ return "Descriptor write error has occured: \(err?.localizedDescription ?? "-")"
+ case let .descriptorReadFailed(_, err):
+ return "Descriptor read error has occured: \(err?.localizedDescription ?? "-")"
+ case let .openingL2CAPChannelFailed(_, err):
+ return "Opening L2CAP channel error has occured: \(err?.localizedDescription ?? "-")"
+ case let .publishingL2CAPChannelFailed(_, err):
+ return "Publishing L2CAP channel error has occured: \(err?.localizedDescription ?? "-")"
+ // Unknown
+ case .unknownWriteType:
+ return "Unknown write type"
+ }
+ }
+}
+
+extension BluetoothError {
+ init?(state: BluetoothState) {
+ switch state {
+ case .unsupported:
+ self = .bluetoothUnsupported
+ case .unauthorized:
+ self = .bluetoothUnauthorized
+ case .poweredOff:
+ self = .bluetoothPoweredOff
+ case .unknown:
+ self = .bluetoothInUnknownState
+ case .resetting:
+ self = .bluetoothResetting
+ default:
+ return nil
+ }
+ }
+}
+
+extension BluetoothError: Equatable {}
+
+// swiftlint:disable cyclomatic_complexity
+
+public func == (lhs: BluetoothError, rhs: BluetoothError) -> Bool {
+ switch (lhs, rhs) {
+ case (.scanInProgress, .scanInProgress): return true
+ case (.advertisingInProgress, .advertisingInProgress): return true
+ case (.advertisingStartFailed, .advertisingStartFailed): return true
+ // States
+ case (.bluetoothUnsupported, .bluetoothUnsupported): return true
+ case (.bluetoothUnauthorized, .bluetoothUnauthorized): return true
+ case (.bluetoothPoweredOff, .bluetoothPoweredOff): return true
+ case (.bluetoothInUnknownState, .bluetoothInUnknownState): return true
+ case (.bluetoothResetting, .bluetoothResetting): return true
+ // Services
+ case let (.servicesDiscoveryFailed(l, _), .servicesDiscoveryFailed(r, _)): return l == r
+ case let (.includedServicesDiscoveryFailed(l, _), .includedServicesDiscoveryFailed(r, _)): return l == r
+ case let (.addingServiceFailed(l, _), .addingServiceFailed(r, _)): return l == r
+ // Peripherals
+ case let (.peripheralIsAlreadyObservingConnection(l), .peripheralIsAlreadyObservingConnection(r)): return l == r
+ case let (.peripheralIsConnectingOrAlreadyConnected(l), .peripheralIsConnectingOrAlreadyConnected(r)): return l == r
+ case let (.peripheralIsAlreadyObservingConnection(l), .peripheralIsConnectingOrAlreadyConnected(r)): return l == r
+ case let (.peripheralIsConnectingOrAlreadyConnected(l), .peripheralIsAlreadyObservingConnection(r)): return l == r
+ case let (.peripheralConnectionFailed(l, _), .peripheralConnectionFailed(r, _)): return l == r
+ case let (.peripheralDisconnected(l, _), .peripheralDisconnected(r, _)): return l == r
+ case let (.peripheralRSSIReadFailed(l, _), .peripheralRSSIReadFailed(r, _)): return l == r
+ // Characteristics
+ case let (.characteristicsDiscoveryFailed(l, _), .characteristicsDiscoveryFailed(r, _)): return l == r
+ case let (.characteristicWriteFailed(l, _), .characteristicWriteFailed(r, _)): return l == r
+ case let (.characteristicReadFailed(l, _), .characteristicReadFailed(r, _)): return l == r
+ case let (.characteristicNotifyChangeFailed(l, _), .characteristicNotifyChangeFailed(r, _)): return l == r
+ case let (.characteristicSetNotifyValueFailed(l, _), .characteristicSetNotifyValueFailed(r, _)): return l == r
+ // Descriptors
+ case let (.descriptorsDiscoveryFailed(l, _), .descriptorsDiscoveryFailed(r, _)): return l == r
+ case let (.descriptorWriteFailed(l, _), .descriptorWriteFailed(r, _)): return l == r
+ case let (.descriptorReadFailed(l, _), .descriptorReadFailed(r, _)): return l == r
+ // L2CAP
+ case let (.openingL2CAPChannelFailed(l, _), .openingL2CAPChannelFailed(r, _)): return l == r
+ case let (.publishingL2CAPChannelFailed(l, _), .publishingL2CAPChannelFailed(r, _)): return l == r
+ // Unknown
+ case (.unknownWriteType, .unknownWriteType): return true
+ default: return false
+ }
+}
+
+// swiftlint:enable cyclomatic_complexity
--- /dev/null
+import Foundation
+
+/// Type describing bluetooth state, equivalent to
+/// [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
+public enum BluetoothState: Int {
+ case unknown
+ case resetting
+ case unsupported
+ case unauthorized
+ case poweredOff
+ case poweredOn
+}
--- /dev/null
+import Foundation
+
+class WeakBox<T: AnyObject>: CustomDebugStringConvertible {
+ weak var value: T?
+ init() {}
+ init(value: T) {
+ self.value = value
+ }
+}
+
+extension WeakBox {
+ var debugDescription: String {
+ return "WeakBox(\(String(describing: value)))"
+ }
+}
+
+/// `ThreadSafeBox` is a helper class that allows use of resource (value) in a thread safe manner.
+/// All read and write calls are wrapped in concurrent `DispatchQueue` which protects writing to
+/// resource from more than 1 thread at a time.
+class ThreadSafeBox<T>: CustomDebugStringConvertible {
+ private let queue = DispatchQueue(label: "com.polidea.RxBluetoothKit.ThreadSafeBox")
+ fileprivate var value: T
+ init(value: T) {
+ self.value = value
+ }
+
+ func read<Result: Any>(_ block: (T) -> Result) -> Result {
+ var result: Result! = nil
+ queue.sync {
+ result = block(value)
+ }
+ return result
+ }
+
+ func write(_ block: @escaping (inout T) -> Void) {
+ queue.async {
+ block(&self.value)
+ }
+ }
+
+ func writeSync(_ block: @escaping (inout T) -> Void) {
+ queue.sync {
+ block(&self.value)
+ }
+ }
+
+ @discardableResult func compareAndSet(compare: (T) -> Bool, set: @escaping (inout T) -> Void) -> Bool {
+ var result: Bool = false
+ queue.sync {
+ result = compare(value)
+ if result {
+ set(&self.value)
+ }
+ }
+ return result
+ }
+}
+
+extension ThreadSafeBox {
+ var debugDescription: String {
+ return "ThreadSafeBox(\(String(describing: value)))"
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+extension CBCentral {
+ /// There is still no identifier property for macOS, that's why we need to retrieve it by value method
+ var uuidIdentifier: UUID {
+ return value(forKey: "identifier") as! NSUUID as UUID
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+class CBCentralManagerDelegateWrapper: NSObject, CBCentralManagerDelegate {
+
+ let didUpdateState = PublishSubject<BluetoothState>()
+ let willRestoreState = ReplaySubject<[String: Any]>.create(bufferSize: 1)
+ let didDiscoverPeripheral = PublishSubject<(CBPeripheral, [String: Any], NSNumber)>()
+ let didConnectPeripheral = PublishSubject<CBPeripheral>()
+ let didFailToConnectPeripheral = PublishSubject<(CBPeripheral, Error?)>()
+ let didDisconnectPeripheral = PublishSubject<(CBPeripheral, Error?)>()
+ let didUpdateANCSAuthorizationForPeripheral = PublishSubject<(CBPeripheral)>()
+
+ func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ guard let bleState = BluetoothState(rawValue: central.state.rawValue) else { return }
+ RxBluetoothKitLog.d("\(central.logDescription) didUpdateState(state: \(bleState.logDescription))")
+ didUpdateState.onNext(bleState)
+ }
+
+ func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) {
+ RxBluetoothKitLog.d("\(central.logDescription) willRestoreState(restoredState: \(dict))")
+ willRestoreState.onNext(dict)
+ }
+
+ func centralManager(_ central: CBCentralManager,
+ didDiscover peripheral: CBPeripheral,
+ advertisementData: [String: Any],
+ rssi: NSNumber) {
+ RxBluetoothKitLog.d("""
+ \(central.logDescription) didDiscover(peripheral: \(peripheral.logDescription),
+ rssi: \(rssi))
+ """)
+ didDiscoverPeripheral.onNext((peripheral, advertisementData, rssi))
+ }
+
+ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+ RxBluetoothKitLog.d("""
+ \(central.logDescription) didConnect(to: \(peripheral.logDescription))
+ """)
+ didConnectPeripheral.onNext(peripheral)
+ }
+
+ func centralManager(_ central: CBCentralManager,
+ didFailToConnect peripheral: CBPeripheral,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(central.logDescription) didFailToConnect(to: \(peripheral.logDescription),
+ error: \(String(describing: error)))
+ """)
+ didFailToConnectPeripheral.onNext((peripheral, error))
+ }
+
+ func centralManager(_ central: CBCentralManager,
+ didDisconnectPeripheral peripheral: CBPeripheral,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(central.logDescription) didDisconnect(from: \(peripheral.logDescription),
+ error: \(String(describing: error)))
+ """)
+ didDisconnectPeripheral.onNext((peripheral, error))
+ }
+
+ #if !os(macOS)
+ @available(iOS 13.0, watchOS 6.0, tvOS 13.0, *)
+ func centralManager(_ central: CBCentralManager,
+ didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
+ RxBluetoothKitLog.d("""
+ \(central.logDescription) didUpdateANCSAuthorizationFor
+ (peripheral: \(peripheral.logDescription)
+ """)
+ didUpdateANCSAuthorizationForPeripheral.onNext(peripheral)
+ }
+ #endif
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+extension CBPeripheral {
+ /// There is still no identifier property for macOS, that's why we need to retrieve it by value method
+ var uuidIdentifier: UUID {
+ return value(forKey: "identifier") as! NSUUID as UUID
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+class CBPeripheralDelegateWrapper: NSObject, CBPeripheralDelegate {
+
+ let peripheralDidUpdateName = PublishSubject<String?>()
+ let peripheralDidModifyServices = PublishSubject<([CBService])>()
+ let peripheralDidReadRSSI = PublishSubject<(Int, Error?)>()
+ let peripheralDidDiscoverServices = PublishSubject<([CBService]?, Error?)>()
+ let peripheralDidDiscoverIncludedServicesForService = PublishSubject<(CBService, Error?)>()
+ let peripheralDidDiscoverCharacteristicsForService = PublishSubject<(CBService, Error?)>()
+ let peripheralDidUpdateValueForCharacteristic = PublishSubject<(CBCharacteristic, Error?)>()
+ let peripheralDidWriteValueForCharacteristic = PublishSubject<(CBCharacteristic, Error?)>()
+ let peripheralDidUpdateNotificationStateForCharacteristic =
+ PublishSubject<(CBCharacteristic, Error?)>()
+ let peripheralDidDiscoverDescriptorsForCharacteristic =
+ PublishSubject<(CBCharacteristic, Error?)>()
+ let peripheralDidUpdateValueForDescriptor = PublishSubject<(CBDescriptor, Error?)>()
+ let peripheralDidWriteValueForDescriptor = PublishSubject<(CBDescriptor, Error?)>()
+ let peripheralIsReadyToSendWriteWithoutResponse = PublishSubject<Void>()
+ let peripheralDidOpenL2CAPChannel = PublishSubject<(Any?, Error?)>()
+
+ func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didUpdateName(name: \(String(describing: peripheral.name)))
+ """)
+ peripheralDidUpdateName.onNext(peripheral.name)
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didModifyServices(services:
+ [\(invalidatedServices.logDescription))]
+ """)
+ peripheralDidModifyServices.onNext(invalidatedServices)
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didReadRSSI rssi: NSNumber, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didReadRSSI(rssi: \(rssi),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidReadRSSI.onNext((rssi.intValue, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didDiscoverServices(services
+ : \(String(describing: peripheral.services?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidDiscoverServices.onNext((peripheral.services, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didDiscoverIncludedServicesFor service: CBService,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didDiscoverIncludedServices(for:
+ \(service.logDescription), includedServices:
+ \(String(describing: service.includedServices?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidDiscoverIncludedServicesForService.onNext((service, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didDiscoverCharacteristicsFor service: CBService,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didDiscoverCharacteristicsFor(for:
+ \(service.logDescription), characteristics:
+ \(String(describing: service.characteristics?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidDiscoverCharacteristicsForService.onNext((service, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didUpdateValueFor characteristic: CBCharacteristic,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didUpdateValueFor(for:\(characteristic.logDescription),
+ value: \(String(describing: characteristic.value?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidUpdateValueForCharacteristic
+ .onNext((characteristic, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didWriteValueFor characteristic: CBCharacteristic,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didWriteValueFor(for:\(characteristic.logDescription),
+ value: \(String(describing: characteristic.value?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidWriteValueForCharacteristic
+ .onNext((characteristic, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didUpdateNotificationStateFor characteristic: CBCharacteristic,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didUpdateNotificationStateFor(
+ for:\(characteristic.logDescription), isNotifying: \(characteristic.isNotifying),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidUpdateNotificationStateForCharacteristic
+ .onNext((characteristic, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didDiscoverDescriptorsFor characteristic: CBCharacteristic,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didDiscoverDescriptorsFor
+ (for:\(characteristic.logDescription), descriptors:
+ \(String(describing: characteristic.descriptors?.logDescription)),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidDiscoverDescriptorsForCharacteristic
+ .onNext((characteristic, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didUpdateValueFor descriptor: CBDescriptor,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didUpdateValueFor(for:\(descriptor.logDescription),
+ value: \(String(describing: descriptor.value)), error: \(String(describing: error)))
+ """)
+ peripheralDidUpdateValueForDescriptor.onNext((descriptor, error))
+ }
+
+ func peripheral(_ peripheral: CBPeripheral,
+ didWriteValueFor descriptor: CBDescriptor,
+ error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didWriteValueFor(for:\(descriptor.logDescription),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidWriteValueForDescriptor.onNext((descriptor, error))
+ }
+
+ func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) peripheralIsReady(toSendWriteWithoutResponse)
+ """)
+ peripheralIsReadyToSendWriteWithoutResponse.onNext(())
+ }
+
+ @available(OSX 10.13, iOS 11, *)
+ func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didOpenL2CAPChannel(for:\(peripheral.logDescription),
+ error: \(String(describing: error)))
+ """)
+ peripheralDidOpenL2CAPChannel.onNext((channel, error))
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+class CBPeripheralManagerDelegateWrapper: NSObject, CBPeripheralManagerDelegate {
+
+ let didUpdateState = PublishSubject<BluetoothState>()
+ let isReady = PublishSubject<Void>()
+ let didStartAdvertising = PublishSubject<Error?>()
+ let didReceiveRead = PublishSubject<CBATTRequest>()
+ let willRestoreState = ReplaySubject<[String: Any]>.create(bufferSize: 1)
+ let didAddService = PublishSubject<(CBService, Error?)>()
+ let didReceiveWrite = PublishSubject<[CBATTRequest]>()
+ let didSubscribeTo = PublishSubject<(CBCentral, CBCharacteristic)>()
+ let didUnsubscribeFrom = PublishSubject<(CBCentral, CBCharacteristic)>()
+ let didPublishL2CAPChannel = PublishSubject<(CBL2CAPPSM, Error?)>()
+ let didUnpublishL2CAPChannel = PublishSubject<(CBL2CAPPSM, Error?)>()
+ private var _didOpenChannel: Any?
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)
+ var didOpenChannel: PublishSubject<(CBL2CAPChannel?, Error?)> {
+ if _didOpenChannel == nil {
+ _didOpenChannel = PublishSubject<(CBL2CAPChannel?, Error?)>()
+ }
+ return _didOpenChannel as! PublishSubject<(CBL2CAPChannel?, Error?)>
+ }
+
+ func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
+ guard let bleState = BluetoothState(rawValue: peripheral.state.rawValue) else { return }
+ RxBluetoothKitLog.d("\(peripheral.logDescription) didUpdateState(state: \(bleState.logDescription))")
+ didUpdateState.onNext(bleState)
+ }
+
+ func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
+ RxBluetoothKitLog.d("\(peripheral.logDescription) isReady()")
+ isReady.onNext(())
+ }
+
+ func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
+ RxBluetoothKitLog.d("\(peripheral.logDescription) didStartAdvertising(error: \(String(describing: error)))")
+ didStartAdvertising.onNext(error)
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
+ RxBluetoothKitLog.d("\(peripheral.logDescription) didReceiveRead(request: \(request.logDescription))")
+ didReceiveRead.onNext(request)
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, willRestoreState dict: [String: Any]) {
+ RxBluetoothKitLog.d("\(peripheral.logDescription) willRestoreState(dict: \(dict))")
+ willRestoreState.onNext(dict)
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription)
+ didAdd(service: \(service.logDescription), error: \(String(describing: error))
+ """)
+ didAddService.onNext((service, error))
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
+ let requestsLog = requests.reduce("[", { $0 + $1.logDescription + "," }).dropLast().appending("]")
+ RxBluetoothKitLog.d("\(peripheral.logDescription) didReceiveWrite(requests: \(requestsLog))")
+ didReceiveWrite.onNext(requests)
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral,
+ didSubscribeTo characteristic: CBCharacteristic) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription)
+ didSubscribeTo(central: \(central.logDescription), characteristic: \(characteristic.logDescription))
+ """)
+ didSubscribeTo.onNext((central, characteristic))
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager,
+ central: CBCentral,
+ didUnsubscribeFrom characteristic: CBCharacteristic) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription)
+ didUnsubscribeFrom(central: \(central.logDescription), characteristic: \(characteristic.logDescription))
+ """)
+ didUnsubscribeFrom.onNext((central, characteristic))
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, didPublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didPublishL2CAPChannel(PSM: \(PSM), error: \(String(describing: error))
+ """)
+ didPublishL2CAPChannel.onNext((PSM, error))
+ }
+
+ func peripheralManager(_ peripheral: CBPeripheralManager, didUnpublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription) didUnpublishL2CAPChannel(PSM: \(PSM), error: \(String(describing: error))
+ """)
+ didUnpublishL2CAPChannel.onNext((PSM, error))
+ }
+
+ @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)
+ func peripheralManager(_ peripheral: CBPeripheralManager, didOpen channel: CBL2CAPChannel?, error: Error?) {
+ RxBluetoothKitLog.d("""
+ \(peripheral.logDescription)
+ didOpen(channel: \(channel?.logDescription ?? "nil"), error: \(String(describing: error))
+ """)
+ didOpenChannel.onNext((channel, error))
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+@available(*, deprecated, renamed: "OnWillRestoreCentralManagerState")
+public typealias OnWillRestoreState = (RestoredState) -> Void
+/// Closure that receives `RestoredState` as a parameter
+public typealias OnWillRestoreCentralManagerState = (CentralManagerRestoredState) -> Void
+
+extension CentralManager {
+
+ // MARK: State restoration
+
+ /// Deprecated, use CentralManager.init(queue:options:onWillRestoreCentralManagerState:) instead
+ @available(*, deprecated, renamed: "CentralManager.init(queue:options:onWillRestoreCentralManagerState:)")
+ public convenience init(queue: DispatchQueue = .main,
+ options: [String: AnyObject]? = nil,
+ cbCentralManager: CBCentralManager? = nil,
+ onWillRestoreState: OnWillRestoreState? = nil) {
+ self.init(queue: queue, options: options, cbCentralManager: cbCentralManager)
+ if let onWillRestoreState = onWillRestoreState {
+ listenOnWillRestoreState(onWillRestoreState)
+ }
+ }
+
+ /// Creates new `CentralManager` instance, which supports bluetooth state restoration.
+ /// - warning: If you pass background queue to the method make sure to observe results on main thread
+ /// for UI related code.
+ /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used
+ /// and all operations and events are executed and received on main thread.
+ /// - parameter options: An optional dictionary containing initialization options for a central manager.
+ /// For more info about it please refer to [Central Manager initialization options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/index.html)
+ /// - parameter cbCentralManager: Optional instance of `CBCentralManager` to be used as a `manager`.
+ /// If you skip this parameter, there will be created an instance of `CBCentralManager` using given queue and options.
+ /// - parameter onWillRestoreCentralManagerState: Closure called when state has been restored.
+ ///
+ /// - seealso: `OnWillRestoreCentralManagerState`
+ public convenience init(queue: DispatchQueue = .main,
+ options: [String: AnyObject]? = nil,
+ cbCentralManager: CBCentralManager? = nil,
+ onWillRestoreCentralManagerState: OnWillRestoreCentralManagerState? = nil) {
+ self.init(queue: queue, options: options, cbCentralManager: cbCentralManager)
+ if let onWillRestoreCentralManagerState = onWillRestoreCentralManagerState {
+ listenOnWillRestoreState(onWillRestoreCentralManagerState)
+ }
+ }
+
+ /// Creates new `CentralManager`
+ /// - parameter centralManager: Central instance which is used to perform all of the necessary operations
+ /// - parameter delegateWrapper: Wrapper on CoreBluetooth's central manager callbacks.
+ /// - parameter peripheralProvider: Provider for providing peripherals and peripheral wrappers
+ /// - parameter connector: Connector instance which is used for establishing connection with peripherals
+ /// - parameter onWillRestoreState: Closure called when state has been restored.
+ convenience init(
+ centralManager: CBCentralManager,
+ delegateWrapper: CBCentralManagerDelegateWrapper,
+ peripheralProvider: PeripheralProvider,
+ connector: Connector,
+ onWillRestoreCentralManagerState: @escaping OnWillRestoreCentralManagerState
+ ) {
+ self.init(
+ centralManager: centralManager,
+ delegateWrapper: delegateWrapper,
+ peripheralProvider: peripheralProvider,
+ connector: connector
+ )
+ listenOnWillRestoreState(onWillRestoreCentralManagerState)
+ }
+
+ @available(*, deprecated, message: "listenOnWillRestoreState(:OnWillRestoreCentralManagerState) instead")
+ func listenOnWillRestoreState(_ handler: @escaping OnWillRestoreState) {
+ _ = restoreStateObservable
+ .map { RestoredState(centralManagerRestoredState: $0) }
+ .subscribe(onNext: { handler($0) })
+ }
+
+ /// Emits `RestoredState` instance, when state of `CentralManager` has been restored,
+ /// Should only be called once in the lifetime of the app
+ /// - returns: Observable which emits next events state has been restored
+ func listenOnWillRestoreState(_ handler: @escaping OnWillRestoreCentralManagerState) {
+ _ = restoreStateObservable
+ .subscribe(onNext: { handler($0) })
+ }
+
+ var restoreStateObservable: Observable<CentralManagerRestoredState> {
+ return delegateWrapper
+ .willRestoreState
+ .take(1)
+ .flatMap { [weak self] dict -> Observable<CentralManagerRestoredState> in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return .just(CentralManagerRestoredState(restoredStateDictionary: dict, centralManager: strongSelf))
+ }
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+/// Error received when device disconnection event occurs
+public typealias DisconnectionReason = Error
+
+/// CentralManager is a class implementing ReactiveX API which wraps all Core Bluetooth Manager's functions allowing to
+/// discover, connect to remote peripheral devices and more.
+/// You can start using this class by discovering available services of nearby peripherals. Before calling any
+/// public `CentralManager`'s functions you should make sure that Bluetooth is turned on and powered on.
+/// It can be done by calling and observing returned value of `observeStateWithInitialValue()` and then
+/// chaining it with `scanForPeripherals(_:options:)`:
+/// ```
+/// let disposable = centralManager.observeStateWithInitialValue()
+/// .filter { $0 == .poweredOn }
+/// .take(1)
+/// .flatMap { centralManager.scanForPeripherals(nil) }
+/// ```
+/// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
+/// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other operations.
+/// You can also simply stop scanning with just disposing it:
+/// ```
+/// disposable.dispose()
+/// ```
+/// - seealso: `Peripheral`
+public class CentralManager: ManagerType {
+
+ /// Implementation of CBCentralManager
+ public let manager: CBCentralManager
+
+ @available(*, deprecated, renamed: "CentralManager.manager")
+ public var centralManager: CBCentralManager { return manager }
+
+ let peripheralProvider: PeripheralProvider
+
+ let delegateWrapper: CBCentralManagerDelegateWrapper
+
+ /// Lock which should be used before accessing any internal structures
+ private let lock = NSLock()
+
+ /// Ongoing scan disposable
+ private var scanDisposable: Disposable?
+
+ /// Connector instance is used for establishing connection with peripherals
+ private let connector: Connector
+
+ // MARK: Initialization
+
+ /// Creates new `CentralManager`
+ /// - parameter centralManager: Central instance which is used to perform all of the necessary operations
+ /// - parameter delegateWrapper: Wrapper on CoreBluetooth's central manager callbacks.
+ /// - parameter peripheralProvider: Provider for providing peripherals and peripheral wrappers
+ /// - parameter connector: Connector instance which is used for establishing connection with peripherals.
+ init(
+ centralManager: CBCentralManager,
+ delegateWrapper: CBCentralManagerDelegateWrapper,
+ peripheralProvider: PeripheralProvider,
+ connector: Connector
+ ) {
+ self.manager = centralManager
+ self.delegateWrapper = delegateWrapper
+ self.peripheralProvider = peripheralProvider
+ self.connector = connector
+ centralManager.delegate = delegateWrapper
+ }
+
+ /// Creates new `CentralManager` instance. By default all operations and events are executed and received on main thread.
+ /// - warning: If you pass background queue to the method make sure to observe results on main thread for UI related code.
+ /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used.
+ /// - parameter options: An optional dictionary containing initialization options for a central manager.
+ /// For more info about it please refer to [Central Manager initialization options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/index.html)
+ /// - parameter cbCentralManager: Optional instance of `CBCentralManager` to be used as a `manager`. If you
+ /// skip this parameter, there will be created an instance of `CBCentralManager` using given queue and options.
+ public convenience init(queue: DispatchQueue = .main,
+ options: [String: AnyObject]? = nil,
+ cbCentralManager: CBCentralManager? = nil) {
+ let delegateWrapper = CBCentralManagerDelegateWrapper()
+ let centralManager = cbCentralManager ??
+ CBCentralManager(delegate: delegateWrapper, queue: queue, options: options)
+ self.init(
+ centralManager: centralManager,
+ delegateWrapper: delegateWrapper,
+ peripheralProvider: PeripheralProvider(),
+ connector: Connector(centralManager: centralManager, delegateWrapper: delegateWrapper)
+ )
+ }
+
+ /// Attaches RxBluetoothKit delegate to CBCentralManager.
+ /// This method is useful in cases when delegate of CBCentralManager was reassigned outside of
+ /// RxBluetoothKit library (e.g. CBCentralManager was used in some other library or used in non-reactive way)
+ public func attach() {
+ manager.delegate = delegateWrapper
+ }
+
+ // MARK: State
+
+ public var state: BluetoothState {
+ return BluetoothState(rawValue: manager.state.rawValue) ?? .unknown
+ }
+
+ public func observeState() -> Observable<BluetoothState> {
+ return self.delegateWrapper.didUpdateState.asObservable()
+ }
+
+ public func observeStateWithInitialValue() -> Observable<BluetoothState> {
+ return Observable.deferred { [weak self] in
+ guard let self = self else {
+ RxBluetoothKitLog.w("observeState - CentralManager deallocated")
+ return .never()
+ }
+
+ return self.delegateWrapper.didUpdateState.asObservable()
+ .startWith(self.state)
+ }
+ }
+
+ // MARK: Scanning
+
+ /// Value indicating if manager is currently scanning.
+ public var isScanInProgress: Bool {
+ lock.lock(); defer { lock.unlock() }
+ return scanDisposable != nil
+ }
+
+ /// Scans for `Peripheral`s after subscription to returned observable. First parameter `serviceUUIDs` is
+ /// an array of `Service` UUIDs which needs to be implemented by a peripheral to be discovered. If user don't want to
+ /// filter any peripherals, `nil` can be used instead. Additionally dictionary of
+ /// [CBCentralManager specific options](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/#//apple_ref/doc/constant_group/Peripheral_Scanning_Options)
+ /// can be passed to allow further customisation.
+ /// Scans by default are infinite streams of `ScannedPeripheral` structures which need to be stopped by the user. For
+ /// example this can be done by limiting scanning to certain number of peripherals or time:
+ /// ```
+ /// centralManager.scanForPeripherals(withServices: nil)
+ /// .timeout(3.0, timeoutScheduler)
+ /// .take(2)
+ /// ```
+ ///
+ /// There can be only one ongoing scanning. It will return `BluetoothError.scanInProgress` error if
+ /// this method will be called when there is already ongoing scan.
+ /// As a result you will receive `ScannedPeripheral` which contains `Peripheral` object, `AdvertisementData` and
+ /// peripheral's RSSI registered during discovery. You can then `establishConnection(_:options:)` and do other
+ /// operations.
+ ///
+ /// - seealso: `Peripheral`
+ ///
+ /// - parameter serviceUUIDs: Services of peripherals to search for. Nil value will accept all peripherals.
+ /// - parameter options: Optional scanning options.
+ /// - returns: Infinite stream of scanned peripherals.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.scanInProgress`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func scanForPeripherals(withServices serviceUUIDs: [CBUUID]?, options: [String: Any]? = nil)
+ -> Observable<ScannedPeripheral> {
+ let observable: Observable<ScannedPeripheral> = Observable.create { [weak self] observer in
+ guard let strongSelf = self else {
+ observer.onError(BluetoothError.destroyed)
+ return Disposables.create()
+ }
+ strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
+ if strongSelf.scanDisposable != nil {
+ observer.onError(BluetoothError.scanInProgress)
+ return Disposables.create()
+ }
+ strongSelf.scanDisposable = strongSelf.delegateWrapper.didDiscoverPeripheral
+ .flatMap { [weak self] (cbPeripheral, advertisment, rssi) -> Observable<ScannedPeripheral> in
+ guard let strongSelf = self else {
+ throw BluetoothError.destroyed
+ }
+ let peripheral = strongSelf.retrievePeripheral(for: cbPeripheral)
+ let advertismentData = AdvertisementData(advertisementData: advertisment)
+ return .just(ScannedPeripheral(peripheral: peripheral,
+ advertisementData: advertismentData, rssi: rssi))
+ }
+ .subscribe(observer)
+
+ strongSelf.manager.scanForPeripherals(withServices: serviceUUIDs, options: options)
+
+ return Disposables.create { [weak self] in
+ guard let strongSelf = self else { return }
+ // When disposed, stop scan and dispose scanning
+ if strongSelf.state == .poweredOn {
+ strongSelf.manager.stopScan()
+ }
+ do { strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
+ strongSelf.scanDisposable?.dispose()
+ strongSelf.scanDisposable = nil
+ }
+ }
+ }
+
+ return ensure(.poweredOn, observable: observable)
+ }
+
+ // MARK: Peripheral's Connection Management
+
+ /// Establishes connection with a given `Peripheral`.
+ /// When connection did succeded it sends event with `Peripheral` - from now on it is possible to call all other methods that require connection.
+ /// The connection is automatically disconnected when resulting Observable is unsubscribed.
+ /// On the other hand when the connection is interrupted or failed by the device or the system, the Observable will be unsubscribed as well
+ /// following `BluetoothError.peripheralConnectionFailed` or `BluetoothError.peripheralDisconnected` emission.
+ /// Additionally you can pass optional [dictionary](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCentralManager_Class/#//apple_ref/doc/constant_group/Peripheral_Connection_Options)
+ /// to customise the behaviour of connection.
+ ///
+ /// - parameter peripheral: The `Peripheral` to which `CentralManager` is attempting to establish connection.
+ /// - parameter options: Dictionary to customise the behaviour of connection.
+ /// - returns: `Observable` which emits next event after connection is established.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.peripheralIsAlreadyObservingConnection`
+ /// * `BluetoothError.peripheralConnectionFailed`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func establishConnection(_ peripheral: Peripheral, options: [String: Any]? = nil) -> Observable<Peripheral> {
+ let observable = connector.establishConnection(with: peripheral, options: options)
+ return ensure(.poweredOn, observable: observable)
+ }
+
+ // MARK: Retrieving Lists of Peripherals
+
+ /// Returns list of the `Peripheral`s which are currently connected to the `CentralManager` and contain
+ /// all of the specified `Service`'s UUIDs.
+ ///
+ /// - parameter serviceUUIDs: A list of `Service` UUIDs
+ /// - returns: Retrieved `Peripheral`s. They are in connected state and contain all of the
+ /// `Service`s with UUIDs specified in the `serviceUUIDs` parameter.
+ public func retrieveConnectedPeripherals(withServices serviceUUIDs: [CBUUID]) -> [Peripheral] {
+ return manager.retrieveConnectedPeripherals(withServices: serviceUUIDs)
+ .map { self.retrievePeripheral(for: $0) }
+ }
+
+ /// Returns list of `Peripheral`s by their identifiers which are known to `CentralManager`.
+ ///
+ /// - parameter identifiers: List of `Peripheral`'s identifiers which should be retrieved.
+ /// - returns: Retrieved `Peripheral`s.
+ public func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> [Peripheral] {
+ return manager.retrievePeripherals(withIdentifiers: identifiers)
+ .map { self.retrievePeripheral(for: $0) }
+ }
+
+ // MARK: Connection and disconnection observing
+
+ /// Emits `Peripheral` instance when it's connected.
+ ///
+ /// - parameter peripheral: Optional `Peripheral` which is observed for connection. When not specified it will observe fo any `Peripheral`.
+ /// - returns: Observable which emits next events when `peripheral` was connected.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeConnect(for peripheral: Peripheral? = nil) -> Observable<Peripheral> {
+ let observable = delegateWrapper.didConnectPeripheral
+ .filter { peripheral != nil ? ($0 == peripheral!.peripheral) : true }
+ .map { [weak self] (cbPeripheral: CBPeripheral) -> Peripheral in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return peripheral ?? strongSelf.retrievePeripheral(for: cbPeripheral)
+ }
+ return ensure(.poweredOn, observable: observable)
+ }
+
+ /// Emits `Peripheral` instance when it's disconnected.
+ /// - parameter peripheral: Optional `Peripheral` which is observed for disconnection. When not specified it will observe for any `Peripheral`.
+ /// - returns: Observable which emits next events when `Peripheral` instance was disconnected.
+ /// It provides optional error which may contain more information about the cause of the disconnection
+ /// if it wasn't the `cancelPeripheralConnection` call.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeDisconnect(for peripheral: Peripheral? = nil) -> Observable<(Peripheral, DisconnectionReason?)> {
+ let observable = delegateWrapper.didDisconnectPeripheral
+ .filter { peripheral != nil ? ($0.0 == peripheral!.peripheral) : true }
+ .map { [weak self] (cbPeripheral, error) -> (Peripheral, DisconnectionReason?) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ let peripheral = peripheral ?? strongSelf.retrievePeripheral(for: cbPeripheral)
+ return (peripheral, error)
+ }
+ return ensure(.poweredOn, observable: observable)
+ .catchError { error in
+ if error is BluetoothError, let peripheral = peripheral {
+ return .concat(.just((peripheral, error)), .error(error))
+ } else {
+ return .error(error)
+ }
+ }
+ }
+
+ // MARK: ANCS
+
+ /// Emits boolean values according to ancsAuthorized property on a CBPeripheral.
+ ///
+ /// - parameter peripheral: `Peripheral` which is observed for ancsAuthorized chances.
+ /// - returns: Observable which emits next events when `ancsAuthorized` property changes on a peripheral.
+ #if !os(macOS)
+ @available(iOS 13.0, watchOS 6.0, tvOS 13.0, *)
+ public func observeANCSAuthorized(for peripheral: Peripheral) -> Observable<Bool> {
+ let observable = delegateWrapper.didUpdateANCSAuthorizationForPeripheral
+ .asObservable()
+ .filter { $0 == peripheral.peripheral }
+ // ancsAuthorized is a Bool by default, but the testing framework
+ // will use Bool! instead. In order to support that we are converting
+ // to optional and unwrapping the value.
+ .map { ($0.ancsAuthorized as Bool?)! }
+
+ return ensure(.poweredOn, observable: observable)
+ }
+ #endif
+
+ // MARK: Internal functions
+
+ /// Ensure that specified `peripheral` is connected during subscription.
+ /// - parameter peripheral: `Peripheral` which should be connected during subscription.
+ /// - returns: Observable which emits error when `peripheral` is disconnected during subscription.
+ func ensurePeripheralIsConnected<T>(_ peripheral: Peripheral) -> Observable<T> {
+ return .deferred {
+ if !peripheral.isConnected {
+ throw BluetoothError.peripheralDisconnected(peripheral, nil)
+ }
+ return self.delegateWrapper.didDisconnectPeripheral
+ .filter { $0.0 == peripheral.peripheral }
+ .map { (_, error) -> T in
+ throw BluetoothError.peripheralDisconnected(peripheral, error)
+ }
+ }
+ }
+
+ func retrievePeripheral(for cbPeripheral: CBPeripheral) -> Peripheral {
+ return peripheralProvider.provide(for: cbPeripheral, centralManager: self)
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// It should be deleted when `RestoredState` will be deleted
+protocol CentralManagerRestoredStateType {
+ var restoredStateData: [String: Any] { get }
+ var centralManager: CentralManager { get }
+ var peripherals: [Peripheral] { get }
+ var scanOptions: [String: AnyObject]? { get }
+ var services: [Service] { get }
+}
+
+/// Convenience class which helps reading state of restored CentralManager.
+public struct CentralManagerRestoredState: CentralManagerRestoredStateType {
+
+ /// Restored state dictionary.
+ public let restoredStateData: [String: Any]
+
+ public unowned let centralManager: CentralManager
+ /// Creates restored state information based on CoreBluetooth's dictionary
+ /// - parameter restoredStateDictionary: Core Bluetooth's restored state data
+ /// - parameter centralManager: `CentralManager` instance of which state has been restored.
+ init(restoredStateDictionary: [String: Any], centralManager: CentralManager) {
+ restoredStateData = restoredStateDictionary
+ self.centralManager = centralManager
+ }
+
+ /// Array of `Peripheral` objects which have been restored.
+ /// These are peripherals that were connected to the central manager (or had a connection pending)
+ /// at the time the app was terminated by the system.
+ public var peripherals: [Peripheral] {
+ let objects = restoredStateData[CBCentralManagerRestoredStatePeripheralsKey] as? [AnyObject]
+ guard let arrayOfAnyObjects = objects else { return [] }
+
+ #if swift(>=4.1)
+ let cbPeripherals = arrayOfAnyObjects.compactMap { $0 as? CBPeripheral }
+ #else
+ let cbPeripherals = arrayOfAnyObjects.flatMap { $0 as? CBPeripheral }
+ #endif
+
+ return cbPeripherals.map { centralManager.retrievePeripheral(for: $0) }
+ }
+
+ /// Dictionary that contains all of the peripheral scan options that were being used
+ /// by the central manager at the time the app was terminated by the system.
+ public var scanOptions: [String: AnyObject]? {
+ return restoredStateData[CBCentralManagerRestoredStatePeripheralsKey] as? [String: AnyObject]
+ }
+
+ /// Array of `Service` objects which have been restored.
+ /// These are all the services the central manager was scanning for at the time the app
+ /// was terminated by the system.
+ public var services: [Service] {
+ let objects = restoredStateData[CBCentralManagerRestoredStateScanServicesKey] as? [AnyObject]
+ guard let arrayOfAnyObjects = objects else { return [] }
+
+ #if swift(>=4.1)
+ let cbServices = arrayOfAnyObjects.compactMap { $0 as? CBService }
+ #else
+ let cbServices = arrayOfAnyObjects.flatMap { $0 as? CBService }
+ #endif
+
+ return cbServices.compactMap {
+ guard let cbPeripheral = $0.peripheral else { return nil }
+ let peripheral = centralManager.retrievePeripheral(for: cbPeripheral)
+ return Service(peripheral: peripheral, service: $0)
+ }
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+/// Characteristic is a class implementing ReactiveX which wraps CoreBluetooth functions related to interaction with [CBCharacteristic](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCharacteristic_Class/)
+public class Characteristic {
+ /// Intance of CoreBluetooth characteristic class
+ public let characteristic: CBCharacteristic
+
+ /// Service which contains this characteristic
+ public let service: Service
+
+ /// Current value of characteristic. If value is not present - it's `nil`.
+ public var value: Data? {
+ return characteristic.value
+ }
+
+ /// The Bluetooth UUID of the `Characteristic` instance.
+ public var uuid: CBUUID {
+ return characteristic.uuid
+ }
+
+ /// Flag which is set to true if characteristic is currently notifying
+ public var isNotifying: Bool {
+ return characteristic.isNotifying
+ }
+
+ /// Properties of characteristic. For more info about this refer to [CBCharacteristicProperties](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBCharacteristic_Class/#//apple_ref/c/tdef/CBCharacteristicProperties)
+ public var properties: CBCharacteristicProperties {
+ return characteristic.properties
+ }
+
+ /// Value of this property is an array of `Descriptor` objects. They provide more detailed information about characteristics value.
+ public var descriptors: [Descriptor]? {
+ return characteristic.descriptors?.map { Descriptor(descriptor: $0, characteristic: self) }
+ }
+
+ init(characteristic: CBCharacteristic, service: Service) {
+ self.characteristic = characteristic
+ self.service = service
+ }
+
+ convenience init?(characteristic: CBCharacteristic, peripheral: Peripheral) {
+ guard let _service = characteristic.service else {
+ return nil
+ }
+ let service = Service(peripheral: peripheral, service: _service)
+ self.init(characteristic: characteristic, service: service)
+ }
+
+ /// Function that triggers descriptors discovery for characteristic.
+ /// - returns: `Single` that emits `next` with array of `Descriptor` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverDescriptors() -> Single<[Descriptor]> {
+ return service.peripheral.discoverDescriptors(for: self)
+ }
+
+ /// Function that allow to observe writes that happened for characteristic.
+ /// - Returns: `Observable` that emits `next` with `Characteristic` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite() -> Observable<Characteristic> {
+ return service.peripheral.observeWrite(for: self)
+ }
+
+ /// Function that allows to know the exact time, when isNotyfing value has changed on a characteristic.
+ ///
+ /// - returns: `Observable` emitting `Characteristic` when isNoytfing value has changed.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicSetNotifyValueFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeNotifyValue() -> Observable<Characteristic> {
+ return service.peripheral.observeNotifyValue(for: self)
+ }
+
+ /// Function that triggers write of data to characteristic. Write is called after subscribtion to `Observable` is made.
+ /// Behavior of this function strongly depends on [CBCharacteristicWriteType](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBPeripheral_Class/#//apple_ref/swift/enum/c:@E@CBCharacteristicWriteType), so be sure to check this out before usage of the method.
+ /// - parameter data: `Data` that'll be written to the `Characteristic`
+ /// - parameter type: Type of write operation. Possible values: `.withResponse`, `.withoutResponse`
+ /// - returns: `Single` whose emission depends on `CBCharacteristicWriteType` passed to the function call.
+ /// Behavior is following:
+ ///
+ /// - `withResponse` - `Observable` emits `next` with `Characteristic` instance write was confirmed without any errors.
+ /// If any problem has happened, errors are emitted.
+ /// - `withoutResponse` - `Observable` emits `next` with `Characteristic` instance once write was called.
+ /// Result of this call is not checked, so as a user you are not sure
+ /// if everything completed successfully. Errors are not emitted
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data, type: CBCharacteristicWriteType) -> Single<Characteristic> {
+ return service.peripheral.writeValue(data, for: self, type: type)
+ }
+
+ /// Function that allow to observe value updates for `Characteristic` instance.
+ /// - Returns: `Observable` that emits `Next` with `Characteristic` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate() -> Observable<Characteristic> {
+ return service.peripheral.observeValueUpdate(for: self)
+ }
+
+ /// Function that triggers read of current value of the `Characteristic` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Returns: `Single` which emits `next` with given characteristic when value is ready to read.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readValue() -> Single<Characteristic> {
+ return service.peripheral.readValue(for: self)
+ }
+
+ /// Setup characteristic notification in order to receive callbacks when given characteristic has been changed.
+ /// Returned observable will emit `Characteristic` on every notification change.
+ /// It is possible to setup more observables for the same characteristic and the lifecycle of the notification will be shared among them.
+ ///
+ /// Notification is automaticaly unregistered once this observable is unsubscribed
+ ///
+ /// - returns: `Observable` emitting `next` with `Characteristic` when given characteristic has been changed.
+ ///
+ /// This is **infinite** stream of values.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdateAndSetNotification() -> Observable<Characteristic> {
+ return service.peripheral.observeValueUpdateAndSetNotification(for: self)
+ }
+}
+
+extension Characteristic: Equatable {}
+extension Characteristic: UUIDIdentifiable {}
+
+/// Compare two characteristics. Characteristics are the same when their UUIDs are the same.
+///
+/// - parameter lhs: First characteristic to compare
+/// - parameter rhs: Second characteristic to compare
+/// - returns: True if both characteristics are the same.
+public func == (lhs: Characteristic, rhs: Characteristic) -> Bool {
+ return lhs.characteristic == rhs.characteristic
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+class CharacteristicNotificationManager {
+
+ private unowned let peripheral: CBPeripheral
+ private let delegateWrapper: CBPeripheralDelegateWrapper
+ private var uuidToActiveObservableMap: [CBUUID: Observable<Characteristic>] = [:]
+ private var uuidToActiveObservablesCountMap: [CBUUID: Int] = [:]
+ private let lock = NSLock()
+
+ init(peripheral: CBPeripheral, delegateWrapper: CBPeripheralDelegateWrapper) {
+ self.peripheral = peripheral
+ self.delegateWrapper = delegateWrapper
+ }
+
+ func observeValueUpdateAndSetNotification(for characteristic: Characteristic) -> Observable<Characteristic> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ strongSelf.lock.lock(); defer { strongSelf.lock.unlock()}
+
+ if let activeObservable = strongSelf.uuidToActiveObservableMap[characteristic.uuid] {
+ return activeObservable
+ }
+
+ let notificationObserable = strongSelf.createValueUpdateObservable(for: characteristic)
+ let observable = notificationObserable
+ .do(onSubscribed: { [weak self] in
+ guard let strongSelf = self else { return }
+ strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
+ let counter = strongSelf.uuidToActiveObservablesCountMap[characteristic.uuid] ?? 0
+ strongSelf.uuidToActiveObservablesCountMap[characteristic.uuid] = counter + 1
+ self?.setNotifyValue(true, for: characteristic)
+ }, onDispose: { [weak self] in
+ guard let strongSelf = self else { return }
+ strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
+ let counter = strongSelf.uuidToActiveObservablesCountMap[characteristic.uuid] ?? 1
+ strongSelf.uuidToActiveObservablesCountMap[characteristic.uuid] = counter - 1
+
+ if counter <= 1 {
+ strongSelf.uuidToActiveObservableMap.removeValue(forKey: characteristic.uuid)
+ strongSelf.setNotifyValue(false, for: characteristic)
+ }
+ })
+ .share()
+
+ strongSelf.uuidToActiveObservableMap[characteristic.uuid] = observable
+ return observable
+ }
+ }
+
+ private func createValueUpdateObservable(for characteristic: Characteristic) -> Observable<Characteristic> {
+ return delegateWrapper
+ .peripheralDidUpdateValueForCharacteristic
+ .filter { $0.0 == characteristic.characteristic }
+ .map { (_, error) -> Characteristic in
+ if let error = error {
+ throw BluetoothError.characteristicReadFailed(characteristic, error)
+ }
+ return characteristic
+ }
+ }
+
+ private func setNotifyValue(_ enabled: Bool, for characteristic: Characteristic) {
+ guard peripheral.state == .connected else {
+ RxBluetoothKitLog.w("\(String(describing: peripheral.logDescription)) is not connected." +
+ " Changing notification state for not connected peripheral is not possible.")
+ return
+ }
+ peripheral.setNotifyValue(enabled, for: characteristic.characteristic)
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+/// `Connector` is a class that is responsible for establishing connection with peripherals.
+class Connector {
+ let centralManager: CBCentralManager
+ let delegateWrapper: CBCentralManagerDelegateWrapper
+ let connectedBox: ThreadSafeBox<Set<UUID>> = ThreadSafeBox(value: [])
+ let disconnectingBox: ThreadSafeBox<Set<UUID>> = ThreadSafeBox(value: [])
+
+ init(
+ centralManager: CBCentralManager,
+ delegateWrapper: CBCentralManagerDelegateWrapper
+ ) {
+ self.centralManager = centralManager
+ self.delegateWrapper = delegateWrapper
+ }
+
+ /// Establishes connection with a given `Peripheral`.
+ /// For more information see `CentralManager.establishConnection(with:options:)`
+ func establishConnection(with peripheral: Peripheral, options: [String: Any]? = nil)
+ -> Observable<Peripheral> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else {
+ return Observable.error(BluetoothError.destroyed)
+ }
+
+ let connectionObservable = strongSelf.createConnectionObservable(for: peripheral, options: options)
+
+ let waitForDisconnectObservable = strongSelf.delegateWrapper.didDisconnectPeripheral
+ .filter { $0.0 == peripheral.peripheral }
+ .take(1)
+ .do(onNext: { [weak self] _ in
+ guard let strongSelf = self else { return }
+ strongSelf.disconnectingBox.write { $0.remove(peripheral.identifier) }
+ })
+ .map { _ in peripheral }
+ let isDisconnectingObservable: Observable<Peripheral> = Observable.create { observer in
+ var isDiconnecting = strongSelf.disconnectingBox.read { $0.contains(peripheral.identifier) }
+ let isDisconnected = peripheral.state == .disconnected
+ // it means that peripheral is already disconnected, but we didn't update disconnecting box
+ if isDiconnecting && isDisconnected {
+ strongSelf.disconnectingBox.write { $0.remove(peripheral.identifier) }
+ isDiconnecting = false
+ }
+ if !isDiconnecting {
+ observer.onNext(peripheral)
+ }
+ return Disposables.create()
+ }
+ return waitForDisconnectObservable.amb(isDisconnectingObservable)
+ .flatMap { _ in connectionObservable }
+ }
+ }
+
+ fileprivate func createConnectionObservable(
+ for peripheral: Peripheral,
+ options: [String: Any]? = nil
+ ) -> Observable<Peripheral> {
+ return Observable.create { [weak self] observer in
+ guard let strongSelf = self else {
+ observer.onError(BluetoothError.destroyed)
+ return Disposables.create()
+ }
+
+ let connectingStarted = strongSelf.connectedBox.compareAndSet(
+ compare: { !$0.contains(peripheral.identifier) },
+ set: { $0.insert(peripheral.identifier) }
+ )
+
+ guard connectingStarted else {
+ observer.onError(BluetoothError.peripheralIsAlreadyObservingConnection(peripheral))
+ return Disposables.create()
+ }
+
+ let connectedObservable = strongSelf.createConnectedObservable(for: peripheral)
+ let failToConnectObservable = strongSelf.createFailToConnectObservable(for: peripheral)
+ let disconnectedObservable = strongSelf.createDisconnectedObservable(for: peripheral)
+
+ let disposable = connectedObservable.amb(failToConnectObservable)
+ .do(onNext: { observer.onNext($0) })
+ .flatMap { _ in disconnectedObservable }
+ .subscribe(onError: { observer.onError($0) })
+
+ // Apple recommends to always connect to a peripheral after retrieving it.
+ // https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/BestPracticesForInteractingWithARemotePeripheralDevice/BestPracticesForInteractingWithARemotePeripheralDevice.html#//apple_ref/doc/uid/TP40013257-CH6-SW9
+ //
+ // Excerpts from "Reconnecting to Peripherals"
+ //
+ // "Retrieve a list of known peripherals—peripherals that you’ve discovered or connected to in the past—using the
+ // retrievePeripheralsWithIdentifiers: method. If the peripheral you’re looking for is in the list, try to connect to it."
+ //
+ // "Assuming that the user finds and selects the desired peripheral, connect it locally to your app by calling the
+ // connectPeripheral:options: method of the CBCentralManager class. (Even though the device is already connected to
+ // the system, you must still connect it locally to your app to begin exploring and interacting with it.) When the local
+ // connection is established, the central manager calls the centralManager:didConnectPeripheral: method of its delegate
+ // object, and the peripheral device is successfully reconnected."
+ strongSelf.centralManager.connect(peripheral.peripheral, options: options)
+
+ return Disposables.create { [weak self] in
+ guard let strongSelf = self else { return }
+ disposable.dispose()
+ let isConnected = strongSelf.connectedBox.read { $0.contains(peripheral.identifier) }
+ if isConnected {
+ if strongSelf.centralManager.state == .poweredOn {
+ strongSelf.disconnectingBox.write { $0.insert(peripheral.identifier) }
+ strongSelf.centralManager.cancelPeripheralConnection(peripheral.peripheral)
+ }
+ strongSelf.connectedBox.write { $0.remove(peripheral.identifier) }
+ }
+ }
+ }
+ }
+
+ fileprivate func createConnectedObservable(for peripheral: Peripheral) -> Observable<Peripheral> {
+ return delegateWrapper.didConnectPeripheral
+ .filter { $0 == peripheral.peripheral }
+ .take(1)
+ .map { _ in peripheral }
+ }
+
+ fileprivate func createDisconnectedObservable(for peripheral: Peripheral) -> Observable<Peripheral> {
+ return delegateWrapper.didDisconnectPeripheral
+ .filter { $0.0 == peripheral.peripheral }
+ .take(1)
+ .do(onNext: { [weak self] _ in
+ guard let strongSelf = self else { return }
+ strongSelf.connectedBox.write { $0.remove(peripheral.identifier) }
+ strongSelf.disconnectingBox.write { $0.remove(peripheral.identifier) }
+ })
+ .map { (_, error) -> Peripheral in
+ throw BluetoothError.peripheralDisconnected(peripheral, error)
+ }
+ }
+
+ fileprivate func createFailToConnectObservable(for peripheral: Peripheral) -> Observable<Peripheral> {
+ return delegateWrapper.didFailToConnectPeripheral
+ .filter { $0.0 == peripheral.peripheral }
+ .take(1)
+ .do(onNext: { [weak self] _ in
+ guard let strongSelf = self else { return }
+ strongSelf.connectedBox.write { $0.remove(peripheral.identifier) }
+ })
+ .map { (_, error) -> Peripheral in
+ throw BluetoothError.peripheralConnectionFailed(peripheral, error)
+ }
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+/// Descriptor is a class implementing ReactiveX which wraps CoreBluetooth functions related to interaction with
+/// [CBDescriptor](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBDescriptor_Class/)
+/// Descriptors provide more information about a characteristic’s value.
+public class Descriptor {
+
+ /// Intance of CoreBluetooth descriptor class
+ public let descriptor: CBDescriptor
+
+ /// Characteristic to which this descriptor belongs.
+ public let characteristic: Characteristic
+
+ /// The Bluetooth UUID of the `Descriptor` instance.
+ public var uuid: CBUUID {
+ return descriptor.uuid
+ }
+
+ /// The value of the descriptor. It can be written and read through functions on `Descriptor` instance.
+ public var value: Any? {
+ return descriptor.value
+ }
+
+ init(descriptor: CBDescriptor, characteristic: Characteristic) {
+ self.descriptor = descriptor
+ self.characteristic = characteristic
+ }
+
+ convenience init?(descriptor: CBDescriptor, peripheral: Peripheral) {
+
+ guard let _characteristic = descriptor.characteristic, let _service = _characteristic.service else {
+ return nil
+ }
+
+ let service = Service(peripheral: peripheral, service: _service)
+ let characteristic = Characteristic(characteristic: _characteristic, service: service)
+ self.init(descriptor: descriptor, characteristic: characteristic)
+ }
+
+ /// Function that allow to observe writes that happened for descriptor.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite() -> Observable<Descriptor> {
+ return characteristic.service.peripheral.observeWrite(for: self)
+ }
+
+ /// Function that triggers write of data to descriptor. Write is called after subscribtion to `Observable` is made.
+ /// - Parameter data: `Data` that'll be written to `Descriptor` instance
+ /// - Returns: `Single` that emits `Next` with `Descriptor` instance, once value is written successfully.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data) -> Single<Descriptor> {
+ return characteristic.service.peripheral.writeValue(data, for: self)
+ }
+
+ /// Function that allow to observe value updates for `Descriptor` instance.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate() -> Observable<Descriptor> {
+ return characteristic.service.peripheral.observeValueUpdate(for: self)
+ }
+
+ /// Function that triggers read of current value of the `Descriptor` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Returns: `Single` which emits `next` with given descriptor when value is ready to read.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readValue() -> Single<Descriptor> {
+ return characteristic.service.peripheral.readValue(for: self)
+ }
+}
+
+extension Descriptor: Equatable {}
+
+/// Compare two descriptors. Descriptors are the same when their UUIDs are the same.
+///
+/// - parameter lhs: First descriptor to compare
+/// - parameter rhs: Second descriptor to compare
+/// - returns: True if both descriptor are the same.
+public func == (lhs: Descriptor, rhs: Descriptor) -> Bool {
+ return lhs.descriptor == rhs.descriptor
+}
--- /dev/null
+import CoreBluetooth
+
+/// ServiceIdentifier protocol specifies how information about services of device should look like.
+/// Sample implementation could look like:
+///
+/// ```
+/// enum DeviceService: String, ServiceIdentifier {
+/// case deviceInformation = "180A"
+/// var uuid: CBUUID {
+/// return CBUUID(string: self.rawValue)
+/// }
+/// }
+/// ```
+/// After implementing this and other protocol `CharacteristicIdentifier, you could call methods even easier
+/// thanks to convenience methods in library. This methods do all of the heavy lifting like discovering services, taking
+/// them from cache etc. for you. We really recommend you to use it.
+public protocol ServiceIdentifier {
+ /// Unique identifier of a service.
+ var uuid: CBUUID { get }
+}
+
+/// Characteristic protocol specifies how information about characteristics of device should look like.
+/// Sample implementation could look like:
+/// ```
+/// enum DeviceCharacteristic: String, CharacteristicIdentifier {
+/// case manufacturerName = "2A29"
+/// var uuid: CBUUID {
+/// return CBUUID(string: self.rawValue)
+/// }
+/// var service: ServiceIdentifier {
+/// switch self {
+/// case .ManufacturerName:
+/// return XXXService.DeviceInformation
+/// }
+/// }
+/// }
+/// ```
+public protocol CharacteristicIdentifier {
+ /// Unique identifier of a characteristic.
+ var uuid: CBUUID { get }
+ /// `ServiceIdentifier` instance that this characteristic belongs to.
+ var service: ServiceIdentifier { get }
+}
+
+/// DescriptorIdentifier protocol specifies how information about descriptors of device should look like.
+public protocol DescriptorIdentifier {
+ /// Unique identifier of a descriptor.
+ var uuid: CBUUID { get }
+ /// `CharacteristicIdentifier` instance that this descriptor belongs to.
+ var characteristic: CharacteristicIdentifier { get }
+}
--- /dev/null
+/// Simple logging interface.
+///
+/// An application that wants RxBluetoothKit to use its logging solution will
+/// need to provide a type that conforms to this signature and assign it to
+/// `RxBluetoothKitLogger.defaultLogger`.
+public protocol Logger {
+ /// Logs the given message (using StaticString parameters).
+ ///
+ /// - Parameters:
+ /// - message: The message to be logger.
+ /// Provided as a closure to avoid performing interpolation if
+ /// the message is not going to be logged.
+ /// - level: The severity of the message.
+ /// A logger can use this flag to decide if to log or ignore
+ /// this specific message.
+ /// - file: The file name of the file where the message was created.
+ /// - function: The function name where the message was created.
+ /// - line: The line number in the file where the message was created.
+ func log(
+ _ message: @autoclosure () -> String,
+ level: RxBluetoothKitLog.LogLevel,
+ file: StaticString,
+ function: StaticString,
+ line: UInt
+ )
+
+ /// Logs the given message (using regular String parameters).
+ ///
+ /// - Parameters:
+ /// - message: The message to be logger.
+ /// Provided as a closure to avoid performing interpolation if
+ /// the message is not going to be logged.
+ /// - level: The severity of the message.
+ /// A logger can use this flag to decide if to log or ignore
+ /// this specific message.
+ /// - file: The file name of the file where the message was created.
+ /// - function: The function name where the message was created.
+ /// - line: The line number in the file where the message was created.
+ func log(
+ _ message: @autoclosure () -> String,
+ level: RxBluetoothKitLog.LogLevel,
+ file: String,
+ function: String,
+ line: UInt
+ )
+
+ /// Set new log level.
+ /// - Parameter logLevel: New log level to be applied.
+ func setLogLevel(_ logLevel: RxBluetoothKitLog.LogLevel)
+
+ /// Get current log level.
+ /// - Returns: Currently set log level.
+ func getLogLevel() -> RxBluetoothKitLog.LogLevel
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+/// RxBluetoothKit specific logging class which gives access to its settings.
+public class RxBluetoothKitLog: ReactiveCompatible {
+
+ fileprivate static let subject = PublishSubject<String>()
+
+ /// Set new log level.
+ /// - Parameter logLevel: New log level to be applied.
+ public static func setLogLevel(_ logLevel: RxBluetoothKitLog.LogLevel) {
+ RxBluetoothKitLogger.defaultLogger.setLogLevel(logLevel)
+ }
+
+ /// Get current log level.
+ /// - Returns: Currently set log level.
+ public static func getLogLevel() -> RxBluetoothKitLog.LogLevel {
+ return RxBluetoothKitLogger.defaultLogger.getLogLevel()
+ }
+
+ private init() {
+ }
+
+ /// Log levels for internal logging mechanism.
+ public enum LogLevel: UInt8 {
+ /// Logging is disabled
+ case none = 255
+ /// All logs are monitored.
+ case verbose = 0
+ /// Only debug logs and of higher importance are logged.
+ case debug = 1
+ /// Only info logs and of higher importance are logged.
+ case info = 2
+ /// Only warning logs and of higher importance are logged.
+ case warning = 3
+ /// Only error logs and of higher importance are logged.
+ case error = 4
+ }
+
+ fileprivate static func log(
+ with logLevel: LogLevel,
+ message: @autoclosure () -> String,
+ file: StaticString,
+ function: StaticString,
+ line: UInt
+ ) {
+ let loggedMessage = message()
+ RxBluetoothKitLogger.defaultLogger.log(
+ loggedMessage,
+ level: logLevel,
+ file: file,
+ function: function,
+ line: line
+ )
+ if getLogLevel() <= logLevel {
+ subject.onNext(loggedMessage)
+ }
+ }
+
+ static func v(
+ _ message: @autoclosure () -> String,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line
+ ) {
+ log(with: .verbose, message: message(), file: file, function: function, line: line)
+ }
+
+ static func d(
+ _ message: @autoclosure () -> String,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line
+ ) {
+ log(with: .debug, message: message(), file: file, function: function, line: line)
+ }
+
+ static func i(
+ _ message: @autoclosure () -> String,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line
+ ) {
+ log(with: .info, message: message(), file: file, function: function, line: line)
+ }
+
+ static func w(
+ _ message: @autoclosure () -> String,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line
+ ) {
+ log(with: .warning, message: message(), file: file, function: function, line: line)
+ }
+
+ static func e(
+ _ message: @autoclosure () -> String,
+ file: StaticString = #file,
+ function: StaticString = #function,
+ line: UInt = #line
+ ) {
+ log(with: .error, message: message(), file: file, function: function, line: line)
+ }
+}
+
+extension RxBluetoothKitLog.LogLevel: Comparable {
+ public static func < (lhs: RxBluetoothKitLog.LogLevel, rhs: RxBluetoothKitLog.LogLevel) -> Bool {
+ return lhs.rawValue < rhs.rawValue
+ }
+
+ public static func <= (lhs: RxBluetoothKitLog.LogLevel, rhs: RxBluetoothKitLog.LogLevel) -> Bool {
+ return lhs.rawValue <= rhs.rawValue
+ }
+
+ public static func > (lhs: RxBluetoothKitLog.LogLevel, rhs: RxBluetoothKitLog.LogLevel) -> Bool {
+ return lhs.rawValue > rhs.rawValue
+ }
+
+ public static func >= (lhs: RxBluetoothKitLog.LogLevel, rhs: RxBluetoothKitLog.LogLevel) -> Bool {
+ return lhs.rawValue >= rhs.rawValue
+ }
+
+ public static func == (lhs: RxBluetoothKitLog.LogLevel, rhs: RxBluetoothKitLog.LogLevel) -> Bool {
+ return lhs.rawValue == rhs.rawValue
+ }
+}
+
+protocol Loggable {
+ var logDescription: String { get }
+}
+
+extension Data: Loggable {
+ var logDescription: String {
+ return map { String(format: "%02x", $0) }.joined()
+ }
+}
+
+extension BluetoothState: Loggable {
+ var logDescription: String {
+ switch self {
+ case .unknown: return "unknown"
+ case .resetting: return "resetting"
+ case .unsupported: return "unsupported"
+ case .unauthorized: return "unauthorized"
+ case .poweredOff: return "poweredOff"
+ case .poweredOn: return "poweredOn"
+ }
+ }
+}
+
+extension CBCharacteristicWriteType: Loggable {
+ var logDescription: String {
+ switch self {
+ case .withResponse: return "withResponse"
+ case .withoutResponse: return "withoutResponse"
+ @unknown default:
+ return "unknown write type"
+ }
+ }
+}
+
+extension UUID: Loggable {
+ var logDescription: String {
+ return uuidString
+ }
+}
+
+extension CBUUID: Loggable {
+ @objc var logDescription: String {
+ return uuidString
+ }
+}
+
+extension CBCentralManager: Loggable {
+ @objc var logDescription: String {
+ return "CentralManager(\(UInt(bitPattern: ObjectIdentifier(self))))"
+ }
+}
+
+extension CBPeripheral: Loggable {
+ @objc var logDescription: String {
+ return "Peripheral(uuid: \(uuidIdentifier), name: \(String(describing: name)))"
+ }
+}
+
+extension CBCharacteristic: Loggable {
+ @objc var logDescription: String {
+ return "Characteristic(uuid: \(uuid), id: \((UInt(bitPattern: ObjectIdentifier(self)))))"
+ }
+}
+
+extension CBService: Loggable {
+ @objc var logDescription: String {
+ return "Service(uuid: \(uuid), id: \((UInt(bitPattern: ObjectIdentifier(self)))))"
+ }
+}
+
+extension CBDescriptor: Loggable {
+ @objc var logDescription: String {
+ return "Descriptor(uuid: \(uuid), id: \((UInt(bitPattern: ObjectIdentifier(self)))))"
+ }
+}
+
+extension CBPeripheralManager: Loggable {
+ @objc var logDescription: String {
+ return "PeripheralManager(\(UInt(bitPattern: ObjectIdentifier(self))))"
+ }
+}
+
+extension CBATTRequest: Loggable {
+ @objc var logDescription: String {
+ return "ATTRequest(\(UInt(bitPattern: ObjectIdentifier(self)))"
+ }
+}
+
+extension CBCentral: Loggable {
+ @objc var logDescription: String {
+ return "CBCentral(uuid: \(uuidIdentifier))"
+ }
+}
+
+@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)
+extension CBL2CAPChannel: Loggable {
+ @objc var logDescription: String {
+ return "CBL2CAPChannel(\(UInt(bitPattern: ObjectIdentifier(self)))"
+ }
+}
+
+extension Array where Element: Loggable {
+ var logDescription: String {
+ return "[\(map { $0.logDescription }.joined(separator: ", "))]"
+ }
+}
+
+extension Reactive where Base == RxBluetoothKitLog {
+ /**
+ * This is continuous value, which emits before a log is printed to standard output.
+ *
+ * - it never fails
+ * - it delivers events on `MainScheduler.instance`
+ * - `share(scope: .whileConnected)` sharing strategy
+ */
+ public var log: Observable<String> {
+ return RxBluetoothKitLog.subject.asObserver()
+ .observeOn(MainScheduler.instance)
+ .catchErrorJustReturn("")
+ .share(scope: .whileConnected)
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+public protocol ManagerType: AnyObject {
+ associatedtype Manager
+
+ /// Implementation of CBManager
+ var manager: Manager { get }
+ /// Current state of `CentralManager` instance described by `BluetoothState` which is equivalent to
+ /// [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
+ var state: BluetoothState { get }
+
+ /// Continuous state of `CBManager` instance described by `BluetoothState` which is equivalent to [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
+ /// - returns: Observable that emits `next` event whenever state changes.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ func observeState() -> Observable<BluetoothState>
+
+ /// Continuous state of `CBManager` instance described by `BluetoothState` which is equivalent to [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
+ /// - returns: Observable that emits `next` event starting with current state and whenever state changes.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ func observeStateWithInitialValue() -> Observable<BluetoothState>
+}
+
+public extension ManagerType {
+ /// Ensure that `state` is and will be the only state of `CentralManager` during subscription.
+ /// Otherwise error is emitted.
+ /// - parameter state: `BluetoothState` which should be present during subscription.
+ /// - parameter observable: Observable into which potential errors should be merged.
+ /// - returns: New observable which merges errors with source observable.
+ func ensure<T>(_ state: BluetoothState, observable: Observable<T>) -> Observable<T> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ let statesObservable = strongSelf.observeStateWithInitialValue()
+ .filter { $0 != state && BluetoothError(state: $0) != nil }
+ .map { state -> T in throw BluetoothError(state: state)! }
+ return .absorb(statesObservable, observable)
+ }
+ }
+}
--- /dev/null
+/// import RXSWift
+
+extension ObservableType {
+ /// Absorbes all of events from a and b observables into result observable.
+ ///
+ /// - parameter a: First observable
+ /// - parameter b: Second observable
+ /// - returns: New `Observable` which emits all of events from a and b `Observable`s.
+ /// If error or complete is received on any of the `Observable`s, it's propagates immediately to result `Observable`
+ static func absorb(_ a: Observable<Element>, _ b: Observable<Element>) -> Observable<Element> {
+ return .create { observer in
+ let disposable = CompositeDisposable()
+ let innerObserver: AnyObserver<Element> = AnyObserver { event in
+ observer.on(event)
+ if event.isStopEvent {
+ disposable.dispose()
+ }
+ }
+ _ = disposable.insert(a.subscribe(innerObserver))
+ if !disposable.isDisposed {
+ _ = disposable.insert(b.subscribe(innerObserver))
+ }
+
+ return disposable
+ }
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+// swiftlint:disable line_length
+
+extension Peripheral {
+
+ /// Function used to receive service with given identifier. It's taken from cache if it's available,
+ /// or directly by `discoverServices` call
+ /// - Parameter identifier: Unique identifier of Service
+ /// - Returns: `Single` which emits `next` event, when specified service has been found.
+ ///
+ /// Observable can ends with following errors:
+ /// * `RxError.noElements`
+ /// * `BluetoothError.servicesDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func service(with identifier: ServiceIdentifier) -> Single<Service> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ if let services = strongSelf.services,
+ let service = services.first(where: { $0.uuid == identifier.uuid }) {
+ return .just(service)
+ } else {
+ return strongSelf.discoverServices([identifier.uuid])
+ .map {
+ if let service = $0.first {
+ return service
+ }
+ throw RxError.noElements
+ }
+ }
+ }
+ }
+
+ /// Function used to receive characteristic with given identifier. If it's available it's taken from cache.
+ /// Otherwise - directly by `discoverCharacteristics` call
+ /// - Parameter identifier: Unique identifier of Characteristic, that has information
+ /// about service which characteristic belongs to.
+ /// - Returns: `Single` which emits `next` event, when specified characteristic has been found.
+ ///
+ /// Observable can ends with following errors:
+ /// * `RxError.noElements`
+ /// * `BluetoothError.characteristicsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func characteristic(with identifier: CharacteristicIdentifier) -> Single<Characteristic> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return strongSelf.service(with: identifier.service)
+ .flatMap { service -> Single<Characteristic> in
+ if let characteristics = service.characteristics, let characteristic = characteristics.first(where: {
+ $0.uuid == identifier.uuid
+ }) {
+ return .just(characteristic)
+ }
+ return service.discoverCharacteristics([identifier.uuid])
+ .map {
+ if let characteristic = $0.first {
+ return characteristic
+ }
+ throw RxError.noElements
+ }
+ }
+ }
+ }
+
+ /// Function used to receive descriptor with given identifier. If it's available it's taken from cache.
+ /// Otherwise - directly by `discoverDescriptor` call
+ /// - Parameter identifier: Unique identifier of Descriptor, that has information
+ /// about characteristic which descriptor belongs to.
+ /// - Returns: `Single` which emits `next` event, when specified descriptor has been found.
+ ///
+ /// Observable can ends with following errors:
+ /// * `RxError.noElements`
+ /// * `BluetoothError.descriptorsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func descriptor(with identifier: DescriptorIdentifier) -> Single<Descriptor> {
+ return .deferred { [weak self] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return strongSelf.characteristic(with: identifier.characteristic)
+ .flatMap { characteristic -> Single<Descriptor> in
+ if let descriptors = characteristic.descriptors,
+ let descriptor = descriptors.first(where: { $0.uuid == identifier.uuid }) {
+ return .just(descriptor)
+ }
+ return characteristic.discoverDescriptors()
+ .map {
+ if let descriptor = $0.filter({ $0.uuid == identifier.uuid }).first {
+ return descriptor
+ }
+ throw RxError.noElements
+ }
+ }
+ }
+ }
+
+ /// Function that allow to observe writes that happened for characteristic.
+ /// - Parameter identifier: Identifier of characteristic of which value writes should be observed.
+ /// - Returns: Observable that emits `next` with `Characteristic` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite(for identifier: CharacteristicIdentifier)
+ -> Observable<Characteristic> {
+ return characteristic(with: identifier)
+ .asObservable()
+ .flatMap { [weak self] in
+ self?.observeWrite(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that triggers write of data to characteristic. Write is called after subscribtion to `Observable` is made.
+ /// Behavior of this function strongly depends on [CBCharacteristicWriteType](https://developer.apple.com/documentation/corebluetooth/cbcharacteristicwritetype),
+ /// so be sure to check this out before usage of the method.
+ /// - parameter data: Data that'll be written written to `Characteristic` instance
+ /// - parameter forCharacteristicWithIdentifier: unique identifier of characteristic, which also holds information about service characteristic belongs to.
+ /// - parameter type: Type of write operation. Possible values: `.withResponse`, `.withoutResponse`
+ /// - returns: Observable that emition depends on `CBCharacteristicWriteType` passed to the function call.
+ /// Behavior is following:
+ /// - `WithResponse` - Observable emits `next` with `Characteristic` instance write was confirmed without any errors.
+ /// Immediately after that `complete` is called. If any problem has happened, errors are emitted.
+ /// - `WithoutResponse` - Observable emits `next` with `Characteristic` instance once write was called.
+ /// Immediately after that `.complete` is called. Result of this call is not checked, so as a user you are not sure
+ /// if everything completed successfully. Errors are not emitted
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data, for identifier: CharacteristicIdentifier,
+ type: CBCharacteristicWriteType) -> Single<Characteristic> {
+ return characteristic(with: identifier)
+ .flatMap { [weak self] in
+ self?.writeValue(data, for: $0, type: type) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that allow to observe value updates for `Characteristic` instance.
+ /// - Parameter identifier: unique identifier of characteristic, which also holds information about service that characteristic belongs to.
+ /// - Returns: Observable that emits `next` with `Characteristic` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate(for identifier: CharacteristicIdentifier) -> Observable<Characteristic> {
+ return characteristic(with: identifier)
+ .asObservable()
+ .flatMap { [weak self] in
+ self?.observeValueUpdate(for: $0).asObservable() ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that triggers read of current value of the `Characteristic` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Parameter identifier: unique identifier of characteristic, which also holds information about service that characteristic belongs to.
+ /// - Returns: Observable which emits `next` with given characteristic when value is ready to read. Immediately after that
+ /// `.complete` is emitted.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readValue(for identifier: CharacteristicIdentifier) -> Single<Characteristic> {
+ return characteristic(with: identifier)
+ .flatMap { [weak self] in
+ self?.readValue(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Setup characteristic notification in order to receive callbacks when given characteristic has been changed.
+ /// Returned observable will emit `Characteristic` on every notification change.
+ /// It is possible to setup more observables for the same characteristic and the lifecycle of the notification will be shared among them.
+ ///
+ /// Notification is automaticaly unregistered once this observable is unsubscribed
+ ///
+ /// - parameter characteristic: `Characteristic` for notification setup.
+ /// - returns: `Observable` emitting `Characteristic` when given characteristic has been changed.
+ ///
+ /// This is **infinite** stream of values.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdateAndSetNotification(for identifier: CharacteristicIdentifier)
+ -> Observable<Characteristic> {
+ return characteristic(with: identifier)
+ .asObservable()
+ .flatMap { [weak self] in
+ self?.observeValueUpdateAndSetNotification(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that triggers descriptors discovery for characteristic
+ /// - parameter identifier: unique identifier of descriptor, which also holds information about characteristic that descriptor belongs to.
+ /// - Returns: `Single` that emits `next` with array of `Descriptor` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverDescriptors(for identifier: CharacteristicIdentifier) ->
+ Single<[Descriptor]> {
+ return characteristic(with: identifier)
+ .flatMap { [weak self] in
+ self?.discoverDescriptors(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that allow to observe writes that happened for descriptor.
+ /// - parameter identifier: unique identifier of descriptor, which also holds information about characteristic that descriptor belongs to.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite(for identifier: DescriptorIdentifier) -> Observable<Descriptor> {
+ return descriptor(with: identifier)
+ .asObservable()
+ .flatMap { [weak self] in
+ self?.observeWrite(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that triggers write of data to descriptor. Write is called after subscribtion to `Observable` is made.
+ /// - parameter data: `Data` that'll be written to `Descriptor` instance
+ /// - parameter identifier: unique identifier of descriptor, which also holds information about characteristic that descriptor belongs to.
+ /// - returns: `Single` that emits `next` with `Descriptor` instance, once value is written successfully.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data, for identifier: DescriptorIdentifier)
+ -> Single<Descriptor> {
+ return descriptor(with: identifier)
+ .flatMap { [weak self] in
+ self?.writeValue(data, for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that allow to observe value updates for `Descriptor` instance.
+ /// - parameter identifier: unique identifier of descriptor, which also holds information about characteristic that descriptor belongs to.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate(for identifier: DescriptorIdentifier) -> Observable<Descriptor> {
+ return descriptor(with: identifier)
+ .asObservable()
+ .flatMap { [weak self] in
+ self?.observeValueUpdate(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+
+ /// Function that triggers read of current value of the `Descriptor` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Parameter identifier: `Descriptor` to read value from
+ /// - Returns: Observable which emits `next` with given descriptor when value is ready to read. Immediately after that
+ /// `.complete` is emitted.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readValue(for identifier: DescriptorIdentifier) -> Single<Descriptor> {
+ return descriptor(with: identifier)
+ .flatMap { [weak self] in
+ self?.readValue(for: $0) ?? .error(BluetoothError.destroyed)
+ }
+ }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+// swiftlint:disable line_length
+// swiftlint:disable type_body_length
+
+/// Peripheral is a class implementing ReactiveX API which wraps all Core Bluetooth functions
+/// allowing to talk to peripheral like discovering characteristics, services and all of the read/write calls.
+public class Peripheral {
+
+ /// Intance of CentralManager which is used to the bluetooth communication
+ public unowned let manager: CentralManager
+
+ /// Implementation of peripheral
+ public let peripheral: CBPeripheral
+
+ /// Object responsible for characteristic notification observing
+ private let notificationManager: CharacteristicNotificationManager
+
+ let delegateWrapper: CBPeripheralDelegateWrapper
+
+ private let remainingServicesDiscoveryRequest = ThreadSafeBox<Int>(value: 0)
+ private let peripheralDidDiscoverServices = PublishSubject<([CBService]?, Error?)>()
+
+ private let remainingIncludedServicesDiscoveryRequest = ThreadSafeBox<[CBUUID: Int]>(value: [CBUUID: Int]())
+ private let peripheralDidDiscoverIncludedServicesForService = PublishSubject<(CBService, Error?)>()
+
+ private let remainingCharacteristicsDiscoveryRequest = ThreadSafeBox<[CBUUID: Int]>(value: [CBUUID: Int]())
+ private let peripheralDidDiscoverCharacteristicsForService = PublishSubject<(CBService, Error?)>()
+
+ private let disposeBag = DisposeBag()
+
+ /// Creates new `Peripheral`
+ /// - parameter manager: Central instance which is used to perform all of the necessary operations.
+ /// - parameter peripheral: Instance representing specific peripheral allowing to perform operations on it.
+ /// - parameter delegateWrapper: Rx wrapper for `CBPeripheralDelegate`.
+ /// - parameter notificationManager: Instance used to observe characteristics notification
+ init(
+ manager: CentralManager,
+ peripheral: CBPeripheral,
+ delegateWrapper: CBPeripheralDelegateWrapper,
+ notificationManager: CharacteristicNotificationManager
+ ) {
+ self.manager = manager
+ self.peripheral = peripheral
+ self.delegateWrapper = delegateWrapper
+ self.notificationManager = notificationManager
+ peripheral.delegate = self.delegateWrapper
+
+ setupSubjects()
+ }
+
+ convenience init(manager: CentralManager,
+ peripheral: CBPeripheral,
+ delegateWrapper: CBPeripheralDelegateWrapper) {
+ let notificationManager = CharacteristicNotificationManager(peripheral: peripheral, delegateWrapper: delegateWrapper)
+ self.init(manager: manager,
+ peripheral: peripheral,
+ delegateWrapper: delegateWrapper,
+ notificationManager: notificationManager)
+ }
+
+ private func setupSubjects() {
+ manager.delegateWrapper
+ .didDisconnectPeripheral
+ .filter { [weak self] peripheral, _ in
+ peripheral.uuidIdentifier == self?.peripheral.uuidIdentifier
+ }
+ .subscribe(onNext: { [weak self] _ in
+ self?.remainingServicesDiscoveryRequest.writeSync { value in
+ value = 0
+ }
+
+ self?.remainingIncludedServicesDiscoveryRequest.writeSync { array in
+ array.removeAll()
+ }
+
+ self?.remainingCharacteristicsDiscoveryRequest.writeSync { array in
+ array.removeAll()
+ }
+ })
+ .disposed(by: disposeBag)
+
+ delegateWrapper.peripheralDidDiscoverServices.subscribe { [weak self] event in
+ self?.remainingServicesDiscoveryRequest.writeSync { value in
+ if value > 0 {
+ value -= 1
+ }
+ }
+ self?.peripheralDidDiscoverServices.on(event)
+ }.disposed(by: disposeBag)
+
+ delegateWrapper.peripheralDidDiscoverIncludedServicesForService.subscribe { [weak self] event in
+ self?.remainingIncludedServicesDiscoveryRequest.writeSync { array in
+ if let element = event.element {
+ let oldValue = array[element.0.uuid] ?? 1
+ if oldValue > 0 {
+ array[element.0.uuid] = oldValue - 1
+ }
+ }
+ }
+ self?.peripheralDidDiscoverIncludedServicesForService.on(event)
+ }.disposed(by: disposeBag)
+
+ delegateWrapper.peripheralDidDiscoverCharacteristicsForService.subscribe { [weak self] event in
+ self?.remainingCharacteristicsDiscoveryRequest.writeSync { array in
+ if let element = event.element {
+ let oldValue = array[element.0.uuid] ?? 1
+ if oldValue > 0 {
+ array[element.0.uuid] = oldValue - 1
+ }
+ }
+ }
+ self?.peripheralDidDiscoverCharacteristicsForService.on(event)
+ }.disposed(by: disposeBag)
+ }
+
+ /// Attaches RxBluetoothKit delegate to CBPeripheral.
+ /// This method is useful in cases when delegate of CBPeripheral was reassigned outside of
+ /// RxBluetoothKit library (e.g. CBPeripheral was used in some other library or used in non-reactive way)
+ public func attach() {
+ peripheral.delegate = delegateWrapper
+ }
+
+ /// Value indicating if peripheral is currently in connected state.
+ public var isConnected: Bool {
+ return peripheral.state == .connected
+ }
+
+ /// Current state of `Peripheral` instance described by [CBPeripheralState](https://developer.apple.com/documentation/corebluetooth/cbperipheralstate).
+ /// - returns: Current state of `Peripheral` as `CBPeripheralState`.
+ public var state: CBPeripheralState {
+ return peripheral.state
+ }
+
+ /// Current name of `Peripheral` instance. Analogous to [name](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name) of `CBPeripheral`.
+ public var name: String? {
+ return peripheral.name
+ }
+
+ /// Unique identifier of `Peripheral` instance. Assigned once peripheral is discovered by the system.
+ public var identifier: UUID {
+ return peripheral.uuidIdentifier
+ }
+
+ /// A list of services that have been discovered. Analogous to [services](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518978-services) of `CBPeripheral`.
+ public var services: [Service]? {
+ return peripheral.services?.map {
+ Service(peripheral: self, service: $0)
+ }
+ }
+
+ /// YES if the remote device has space to send a write without response. If this value is NO,
+ /// the value will be set to YES after the current writes have been flushed, and
+ /// `peripheralIsReadyToSendWriteWithoutResponse:` will be called.
+ public var canSendWriteWithoutResponse: Bool {
+ if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
+ return peripheral.canSendWriteWithoutResponse
+ } else {
+ return true
+ }
+ }
+
+ // MARK: Connecting
+
+ /// Continuous value indicating if peripheral is in connected state. This is continuous value, which emits `.next` whenever state change occurs
+ /// - returns Observable which emits next events when `Peripheral` is connected or disconnected.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeConnection() -> Observable<Bool> {
+ let disconnected = manager.observeDisconnect(for: self).map { _ in false }
+ let connected = manager.observeConnect(for: self).map { _ in true }
+ return Observable.of(disconnected, connected).merge()
+ }
+
+ /// Establishes connection with a given `Peripheral`.
+ /// For more information look into `CentralManager.establishConnection(with:options:)` because this method calls it directly.
+ /// - parameter options: Dictionary to customise the behaviour of connection.
+ /// - returns: `Observable` which emits `next` event after connection is established.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.peripheralIsAlreadyObservingConnection`
+ /// * `BluetoothError.peripheralConnectionFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func establishConnection(options: [String: Any]? = nil) -> Observable<Peripheral> {
+ return manager.establishConnection(self, options: options)
+ }
+
+ // MARK: Services
+
+ /// Triggers discover of specified services of peripheral. If the servicesUUIDs parameter is nil, all the available services of the
+ /// peripheral are returned; setting the parameter to nil is considerably slower and is not recommended.
+ /// If all of the specified services are already discovered - these are returned without doing any underlying Bluetooth operations.
+ ///
+ /// - Parameter serviceUUIDs: An array of [CBUUID](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBUUID_Class/)
+ /// objects that you are interested in. Here, each [CBUUID](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBUUID_Class/)
+ /// object represents a UUID that identifies the type of service you want to discover.
+ /// - Returns: `Single` that emits `next` with array of `Service` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.servicesDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverServices(_ serviceUUIDs: [CBUUID]?) -> Single<[Service]> {
+ if let identifiers = serviceUUIDs, !identifiers.isEmpty,
+ let cachedServices = self.services,
+ let filteredServices = filterUUIDItems(uuids: serviceUUIDs, items: cachedServices, requireAll: true) {
+ return ensureValidPeripheralState(for: .just(filteredServices)).asSingle()
+ }
+ let observable = peripheralDidDiscoverServices
+ .filter { [weak self] (services, error) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ guard let cachedServices = strongSelf.services, error == nil else { return true }
+ let foundRequestedServices = serviceUUIDs != nil && filterUUIDItems(uuids: serviceUUIDs, items: cachedServices, requireAll: true) != nil
+ return foundRequestedServices || strongSelf.remainingServicesDiscoveryRequest.read { $0 == 0 }
+ }
+ .flatMap { [weak self] (_, error) -> Observable<[Service]> in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ guard let cachedServices = strongSelf.services, error == nil else {
+ throw BluetoothError.servicesDiscoveryFailed(strongSelf, error)
+ }
+ if let filteredServices = filterUUIDItems(uuids: serviceUUIDs, items: cachedServices, requireAll: false) {
+ return .just(filteredServices)
+ }
+ return .empty()
+ }
+ .take(1)
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.remainingServicesDiscoveryRequest.writeSync { value in
+ value += 1
+ }
+ self?.peripheral.discoverServices(serviceUUIDs)
+ }
+ )
+ .asSingle()
+ }
+
+ /// Function that triggers included services discovery for specified services. Discovery is called after
+ /// subscribtion to `Observable` is made.
+ /// If all of the specified included services are already discovered - these are returned without doing any underlying Bluetooth
+ /// operations.
+ ///
+ /// - Parameter includedServiceUUIDs: Identifiers of included services that should be discovered. If `nil` - all of the
+ /// included services will be discovered. If you'll pass empty array - none of them will be discovered.
+ /// - Parameter service: Service of which included services should be discovered.
+ /// - Returns: `Single` that emits `next` with array of `Service` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.includedServicesDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverIncludedServices(_ includedServiceUUIDs: [CBUUID]?, for service: Service) -> Single<[Service]> {
+ if let identifiers = includedServiceUUIDs, !identifiers.isEmpty,
+ let services = service.includedServices,
+ let filteredServices = filterUUIDItems(uuids: includedServiceUUIDs, items: services, requireAll: true) {
+ return ensureValidPeripheralState(for: .just(filteredServices)).asSingle()
+ }
+ let observable = peripheralDidDiscoverIncludedServicesForService
+ .filter { $0.0 == service.service }
+ .filter { [weak self] (cbService, error) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ guard let includedCBServices = cbService.includedServices, error == nil else { return true }
+
+ let includedServices = includedCBServices.map { Service(peripheral: strongSelf, service: $0) }
+ let foundRequestedServices = includedServiceUUIDs != nil && filterUUIDItems(uuids: includedServiceUUIDs, items: includedServices, requireAll: true) != nil
+ return foundRequestedServices || strongSelf.remainingIncludedServicesDiscoveryRequest.read { array in
+ return (array[cbService.uuid] ?? 0) == 0
+ }
+ }
+ .flatMap { [weak self] (cbService, error) -> Observable<[Service]> in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ guard let includedRxServices = cbService.includedServices, error == nil else {
+ throw BluetoothError.includedServicesDiscoveryFailed(strongSelf, error)
+ }
+ let includedServices = includedRxServices.map { Service(peripheral: strongSelf, service: $0) }
+ if let filteredServices = filterUUIDItems(uuids: includedServiceUUIDs, items: includedServices, requireAll: false) {
+ return .just(filteredServices)
+ }
+ return .empty()
+ }
+ .take(1)
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.remainingIncludedServicesDiscoveryRequest.writeSync { array in
+ let oldValue = array[service.uuid] ?? 0
+ array[service.uuid] = oldValue + 1
+ }
+ self?.peripheral.discoverIncludedServices(includedServiceUUIDs, for: service.service)
+ }
+ )
+ .asSingle()
+ }
+
+ // MARK: Characteristics
+
+ /// Function that triggers characteristics discovery for specified Services and identifiers. Discovery is called after
+ /// subscribtion to `Observable` is made.
+ /// If all of the specified characteristics are already discovered - these are returned without doing any underlying Bluetooth operations.
+ ///
+ /// - Parameter characteristicUUIDs: Identifiers of characteristics that should be discovered. If `nil` - all of the
+ /// characteristics will be discovered. If you'll pass empty array - none of them will be discovered.
+ /// - Parameter service: Service of which characteristics should be discovered.
+ /// - Returns: `Single` that emits `next` with array of `Characteristic` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverCharacteristics(_ characteristicUUIDs: [CBUUID]?, for service: Service) -> Single<[Characteristic]> {
+ if let identifiers = characteristicUUIDs, !identifiers.isEmpty,
+ let characteristics = service.characteristics,
+ let filteredCharacteristics = filterUUIDItems(uuids: characteristicUUIDs, items: characteristics, requireAll: true) {
+ return ensureValidPeripheralState(for: .just(filteredCharacteristics)).asSingle()
+ }
+ let observable = peripheralDidDiscoverCharacteristicsForService
+ .filter { $0.0 == service.service }
+ .filter { [weak self] (cbService, error) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ guard let cbCharacteristics = cbService.characteristics, error == nil else { return true }
+
+ let characteristics = cbCharacteristics.map { Characteristic(characteristic: $0, service: service) }
+ let foundRequestedCharacteristis = characteristicUUIDs != nil && filterUUIDItems(uuids: characteristicUUIDs, items: characteristics, requireAll: true) != nil
+ return foundRequestedCharacteristis || strongSelf.remainingCharacteristicsDiscoveryRequest.read { array in
+ return (array[cbService.uuid] ?? 0) == 0
+ }
+ }
+ .flatMap { (cbService, error) -> Observable<[Characteristic]> in
+ guard let cbCharacteristics = cbService.characteristics, error == nil else {
+ throw BluetoothError.characteristicsDiscoveryFailed(service, error)
+ }
+ let characteristics = cbCharacteristics.map { Characteristic(characteristic: $0, service: service) }
+ if let filteredCharacteristics = filterUUIDItems(uuids: characteristicUUIDs, items: characteristics, requireAll: false) {
+ return .just(filteredCharacteristics)
+ }
+ return .empty()
+ }
+ .take(1)
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.remainingCharacteristicsDiscoveryRequest.writeSync { array in
+ let oldValue = array[service.uuid] ?? 0
+ array[service.uuid] = oldValue + 1
+ }
+ self?.peripheral.discoverCharacteristics(characteristicUUIDs, for: service.service)
+ }
+ ).asSingle()
+ }
+
+ /// Function that allow to observe writes that happened for characteristic.
+ /// - Parameter characteristic: Optional `Characteristic` of which value changes should be observed. When not specified it will observe for any `Characteristic`.
+ /// - Returns: Observable that emits `next` with `Characteristic` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite(for characteristic: Characteristic? = nil) -> Observable<Characteristic> {
+ let observable = delegateWrapper
+ .peripheralDidWriteValueForCharacteristic
+ .filter { characteristic != nil ? ($0.0 == characteristic!.characteristic) : true }
+ .map { [weak self] (cbCharacteristic, error) -> Characteristic in
+ guard let strongSelf = self,
+ let characteristic = characteristic ?? Characteristic(characteristic: cbCharacteristic, peripheral: strongSelf) else { throw BluetoothError.destroyed }
+
+ if let error = error {
+ throw BluetoothError.characteristicWriteFailed(characteristic, error)
+ }
+ return characteristic
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// The maximum amount of data, in bytes, that can be sent to a characteristic in a single write.
+ /// - parameter type: Type of write operation. Possible values: `.withResponse`, `.withoutResponse`
+ /// - seealso: `writeValue(_:for:type:)`
+ @available(OSX 10.12, iOS 9.0, *)
+ public func maximumWriteValueLength(for type: CBCharacteristicWriteType) -> Int {
+ return peripheral.maximumWriteValueLength(for: type)
+ }
+
+ /// Function that triggers write of data to characteristic. Write is called after subscribtion to `Observable` is made.
+ /// Behavior of this function strongly depends on [CBCharacteristicWriteType](https://developer.apple.com/documentation/corebluetooth/cbcharacteristicwritetype),
+ /// so be sure to check this out before usage of the method.
+ ///
+ /// Behavior is following:
+ /// - `withResponse` - Observable emits `next` with `Characteristic` instance write was confirmed without any errors.
+ /// Immediately after that `complete` is called. If any problem has happened, errors are emitted.
+ /// - `withoutResponse` - Observable emits `next` with `Characteristic` instance once write was called.
+ /// Immediately after that `.complete` is called. Result of this call is not checked, so as a user you are not sure
+ /// if everything completed successfully. Errors are not emitted. It ensures that peripheral is ready to write
+ /// without response by listening to the proper delegate method
+ ///
+ /// - parameter data: Data that'll be written to `Characteristic` instance
+ /// - parameter characteristic: `Characteristic` instance to write value to.
+ /// - parameter type: Type of write operation. Possible values: `.withResponse`, `.withoutResponse`
+ /// - parameter canSendWriteWithoutResponseCheckEnabled: check if canSendWriteWithoutResponse should be enabled. Done because of internal MacOS bug.
+ /// - returns: Observable that emission depends on `CBCharacteristicWriteType` passed to the function call.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data,
+ for characteristic: Characteristic,
+ type: CBCharacteristicWriteType,
+ canSendWriteWithoutResponseCheckEnabled: Bool = true) -> Single<Characteristic> {
+ let writeOperationPerformingAndListeningObservable = { [weak self] (observable: Observable<Characteristic>)
+ -> Observable<Characteristic> in
+ guard let strongSelf = self else { return Observable.error(BluetoothError.destroyed) }
+ return strongSelf.ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.writeValue(data, for: characteristic.characteristic, type: type)
+ }
+ )
+ }
+ switch type {
+ case .withoutResponse:
+ return observeWriteWithoutResponseReadiness()
+ .map { _ in true }
+ .startWith(canSendWriteWithoutResponseCheckEnabled ? canSendWriteWithoutResponse : true)
+ .filter { $0 }
+ .take(1)
+ .flatMap { _ in
+ writeOperationPerformingAndListeningObservable(Observable.just(characteristic))
+ }.asSingle()
+ case .withResponse:
+ return writeOperationPerformingAndListeningObservable(observeWrite(for: characteristic).take(1))
+ .asSingle()
+ @unknown default:
+ return .error(BluetoothError.unknownWriteType)
+ }
+ }
+
+ /// Function that allow to observe value updates for `Characteristic` instance.
+ /// - Parameter characteristic: Optional `Characteristic` of which value changes should be observed. When not specified it will observe for any `Characteristic`.
+ /// - Returns: Observable that emits `next` with `Characteristic` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate(for characteristic: Characteristic? = nil) -> Observable<Characteristic> {
+ let observable = delegateWrapper
+ .peripheralDidUpdateValueForCharacteristic
+ .filter { characteristic != nil ? ($0.0 == characteristic!.characteristic) : true }
+ .map { [weak self] (cbCharacteristic, error) -> Characteristic in
+ guard let strongSelf = self, let characteristic = characteristic ?? Characteristic(characteristic: cbCharacteristic, peripheral: strongSelf) else { throw BluetoothError.destroyed }
+
+ if let error = error {
+ throw BluetoothError.characteristicReadFailed(characteristic, error)
+ }
+ return characteristic
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Function that triggers read of current value of the `Characteristic` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Parameter characteristic: `Characteristic` to read value from
+ /// - Returns: `Single` which emits `next` with given characteristic when value is ready to read.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ public func readValue(for characteristic: Characteristic) -> Single<Characteristic> {
+ let observable = observeValueUpdate(for: characteristic).take(1)
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.readValue(for: characteristic.characteristic)
+ }
+ ).asSingle()
+ }
+
+ /// Setup characteristic notification in order to receive callbacks when given characteristic has been changed.
+ /// Returned observable will emit `Characteristic` on every notification change.
+ /// It is possible to setup more observables for the same characteristic and the lifecycle of the notification will be shared among them.
+ ///
+ /// Notification is automaticaly unregistered once this observable is unsubscribed
+ ///
+ /// - parameter characteristic: `Characteristic` for notification setup.
+ /// - returns: `Observable` emitting `Characteristic` when given characteristic has been changed.
+ ///
+ /// This is **infinite** stream of values.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdateAndSetNotification(for characteristic: Characteristic) -> Observable<Characteristic> {
+ let observable = notificationManager.observeValueUpdateAndSetNotification(for: characteristic)
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Use this function in order to know the exact time, when isNotyfing value has changed on a Characteristic.
+ ///
+ /// - parameter characteristic: `Characteristic` which you observe for isNotyfing changes.
+ /// - returns: `Observable` emitting `Characteristic` when given characteristic has changed it's isNoytfing value.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicSetNotifyValueFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeNotifyValue(for characteristic: Characteristic) -> Observable<Characteristic> {
+ return delegateWrapper.peripheralDidUpdateNotificationStateForCharacteristic
+ .filter { $0.0 == characteristic.characteristic }
+ .map { [weak self] (cbCharacteristic, error) -> Characteristic in
+ guard let strongSelf = self,
+ let characteristic = Characteristic(characteristic: cbCharacteristic, peripheral: strongSelf) else { throw BluetoothError.destroyed }
+
+ if let error = error {
+ throw BluetoothError.characteristicSetNotifyValueFailed(characteristic, error)
+ }
+ return characteristic
+ }
+ }
+
+ // MARK: Descriptors
+
+ /// Function that triggers descriptors discovery for characteristic
+ /// If all of the descriptors are already discovered - these are returned without doing any underlying Bluetooth operations.
+ /// - Parameter characteristic: `Characteristic` instance for which descriptors should be discovered.
+ /// - Returns: `Single` that emits `next` with array of `Descriptor` instances, once they're discovered.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverDescriptors(for characteristic: Characteristic) -> Single<[Descriptor]> {
+ if let descriptors = characteristic.descriptors {
+ let resultDescriptors = descriptors.map { Descriptor(descriptor: $0.descriptor, characteristic: characteristic) }
+ return ensureValidPeripheralState(for: .just(resultDescriptors)).asSingle()
+ }
+ let observable = delegateWrapper
+ .peripheralDidDiscoverDescriptorsForCharacteristic
+ .filter { $0.0 == characteristic.characteristic }
+ .take(1)
+ .map { (cbCharacteristic, error) -> [Descriptor] in
+ if let descriptors = cbCharacteristic.descriptors, error == nil {
+ return descriptors.map {
+ Descriptor(descriptor: $0, characteristic: characteristic) }
+ }
+ throw BluetoothError.descriptorsDiscoveryFailed(characteristic, error)
+ }
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.discoverDescriptors(for: characteristic.characteristic)
+ }
+ ).asSingle()
+ }
+
+ /// Function that allow to observe writes that happened for descriptor.
+ /// - Parameter descriptor: Optional `Descriptor` of which value changes should be observed. When not specified it will observe for any `Descriptor`.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when write has happened.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeWrite(for descriptor: Descriptor? = nil) -> Observable<Descriptor> {
+ let observable = delegateWrapper
+ .peripheralDidWriteValueForDescriptor
+ .filter { descriptor != nil ? ($0.0 == descriptor!.descriptor) : true }
+ .map { [weak self] (cbDescriptor, error) -> Descriptor in
+ guard let strongSelf = self, let descriptor = descriptor ?? Descriptor(descriptor: cbDescriptor, peripheral: strongSelf) else { throw BluetoothError.destroyed }
+
+ if let error = error {
+ throw BluetoothError.descriptorWriteFailed(descriptor, error)
+ }
+ return descriptor
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Function that allow to observe value updates for `Descriptor` instance.
+ /// - Parameter descriptor: Optional `Descriptor` of which value changes should be observed. When not specified it will observe for any `Descriptor`.
+ /// - Returns: Observable that emits `next` with `Descriptor` instance every time when value has changed.
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeValueUpdate(for descriptor: Descriptor? = nil) -> Observable<Descriptor> {
+ let observable = delegateWrapper
+ .peripheralDidUpdateValueForDescriptor
+ .filter { descriptor != nil ? ($0.0 == descriptor!.descriptor) : true }
+ .map { [weak self] (cbDescriptor, error) -> Descriptor in
+ guard let strongSelf = self, let descriptor = descriptor ?? Descriptor(descriptor: cbDescriptor, peripheral: strongSelf) else { throw BluetoothError.destroyed }
+ if let error = error {
+ throw BluetoothError.descriptorReadFailed(descriptor, error)
+ }
+ return descriptor
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Function that triggers read of current value of the `Descriptor` instance.
+ /// Read is called after subscription to `Observable` is made.
+ /// - Parameter descriptor: `Descriptor` to read value from
+ /// - Returns: `Single` which emits `next` with given descriptor when value is ready to read.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readValue(for descriptor: Descriptor) -> Single<Descriptor> {
+ let observable = observeValueUpdate(for: descriptor).take(1)
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.readValue(for: descriptor.descriptor) }
+ )
+ .asSingle()
+ }
+
+ /// Function that triggers write of data to descriptor. Write is called after subscribtion to `Observable` is made.
+ /// - Parameter data: `Data` that'll be written to `Descriptor` instance
+ /// - Parameter descriptor: `Descriptor` instance to write value to.
+ /// - Returns: `Single` that emits `next` with `Descriptor` instance, once value is written successfully.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.descriptorWriteFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func writeValue(_ data: Data, for descriptor: Descriptor) -> Single<Descriptor> {
+ let observeWrite = self.observeWrite(for: descriptor).take(1)
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observeWrite,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.writeValue(data, for: descriptor.descriptor) }
+ )
+ .asSingle()
+ }
+
+ // MARK: Other methods
+
+ /// Function that triggers read of `Peripheral` RSSI value. Read is called after subscription to `Observable` is made.
+ /// - returns: `Single` that emits tuple: `(Peripheral, Int)` once new RSSI value is read.
+ /// `Int` is new RSSI value, `Peripheral` is returned to allow easier chaining.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.peripheralRSSIReadFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func readRSSI() -> Single<(Peripheral, Int)> {
+ let observable = delegateWrapper
+ .peripheralDidReadRSSI
+ .take(1)
+ .map { [weak self] (rssi, error) -> (Peripheral, Int) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ if let error = error {
+ throw BluetoothError.peripheralRSSIReadFailed(strongSelf, error)
+ }
+ return (strongSelf, rssi)
+ }
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(
+ for: observable,
+ postSubscriptionCall: { [weak self] in
+ self?.peripheral.readRSSI()
+ }
+ ).asSingle()
+ }
+
+ /// Function that allow user to observe incoming `name` property changes of `Peripheral` instance.
+ /// - returns: `Observable` that emits tuples: `(Peripheral, String?)` when name has changed.
+ /// It's `optional String` because peripheral could also lost his name.
+ /// It's **infinite** stream of values, so `.complete` is never emitted.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeNameUpdate() -> Observable<(Peripheral, String?)> {
+ let observable = delegateWrapper.peripheralDidUpdateName.map { [weak self] name -> (Peripheral, String?) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return (strongSelf, name)
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Function that allow to observe incoming service modifications for `Peripheral` instance.
+ /// In case you're interested what exact changes might occur - please refer to
+ /// [Apple Documentation](https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518865-peripheral)
+ ///
+ /// - returns: `Observable` that emits tuples: `(Peripheral, [Service])` when services were modified.
+ /// It's **infinite** stream of values, so `.complete` is never emitted.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeServicesModification() -> Observable<(Peripheral, [Service])> {
+ let observable = delegateWrapper.peripheralDidModifyServices
+ .map { [weak self] services -> [Service] in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return services.map { Service(peripheral: strongSelf, service: $0) } }
+ .map { [weak self] services -> (Peripheral, [Service]) in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ return (strongSelf, services)
+ }
+ return ensureValidPeripheralState(for: observable)
+ }
+
+ /// Resulting observable emits next element if call to `writeValue:forCharacteristic:type:` has failed,
+ /// to indicate when peripheral is again ready to send characteristic value updates again.
+ public func observeWriteWithoutResponseReadiness() -> Observable<Void> {
+ return delegateWrapper.peripheralIsReadyToSendWriteWithoutResponse.asObservable()
+ }
+
+ /// Function that allow to open L2CAP channel for `Peripheral` instance.
+ /// For more information, please refer to
+ /// [What’s New in CoreBluetooth, 712, WWDC 2017](https://developer.apple.com/videos/play/wwdc2017/712/)
+ ///
+ /// - parameter PSM: `PSM` (Protocol/Service Multiplexer) of the channel
+ /// - returns: `Single` that emits `CBL2CAPChannel` when channel has opened
+ /// - since: iOS 11, tvOS 11, watchOS 4
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.openingL2CAPChannelFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ @available(iOS 11, macOS 10.14, tvOS 11, watchOS 4, *)
+ public func openL2CAPChannel(PSM: CBL2CAPPSM) -> Single<CBL2CAPChannel> {
+ let observable = delegateWrapper
+ .peripheralDidOpenL2CAPChannel
+ .map {($0.0 as? CBL2CAPChannel, $0.1)}
+ .take(1)
+ .flatMap { [weak self] (channel, error) -> Observable<CBL2CAPChannel> in
+ guard let strongSelf = self else { throw BluetoothError.destroyed }
+ if let channel = channel, error == nil {
+ return .just(channel)
+ } else {
+ throw BluetoothError.openingL2CAPChannelFailed(strongSelf, error)
+ }
+ }
+
+ return ensureValidPeripheralStateAndCallIfSucceeded(for: observable, postSubscriptionCall: { [weak self] in
+ self?.peripheral.openL2CAPChannel(PSM)
+ }).asSingle()
+ }
+
+ // MARK: Internal functions
+
+ func ensureValidPeripheralStateAndCallIfSucceeded<T>(for observable: Observable<T>,
+ postSubscriptionCall call: @escaping () -> Void
+ ) -> Observable<T> {
+ let operation = Observable<T>.deferred {
+ call()
+ return .empty()
+ }
+ return ensureValidPeripheralState(for: Observable.merge([observable, operation]))
+ }
+
+ /// Function that merges given observable with error streams of invalid Central Manager states.
+ /// - parameter observable: `Observable` to be transformed
+ /// - returns: Source `Observable` which listens on state change errors as well
+ func ensureValidPeripheralState<T>(for observable: Observable<T>) -> Observable<T> {
+ return Observable<T>.absorb(
+ manager.ensurePeripheralIsConnected(self),
+ manager.ensure(.poweredOn, observable: observable)
+ )
+ }
+}
+
+extension Peripheral: Equatable {}
+
+/// Compare two peripherals which are the same when theirs identifiers are equal.
+///
+/// - parameter lhs: First peripheral to compare
+/// - parameter rhs: Second peripheral to compare
+/// - returns: True if both peripherals are the same
+public func == (lhs: Peripheral, rhs: Peripheral) -> Bool {
+ return lhs.peripheral == rhs.peripheral
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+import CoreBluetooth
+
+/// Closure that receives restored state dict as a parameter
+public typealias OnWillRestorePeripheralManagerState = (PeripheralManagerRestoredState) -> Void
+
+extension PeripheralManager {
+
+ // MARK: State restoration
+
+ /// Creates new `PeripheralManager` instance, which supports bluetooth state restoration.
+ /// - warning: If you pass background queue to the method make sure to observe results on main thread
+ /// for UI related code.
+ /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used
+ /// and all operations and events are executed and received on main thread.
+ /// - parameter options: An optional dictionary containing initialization options for a peripheral manager.
+ /// For more info about it please refer to [Peripheral Manager initialization options](https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/peripheral_manager_initialization_options)
+ /// - parameter cbPeripheralManager: Optional instance of `CBPeripheralManager` to be used as a `manager`. If you
+ /// skip this parameter, there will be created an instance of `CBPeripheralManager` using given queue and options.
+ /// - parameter onWillRestorePeripheralManagerState: Closure called when state has been restored.
+ ///
+ /// - seealso: `RestoredState`
+ public convenience init(queue: DispatchQueue = .main,
+ options: [String: AnyObject]? = nil,
+ cbPeripheralManager: CBPeripheralManager? = nil,
+ onWillRestorePeripheralManagerState: OnWillRestorePeripheralManagerState? = nil) {
+ self.init(queue: queue, options: options)
+ if let onWillRestorePeripheralManagerState = onWillRestorePeripheralManagerState {
+ listenOnWillRestoreState(onWillRestorePeripheralManagerState)
+ }
+ }
+
+ /// Creates new `PeripheralManager`
+ /// - parameter peripheralManager: Peripheral instance which is used to perform all of the necessary operations
+ /// - parameter delegateWrapper: Wrapper on CoreBluetooth's peripheral manager callbacks.
+ /// - parameter onWillRestoreState: Closure called when state has been restored.
+ convenience init(
+ peripheralManager: CBPeripheralManager,
+ delegateWrapper: CBPeripheralManagerDelegateWrapper,
+ onWillRestorePeripheralManagerState: @escaping OnWillRestorePeripheralManagerState
+ ) {
+ self.init(
+ peripheralManager: peripheralManager,
+ delegateWrapper: delegateWrapper
+ )
+ listenOnWillRestoreState(onWillRestorePeripheralManagerState)
+ }
+
+ /// Emits restored state dict instance, when state of `PeripheralManager` has been restored,
+ /// Should only be called once in the lifetime of the app
+ /// - returns: Observable which emits next events state has been restored
+ func listenOnWillRestoreState(_ handler: @escaping OnWillRestorePeripheralManagerState) {
+ _ = delegateWrapper
+ .willRestoreState
+ .take(1)
+ .subscribe(onNext: { [weak self] restoredState in
+ guard let strongSelf = self else { return }
+ let restoredState = PeripheralManagerRestoredState(restoredStateDictionary: restoredState)
+ strongSelf.restoredAdvertisementData = restoredState.advertisementData
+ handler(restoredState)
+ })
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+/// PeripheralManager is a class implementing ReactiveX API which wraps all the Core Bluetooth Peripheral's functions, that allow to
+/// advertise, to publish L2CAP channels and more.
+/// You can start using this class by adding services and starting advertising.
+/// Before calling any public `PeripheralManager`'s functions you should make sure that Bluetooth is turned on and powered on. It can be done
+/// by `observeStateWithInitialValue()`, observing it's value and then chaining it with `add(_:)` and `startAdvertising(_:)`:
+/// ```
+/// let disposable = centralManager.observeStateWithInitialValue()
+/// .filter { $0 == .poweredOn }
+/// .take(1)
+/// .flatMap { centralManager.add(myService) }
+/// .flatMap { centralManager.startAdvertising(myAdvertisementData) }
+/// ```
+/// As a result, your peripheral will start advertising. To stop advertising simply dispose it:
+/// ```
+/// disposable.dispose()
+/// ```
+public class PeripheralManager: ManagerType {
+
+ /// Implementation of CBPeripheralManager
+ public let manager: CBPeripheralManager
+
+ let delegateWrapper: CBPeripheralManagerDelegateWrapper
+
+ /// Lock for checking advertising state
+ private let advertisingLock = NSLock()
+ /// Is there ongoing advertising
+ var isAdvertisingOngoing = false
+ var restoredAdvertisementData: RestoredAdvertisementData?
+
+ // MARK: Initialization
+
+ /// Creates new `PeripheralManager`
+ /// - parameter peripheralManager: `CBPeripheralManager` instance which is used to perform all of the necessary operations
+ /// - parameter delegateWrapper: Wrapper on CoreBluetooth's peripheral manager callbacks.
+ init(peripheralManager: CBPeripheralManager, delegateWrapper: CBPeripheralManagerDelegateWrapper) {
+ self.manager = peripheralManager
+ self.delegateWrapper = delegateWrapper
+ peripheralManager.delegate = delegateWrapper
+ }
+
+ /// Creates new `PeripheralManager` instance. By default all operations and events are executed and received on main thread.
+ /// - warning: If you pass background queue to the method make sure to observe results on main thread for UI related code.
+ /// - parameter queue: Queue on which bluetooth callbacks are received. By default main thread is used.
+ /// - parameter options: An optional dictionary containing initialization options for a peripheral manager.
+ /// For more info about it please refer to [Peripheral Manager initialization options](https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/peripheral_manager_initialization_options)
+ /// - parameter cbPeripheralManager: Optional instance of `CBPeripheralManager` to be used as a `manager`. If you
+ /// skip this parameter, there will be created an instance of `CBPeripheralManager` using given queue and options.
+ public convenience init(queue: DispatchQueue = .main,
+ options: [String: AnyObject]? = nil,
+ cbPeripheralManager: CBPeripheralManager? = nil) {
+ let delegateWrapper = CBPeripheralManagerDelegateWrapper()
+ #if os(iOS) || os(macOS)
+ let peripheralManager = cbPeripheralManager ??
+ CBPeripheralManager(delegate: delegateWrapper, queue: queue, options: options)
+ #else
+ let peripheralManager = CBPeripheralManager()
+ peripheralManager.delegate = delegateWrapper
+ #endif
+ self.init(peripheralManager: peripheralManager, delegateWrapper: delegateWrapper)
+ }
+
+ /// Returns the app’s authorization status for sharing data while in the background state.
+ /// Wrapper of `CBPeripheralManager.authorizationStatus()` method.
+ static var authorizationStatus: CBPeripheralManagerAuthorizationStatus {
+ return CBPeripheralManager.authorizationStatus()
+ }
+
+ // MARK: State
+
+ public var state: BluetoothState {
+ public var state: BluetoothState {
+ return BluetoothState(rawValue: manager.state.rawValue) ?? .unknown
+ }
+
+ public func observeState() -> Observable<BluetoothState> {
+ return self.delegateWrapper.didUpdateState.asObservable()
+ }
+
+ public func observeStateWithInitialValue() -> Observable<BluetoothState> {
+ return Observable.deferred { [weak self] in
+ guard let self = self else {
+ RxBluetoothKitLog.w("observeState - PeripheralManager deallocated")
+ return .never()
+ }
+
+ return self.delegateWrapper.didUpdateState.asObservable()
+ .startWith(self.state)
+ }
+ }
+
+ // MARK: Advertising
+
+ /// Starts peripheral advertising on subscription. It create inifinite observable
+ /// which emits only one next value, of enum type `StartAdvertisingResult`, just
+ /// after advertising start succeeds.
+ /// For more info of what specific `StartAdvertisingResult` enum cases means please
+ /// refer to ``StartAdvertisingResult` documentation.
+ ///
+ /// There can be only one ongoing advertising (CoreBluetooth limit).
+ /// It will return `advertisingInProgress` error if this method is called when
+ /// it is already advertising.
+ ///
+ /// Advertising is automatically stopped just after disposing of the subscription.
+ ///
+ /// It can return `BluetoothError.advertisingStartFailed` error, when start advertisement failed
+ ///
+ /// - parameter advertisementData: Services of peripherals to search for. Nil value will accept all peripherals.
+ /// - returns: Infinite observable which emit `StartAdvertisingResult` when advertisement started.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.advertisingInProgress`
+ /// * `BluetoothError.advertisingStartFailed`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func startAdvertising(_ advertisementData: [String: Any]?) -> Observable<StartAdvertisingResult> {
+ let observable: Observable<StartAdvertisingResult> = Observable.create { [weak self] observer in
+ guard let strongSelf = self else {
+ observer.onError(BluetoothError.destroyed)
+ return Disposables.create()
+ }
+ strongSelf.advertisingLock.lock(); defer { strongSelf.advertisingLock.unlock() }
+ if strongSelf.isAdvertisingOngoing {
+ observer.onError(BluetoothError.advertisingInProgress)
+ return Disposables.create()
+ }
+
+ strongSelf.isAdvertisingOngoing = true
+
+ var disposable: Disposable?
+ if strongSelf.manager.isAdvertising {
+ observer.onNext(.attachedToExternalAdvertising(strongSelf.restoredAdvertisementData))
+ strongSelf.restoredAdvertisementData = nil
+ } else {
+ disposable = strongSelf.delegateWrapper.didStartAdvertising
+ .take(1)
+ .map { error in
+ if let error = error {
+ throw BluetoothError.advertisingStartFailed(error)
+ }
+ return .started
+ }
+ .subscribe(onNext: { observer.onNext($0) }, onError: { observer.onError($0)})
+ strongSelf.manager.startAdvertising(advertisementData)
+ }
+ return Disposables.create { [weak self] in
+ guard let strongSelf = self else { return }
+ disposable?.dispose()
+ strongSelf.manager.stopAdvertising()
+ do { strongSelf.advertisingLock.lock(); defer { strongSelf.advertisingLock.unlock() }
+ strongSelf.isAdvertisingOngoing = false
+ }
+ }
+ }
+
+ return ensure(.poweredOn, observable: observable)
+ }
+
+ // MARK: Services
+
+ /// Function that triggers `CBPeripheralManager.add(_:)` and waits for
+ /// delegate `CBPeripheralManagerDelegate.peripheralManager(_:didAdd:error:)` result.
+ /// If it receives a non nil in the result, it will emit `BluetoothError.addingServiceFailed` error.
+ /// Add method is called after subscription to `Observable` is made.
+ /// - Parameter service: `Characteristic` to read value from
+ /// - Returns: `Single` which emits `next` with given characteristic when value is ready to read.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.addingServiceFailed`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func add(_ service: CBMutableService) -> Single<CBService> {
+ let observable = delegateWrapper
+ .didAddService
+ .filter { $0.0.uuid == service.uuid }
+ .take(1)
+ .map { (cbService, error) -> (CBService) in
+ if let error = error {
+ throw BluetoothError.addingServiceFailed(cbService, error)
+ }
+ return cbService
+ }
+ return ensureValidStateAndCallIfSucceeded(for: observable) {
+ [weak self] in
+ self?.manager.add(service)
+ }.asSingle()
+ }
+
+ /// Wrapper for `CBPeripheralManager.remove(_:)` method
+ public func remove(_ service: CBMutableService) {
+ manager.remove(service)
+ }
+
+ /// Wrapper for `CBPeripheralManager.removeAllServices()` method
+ public func removeAllServices() {
+ manager.removeAllServices()
+ }
+
+ // MARK: Read & Write
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:didReceiveRead:)` results
+ /// - returns: Observable that emits `next` event whenever didReceiveRead occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeDidReceiveRead() -> Observable<CBATTRequest> {
+ return ensure(.poweredOn, observable: delegateWrapper.didReceiveRead)
+ }
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:didReceiveWrite:)` results
+ /// - returns: Observable that emits `next` event whenever didReceiveWrite occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeDidReceiveWrite() -> Observable<[CBATTRequest]> {
+ return ensure(.poweredOn, observable: delegateWrapper.didReceiveWrite)
+ }
+
+ /// Wrapper for `CBPeripheralManager.respond(to:withResult:)` method
+ public func respond(to request: CBATTRequest, withResult result: CBATTError.Code) {
+ manager.respond(to: request, withResult: result)
+ }
+
+ // MARK: Updating value
+
+ /// Wrapper for `CBPeripheralManager.updateValue(_:for:onSubscribedCentrals:)` method
+ public func updateValue(
+ _ value: Data,
+ for characteristic: CBMutableCharacteristic,
+ onSubscribedCentrals centrals: [CBCentral]?) -> Bool {
+ return manager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
+ }
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManagerIsReady(toUpdateSubscribers:)` results
+ /// - returns: Observable that emits `next` event whenever isReadyToUpdateSubscribers occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeIsReadyToUpdateSubscribers() -> Observable<Void> {
+ return ensure(.poweredOn, observable: delegateWrapper.isReady)
+ }
+
+ // MARK: Subscribing
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:central:didSubscribeTo:)` results
+ /// - returns: Observable that emits `next` event whenever didSubscribeTo occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeOnSubscribe() -> Observable<(CBCentral, CBCharacteristic)> {
+ return ensure(.poweredOn, observable: delegateWrapper.didSubscribeTo)
+ }
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:central:didUnsubscribeFrom:)` results
+ /// - returns: Observable that emits `next` event whenever didUnsubscribeFrom occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func observeOnUnsubscribe() -> Observable<(CBCentral, CBCharacteristic)> {
+ return ensure(.poweredOn, observable: delegateWrapper.didUnsubscribeFrom)
+ }
+
+ // MARK: L2CAP
+
+ #if os(iOS) || os(tvOS) || os(watchOS)
+
+ /// Starts publishing L2CAP channel on a subscription. It creates an infinite observable
+ /// which emits only one next value, of `CBL2CAPPSM` type, just
+ /// after L2CAP channel has been published.
+ ///
+ /// Channel is automatically unpublished just after disposing of the subscription.
+ ///
+ /// It can return `publishingL2CAPChannelFailed` error when publishing channel failed
+ ///
+ /// - parameter encryptionRequired: Publishing channel with or without encryption.
+ /// - returns: Infinite observable which emit `CBL2CAPPSM` when channel published.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.publishingL2CAPChannelFailed`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ @available(iOS 11, tvOS 11, watchOS 4, *)
+ public func publishL2CAPChannel(withEncryption encryptionRequired: Bool) -> Observable<CBL2CAPPSM> {
+ let observable: Observable<CBL2CAPPSM> = Observable.create { [weak self] observer in
+ guard let strongSelf = self else {
+ observer.onError(BluetoothError.destroyed)
+ return Disposables.create()
+ }
+
+ var result: CBL2CAPPSM?
+ let disposable = strongSelf.delegateWrapper.didPublishL2CAPChannel
+ .take(1)
+ .map { (cbl2cappSm, error) -> (CBL2CAPPSM) in
+ if let error = error {
+ throw BluetoothError.publishingL2CAPChannelFailed(cbl2cappSm, error)
+ }
+ result = cbl2cappSm
+ return cbl2cappSm
+ }
+ .subscribe(onNext: { observer.onNext($0) }, onError: { observer.onError($0)})
+ strongSelf.manager.publishL2CAPChannel(withEncryption: encryptionRequired)
+ return Disposables.create { [weak self] in
+ guard let strongSelf = self else { return }
+ disposable.dispose()
+ if let result = result {
+ strongSelf.manager.unpublishL2CAPChannel(result)
+ }
+ }
+ }
+ return self.ensure(.poweredOn, observable: observable)
+ }
+
+ /// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:didOpen:error:)` results
+ /// - returns: Observable that emits `next` event whenever didOpen occurs.
+ ///
+ /// It's **infinite** stream, so `.complete` is never called.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ @available(iOS 11, tvOS 11, watchOS 4, *)
+ public func observeDidOpenL2CAPChannel() -> Observable<(CBL2CAPChannel?, Error?)> {
+ return ensure(.poweredOn, observable: delegateWrapper.didOpenChannel)
+ }
+ #endif
+
+ // MARK: Internal functions
+
+ func ensureValidStateAndCallIfSucceeded<T>(for observable: Observable<T>,
+ postSubscriptionCall call: @escaping () -> Void
+ ) -> Observable<T> {
+ let operation = Observable<T>.deferred {
+ call()
+ return .empty()
+ }
+ return ensure(.poweredOn, observable: Observable.merge([observable, operation]))
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Convenience class which helps reading state of restored PeripheralManager.
+public struct PeripheralManagerRestoredState {
+
+ /// Restored state dictionary.
+ public let restoredStateData: [String: Any]
+
+ /// Creates restored state information based on CoreBluetooth's dictionary
+ /// - parameter restoredStateDictionary: Core Bluetooth's restored state data
+ init(restoredStateDictionary: [String: Any]) {
+ restoredStateData = restoredStateDictionary
+ }
+
+ /// An array of CBMutableService objects that contains all of the services that
+ /// were published to the local peripheral’s database at the time the app was
+ /// terminated by the system.
+ public var services: [CBMutableService] {
+ let services = restoredStateData[CBPeripheralManagerRestoredStateServicesKey] as? [CBMutableService]
+ return services ?? []
+ }
+
+ /// A dictionary containing the data that the peripheral manager was advertising
+ /// at the time the app was terminated by the system.
+ public var advertisementData: [String: Any]? {
+ return restoredStateData[CBPeripheralManagerRestoredStateAdvertisementDataKey] as? [String: AnyObject]
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Class for providing peripherals and peripheral wrappers
+class PeripheralProvider {
+
+ private let peripheralsBox: ThreadSafeBox<[Peripheral]> = ThreadSafeBox(value: [])
+
+ private let delegateWrappersBox: ThreadSafeBox<[UUID: CBPeripheralDelegateWrapper]> = ThreadSafeBox(value: [:])
+
+ /// Provides `CBPeripheralDelegateWrapper` for specified `CBPeripheral`.
+ ///
+ /// If it was previously created it returns that object, so that there can be only
+ /// one `CBPeripheralDelegateWrapper` per `CBPeripheral`.
+ ///
+ /// If not it creates new one.
+ ///
+ /// - parameter peripheral: Peripheral for which to provide delegate wrapper
+ /// - returns: Delegate wrapper for specified peripheral.
+ func provideDelegateWrapper(for peripheral: CBPeripheral) -> CBPeripheralDelegateWrapper {
+ if let delegateWrapper = delegateWrappersBox.read({ $0[peripheral.uuidIdentifier] }) {
+ return delegateWrapper
+ } else {
+ delegateWrappersBox.compareAndSet(
+ compare: { $0[peripheral.uuidIdentifier] == nil },
+ set: { $0[peripheral.uuidIdentifier] = CBPeripheralDelegateWrapper()}
+ )
+ return delegateWrappersBox.read({ $0[peripheral.uuidIdentifier]! })
+ }
+ }
+
+ /// Provides `Peripheral` for specified `CBPeripheral`.
+ ///
+ /// If it was previously created it returns that object, so that there can be only one `Peripheral`
+ /// per `CBPeripheral`. If not it creates new one.
+ ///
+ /// - parameter peripheral: Peripheral for which to provide delegate wrapper
+ /// - returns: `Peripheral` for specified peripheral.
+ func provide(for cbPeripheral: CBPeripheral, centralManager: CentralManager) -> Peripheral {
+ if let peripheral = find(cbPeripheral) {
+ return peripheral
+ } else {
+ return createAndAddToBox(cbPeripheral, manager: centralManager)
+ }
+ }
+
+ fileprivate func createAndAddToBox(_ cbPeripheral: CBPeripheral, manager: CentralManager) -> Peripheral {
+ peripheralsBox.compareAndSet(
+ compare: { peripherals in
+ return !peripherals.contains(where: { $0.peripheral == cbPeripheral })
+ },
+ set: { [weak self] peripherals in
+ guard let strongSelf = self else { return }
+ let delegateWrapper = strongSelf.provideDelegateWrapper(for: cbPeripheral)
+ let newPeripheral = Peripheral(
+ manager: manager,
+ peripheral: cbPeripheral,
+ delegateWrapper: delegateWrapper
+ )
+ peripherals.append(newPeripheral)
+ }
+ )
+ return peripheralsBox.read { peripherals in
+ return peripherals.first(where: { $0.peripheral == cbPeripheral })!
+ }
+ }
+
+ fileprivate func find(_ cbPeripheral: CBPeripheral) -> Peripheral? {
+ return peripheralsBox.read { peripherals in
+ return peripherals.first(where: { $0.peripheral == cbPeripheral})
+ }
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Deprecated, use CentralManager.init(queue:options:onWillRestoreCentralManagerState:) instead
+@available(*, deprecated, renamed: "CentralManagerRestoredStateType")
+public struct RestoredState: CentralManagerRestoredStateType {
+ let centralManagerRestoredState: CentralManagerRestoredState
+
+ public var restoredStateData: [String: Any] { return centralManagerRestoredState.restoredStateData }
+
+ public var centralManager: CentralManager { return centralManagerRestoredState.centralManager }
+
+ public var peripherals: [Peripheral] { return centralManagerRestoredState.peripherals }
+
+ public var scanOptions: [String: AnyObject]? { return centralManagerRestoredState.scanOptions }
+
+ public var services: [Service] { return centralManagerRestoredState.services }
+
+ init(centralManagerRestoredState: CentralManagerRestoredState) {
+ self.centralManagerRestoredState = centralManagerRestoredState
+ }
+}
--- /dev/null
+import Foundation
+
+/// Namespace for assigning the default logger.
+public enum RxBluetoothKitLogger {
+ /// The default logger that will be used by RxBluetoothKit
+ public static var defaultLogger: Logger = SimplePrintLogger()
+}
+
+private class SimplePrintLogger: Logger {
+ private var currentLogLevel: RxBluetoothKitLog.LogLevel = .none
+
+ /// Set new log level.
+ /// - Parameter logLevel: New log level to be applied.
+ public func setLogLevel(_ logLevel: RxBluetoothKitLog.LogLevel) {
+ self.currentLogLevel = logLevel
+ }
+
+ /// Get current log level.
+ /// - Returns: Currently set log level.
+ public func getLogLevel() -> RxBluetoothKitLog.LogLevel {
+ return currentLogLevel
+ }
+
+ func log(
+ _ message: @autoclosure () -> String,
+ level: RxBluetoothKitLog.LogLevel,
+ file: StaticString,
+ function: StaticString,
+ line: UInt
+ ) {
+ log(
+ message() + " file: \(file) function: \(function) line: \(line)",
+ level: level,
+ file: String(describing: file),
+ function: String(describing: function),
+ line: line
+ )
+ }
+
+ func log(
+ _ message: @autoclosure () -> String,
+ level: RxBluetoothKitLog.LogLevel,
+ file: String,
+ function: String,
+ line: UInt
+ ) {
+ if currentLogLevel <= level {
+ print("\(tag(with: level)) \(message())")
+ }
+ }
+
+ fileprivate func tag(with logLevel: RxBluetoothKitLog.LogLevel) -> String {
+ let prefix: String
+
+ switch logLevel {
+ case .none:
+ prefix = "[RxBLEKit|NONE|"
+ case .verbose:
+ prefix = "[RxBLEKit|VERB|"
+ case .debug:
+ prefix = "[RxBLEKit|DEBG|"
+ case .info:
+ prefix = "[RxBLEKit|INFO|"
+ case .warning:
+ prefix = "[RxBLEKit|WARN|"
+ case .error:
+ prefix = "[RxBLEKit|ERRO|"
+ }
+ let time = Date().timeIntervalSinceReferenceDate
+
+ return prefix + String(
+ format: "%02.0f:%02.0f:%02.0f.%03.f]:",
+ floor(time / 3600.0).truncatingRemainder(dividingBy: 24),
+ floor(time / 60.0).truncatingRemainder(dividingBy: 60),
+ floor(time).truncatingRemainder(dividingBy: 60),
+ floor(time * 1000).truncatingRemainder(dividingBy: 1000)
+ )
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+/// Represents instance of scanned peripheral - containing it's advertisment data, rssi and peripheral itself.
+/// To perform further actions `peripheral` instance variable can be used ia. to maintain connection.
+public class ScannedPeripheral {
+
+ /// `Peripheral` instance, that allows to perform further bluetooth actions.
+ public let peripheral: Peripheral
+
+ /// Advertisement data of scanned peripheral
+ public let advertisementData: AdvertisementData
+
+ /// Scanned peripheral's RSSI value.
+ public let rssi: NSNumber
+
+ init(peripheral: Peripheral, advertisementData: AdvertisementData, rssi: NSNumber) {
+ self.peripheral = peripheral
+ self.advertisementData = advertisementData
+ self.rssi = rssi
+ }
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+/// import RXSWift
+
+/// Service is a class implementing ReactiveX which wraps CoreBluetooth functions related to interaction with [CBService](https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBService_Class/)
+public class Service {
+ /// Intance of CoreBluetooth service class
+ public let service: CBService
+
+ /// Peripheral to which this service belongs
+ public let peripheral: Peripheral
+
+ /// True if service is primary service
+ public var isPrimary: Bool {
+ return service.isPrimary
+ }
+
+ /// Service's UUID
+ public var uuid: CBUUID {
+ return service.uuid
+ }
+
+ /// Service's included services
+ public var includedServices: [Service]? {
+ return service.includedServices?.map {
+ Service(peripheral: peripheral, service: $0)
+ }
+ }
+
+ /// Service's characteristics
+ public var characteristics: [Characteristic]? {
+ return service.characteristics?.map {
+ Characteristic(characteristic: $0, service: self)
+ }
+ }
+
+ init(peripheral: Peripheral, service: CBService) {
+ self.service = service
+ self.peripheral = peripheral
+ }
+
+ /// Function that triggers characteristics discovery for specified Services and identifiers. Discovery is called after
+ /// subscribtion to `Observable` is made.
+ /// - Parameter identifiers: Identifiers of characteristics that should be discovered. If `nil` - all of the
+ /// characteristics will be discovered. If you'll pass empty array - none of them will be discovered.
+ /// - Returns: `Single` that emits `next` with array of `Characteristic` instances, once they're discovered.
+ /// If not all requested characteristics are discovered, `RxError.noElements` error is emmited.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.characteristicsDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverCharacteristics(_ characteristicUUIDs: [CBUUID]?) -> Single<[Characteristic]> {
+ return peripheral.discoverCharacteristics(characteristicUUIDs, for: self)
+ }
+
+ /// Function that triggers included services discovery for specified services. Discovery is called after
+ /// subscribtion to `Observable` is made.
+ /// - Parameter includedServiceUUIDs: Identifiers of included services that should be discovered. If `nil` - all of the
+ /// included services will be discovered. If you'll pass empty array - none of them will be discovered.
+ /// - Returns: `Single` that emits `next` with array of `Service` instances, once they're discovered.
+ /// If not all requested services are discovered, `RxError.noElements` error is emmited.
+ ///
+ /// Observable can ends with following errors:
+ /// * `BluetoothError.includedServicesDiscoveryFailed`
+ /// * `BluetoothError.peripheralDisconnected`
+ /// * `BluetoothError.destroyed`
+ /// * `BluetoothError.bluetoothUnsupported`
+ /// * `BluetoothError.bluetoothUnauthorized`
+ /// * `BluetoothError.bluetoothPoweredOff`
+ /// * `BluetoothError.bluetoothInUnknownState`
+ /// * `BluetoothError.bluetoothResetting`
+ public func discoverIncludedServices(_ includedServiceUUIDs: [CBUUID]?) -> Single<[Service]> {
+ return peripheral.discoverIncludedServices(includedServiceUUIDs, for: self)
+ }
+}
+
+extension Service: Equatable {}
+extension Service: UUIDIdentifiable {}
+
+/// Compare if services are equal. They are if theirs uuids are the same.
+/// - parameter lhs: First service
+/// - parameter rhs: Second service
+/// - returns: True if services are the same.
+public func == (lhs: Service, rhs: Service) -> Bool {
+ return lhs.service == rhs.service
+}
--- /dev/null
+import Foundation
+
+public typealias RestoredAdvertisementData = [String: Any]
+
+/// Enum result that is returned as a result of `PeripheralManager.startAdvertising` method
+public enum StartAdvertisingResult {
+ /// Advertising started properly with specified `advertisementData`
+ case started
+ /// This is a special case meaning that there is already ongoing advertising that has been started
+ /// outside of `RxBluetoothKit` library and `PeripherlManager.startAdvertising` did only attached
+ /// to ongoing advertising without calling `CBPeripheralManager.startAdvertising`.
+ /// The reason behind that is that we want to give user possibility to stop advertising in
+ /// such state.
+ /// In most cases it happens when app went from background with ongoing advertising - in that case you will receive `RestoredAdvertisementData` param, so you can know with what `advertisementData` it was started before app went background.
+ /// WARNING: remember that this is not really calling `CBPeripheralManager.startAdvertising`
+ /// so it might be not started with `advertisementData` param that you've provided. If you
+ /// want to start with different `advertisementData` then you will need to dispose
+ /// advertising observable and call `startAdvertising` again.
+ case attachedToExternalAdvertising(RestoredAdvertisementData?)
+}
--- /dev/null
+import Foundation
+import CoreBluetooth
+
+protocol UUIDIdentifiable {
+ var uuid: CBUUID { get }
+}
+
+/// Filters an item list based on the provided UUID list. The items must conform to UUIDIdentifiable.
+/// Only items returned whose UUID matches an item in the provided UUID list.
+/// If requredAll is true, each UUID should have at least one item matching in the items list.
+/// Otherwise the result is nil.
+/// - uuids: a UUID list or nil
+/// - items: items to be filtered
+/// - requireAll: method will return nil if this param is true and uuids is not subset of items
+/// - Returns: the filtered item list
+func filterUUIDItems<T: UUIDIdentifiable>(uuids: [CBUUID]?, items: [T], requireAll: Bool) -> [T]? {
+ guard let uuids = uuids, !uuids.isEmpty else { return items }
+
+ let itemsUUIDs = items.map { $0.uuid }
+ let uuidsSet = Set(uuids)
+ guard !requireAll || uuidsSet.isSubset(of: Set(itemsUUIDs)) else { return nil }
+ return items.filter { uuidsSet.contains($0.uuid) }
+}
--- /dev/null
+import Foundation
+/// import RXSWift
+
+func unimplementedFunction(file: String = #file, function: String = #function, line: Int = #line) {
+ fatalError("Unimplemented function \(function) in \(file):\(line)")
+}
+
+extension Observable {
+ static func unimplemented(file: String = #file, function: String = #function, line: Int = #line)
+ -> Observable<Element> {
+ unimplementedFunction(file: file, function: function, line: line)
+ return Observable<Element>.empty()
+ }
+}
--- /dev/null
+//
+// AddRef.swift
+// RxSwift
+//
+// Created by Junior B. on 30/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+final class AddRefSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .completed, .error:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+}
+
+final class AddRef<Element> : Producer<Element> {
+
+ private let _source: Observable<Element>
+ private let _refCount: RefCountDisposable
+
+ init(source: Observable<Element>, refCount: RefCountDisposable) {
+ self._source = source
+ self._refCount = refCount
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let releaseDisposable = self._refCount.retain()
+ let sink = AddRefSink(observer: observer, cancel: cancel)
+ let subscription = Disposables.create(releaseDisposable, self._source.subscribe(sink))
+
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Amb.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Propagates the observable sequence that reacts first.
+
+ - seealso: [amb operator on reactivex.io](http://reactivex.io/documentation/operators/amb.html)
+
+ - returns: An observable sequence that surfaces any of the given sequences, whichever reacted first.
+ */
+ public static func amb<Sequence: Swift.Sequence>(_ sequence: Sequence) -> Observable<Element>
+ where Sequence.Element == Observable<Element> {
+ return sequence.reduce(Observable<Sequence.Element.Element>.never()) { a, o in
+ return a.amb(o.asObservable())
+ }
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Propagates the observable sequence that reacts first.
+
+ - seealso: [amb operator on reactivex.io](http://reactivex.io/documentation/operators/amb.html)
+
+ - parameter right: Second observable sequence.
+ - returns: An observable sequence that surfaces either of the given sequences, whichever reacted first.
+ */
+ public func amb<O2: ObservableType>
+ (_ right: O2)
+ -> Observable<Element> where O2.Element == Element {
+ return Amb(left: self.asObservable(), right: right.asObservable())
+ }
+}
+
+private enum AmbState {
+ case neither
+ case left
+ case right
+}
+
+final private class AmbObserver<Observer: ObserverType>: ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = AmbSink<Observer>
+ typealias This = AmbObserver<Observer>
+ typealias Sink = (This, Event<Element>) -> Void
+
+ private let _parent: Parent
+ fileprivate var _sink: Sink
+ fileprivate var _cancel: Disposable
+
+ init(parent: Parent, cancel: Disposable, sink: @escaping Sink) {
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+
+ self._parent = parent
+ self._sink = sink
+ self._cancel = cancel
+ }
+
+ func on(_ event: Event<Element>) {
+ self._sink(self, event)
+ if event.isStopEvent {
+ self._cancel.dispose()
+ }
+ }
+
+ deinit {
+#if TRACE_RESOURCES
+ _ = Resources.decrementTotal()
+#endif
+ }
+}
+
+final private class AmbSink<Observer: ObserverType>: Sink<Observer> {
+ typealias Element = Observer.Element
+ typealias Parent = Amb<Element>
+ typealias AmbObserverType = AmbObserver<Observer>
+
+ private let _parent: Parent
+
+ private let _lock = RecursiveLock()
+ // state
+ private var _choice = AmbState.neither
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let disposeAll = Disposables.create(subscription1, subscription2)
+
+ let forwardEvent = { (o: AmbObserverType, event: Event<Element>) -> Void in
+ self.forwardOn(event)
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+
+ let decide = { (o: AmbObserverType, event: Event<Element>, me: AmbState, otherSubscription: Disposable) in
+ self._lock.performLocked {
+ if self._choice == .neither {
+ self._choice = me
+ o._sink = forwardEvent
+ o._cancel = disposeAll
+ otherSubscription.dispose()
+ }
+
+ if self._choice == me {
+ self.forwardOn(event)
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+ }
+ }
+
+ let sink1 = AmbObserver(parent: self, cancel: subscription1) { o, e in
+ decide(o, e, .left, subscription2)
+ }
+
+ let sink2 = AmbObserver(parent: self, cancel: subscription1) { o, e in
+ decide(o, e, .right, subscription1)
+ }
+
+ subscription1.setDisposable(self._parent._left.subscribe(sink1))
+ subscription2.setDisposable(self._parent._right.subscribe(sink2))
+
+ return disposeAll
+ }
+}
+
+final private class Amb<Element>: Producer<Element> {
+ fileprivate let _left: Observable<Element>
+ fileprivate let _right: Observable<Element>
+
+ init(left: Observable<Element>, right: Observable<Element>) {
+ self._left = left
+ self._right = right
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = AmbSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// AnonymousDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an Action-based disposable.
+///
+/// When dispose method is called, disposal action will be dereferenced.
+private final class AnonymousDisposable : DisposeBase, Cancelable {
+ public typealias DisposeAction = () -> Void
+
+ private let _isDisposed = AtomicInt(0)
+ private var _disposeAction: DisposeAction?
+
+ /// - returns: Was resource disposed.
+ public var isDisposed: Bool {
+ return isFlagSet(self._isDisposed, 1)
+ }
+
+ /// Constructs a new disposable with the given action used for disposal.
+ ///
+ /// - parameter disposeAction: Disposal action which will be run upon calling `dispose`.
+ private init(_ disposeAction: @escaping DisposeAction) {
+ self._disposeAction = disposeAction
+ super.init()
+ }
+
+ // Non-deprecated version of the constructor, used by `Disposables.create(with:)`
+ fileprivate init(disposeAction: @escaping DisposeAction) {
+ self._disposeAction = disposeAction
+ super.init()
+ }
+
+ /// Calls the disposal action if and only if the current instance hasn't been disposed yet.
+ ///
+ /// After invoking disposal action, disposal action will be dereferenced.
+ fileprivate func dispose() {
+ if fetchOr(self._isDisposed, 1) == 0 {
+ if let action = self._disposeAction {
+ self._disposeAction = nil
+ action()
+ }
+ }
+ }
+}
+
+extension Disposables {
+
+ /// Constructs a new disposable with the given action used for disposal.
+ ///
+ /// - parameter dispose: Disposal action which will be run upon calling `dispose`.
+ public static func create(with dispose: @escaping () -> Void) -> Cancelable {
+ return AnonymousDisposable(disposeAction: dispose)
+ }
+
+}
--- /dev/null
+//
+// AnonymousObserver.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+final class AnonymousObserver<Element>: ObserverBase<Element> {
+ typealias EventHandler = (Event<Element>) -> Void
+
+ private let _eventHandler : EventHandler
+
+ init(_ eventHandler: @escaping EventHandler) {
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ self._eventHandler = eventHandler
+ }
+
+ override func onCore(_ event: Event<Element>) {
+ return self._eventHandler(event)
+ }
+
+#if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+#endif
+}
--- /dev/null
+//
+// AnyObserver.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/28/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// A type-erased `ObserverType`.
+///
+/// Forwards operations to an arbitrary underlying observer with the same `Element` type, hiding the specifics of the underlying observer type.
+public struct AnyObserver<Element> : ObserverType {
+ /// Anonymous event handler type.
+ public typealias EventHandler = (Event<Element>) -> Void
+
+ private let observer: EventHandler
+
+ /// Construct an instance whose `on(event)` calls `eventHandler(event)`
+ ///
+ /// - parameter eventHandler: Event handler that observes sequences events.
+ public init(eventHandler: @escaping EventHandler) {
+ self.observer = eventHandler
+ }
+
+ /// Construct an instance whose `on(event)` calls `observer.on(event)`
+ ///
+ /// - parameter observer: Observer that receives sequence events.
+ public init<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
+ self.observer = observer.on
+ }
+
+ /// Send `event` to this observer.
+ ///
+ /// - parameter event: Event instance.
+ public func on(_ event: Event<Element>) {
+ return self.observer(event)
+ }
+
+ /// Erases type of observer and returns canonical observer.
+ ///
+ /// - returns: type erased observer.
+ public func asObserver() -> AnyObserver<Element> {
+ return self
+ }
+}
+
+extension AnyObserver {
+ /// Collection of `AnyObserver`s
+ typealias s = Bag<(Event<Element>) -> Void>
+}
+
+extension ObserverType {
+ /// Erases type of observer and returns canonical observer.
+ ///
+ /// - returns: type erased observer.
+ public func asObserver() -> AnyObserver<Element> {
+ return AnyObserver(self)
+ }
+
+ /// Transforms observer of type R to type E using custom transform method.
+ /// Each event sent to result observer is transformed and sent to `self`.
+ ///
+ /// - returns: observer that transforms events.
+ public func mapObserver<Result>(_ transform: @escaping (Result) throws -> Element) -> AnyObserver<Result> {
+ return AnyObserver { e in
+ self.on(e.map(transform))
+ }
+ }
+}
--- /dev/null
+//
+// AsMaybe.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/12/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+private final class AsMaybeSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+
+ private var _element: Event<Element>?
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ if self._element != nil {
+ self.forwardOn(.error(RxError.moreThanOneElement))
+ self.dispose()
+ }
+
+ self._element = event
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if let element = self._element {
+ self.forwardOn(element)
+ }
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final class AsMaybe<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = AsMaybeSink(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// AsSingle.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/12/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+private final class AsSingleSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+
+ private var _element: Event<Element>?
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ if self._element != nil {
+ self.forwardOn(.error(RxError.moreThanOneElement))
+ self.dispose()
+ }
+
+ self._element = event
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if let element = self._element {
+ self.forwardOn(element)
+ self.forwardOn(.completed)
+ }
+ else {
+ self.forwardOn(.error(RxError.noElements))
+ }
+ self.dispose()
+ }
+ }
+}
+
+final class AsSingle<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = AsSingleSink(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// AsyncLock.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+In case nobody holds this lock, the work will be queued and executed immediately
+on thread that is requesting lock.
+
+In case there is somebody currently holding that lock, action will be enqueued.
+When owned of the lock finishes with it's processing, it will also execute
+and pending work.
+
+That means that enqueued work could possibly be executed later on a different thread.
+*/
+final class AsyncLock<I: InvocableType>
+ : Disposable
+ , Lock
+ , SynchronizedDisposeType {
+ typealias Action = () -> Void
+
+ var _lock = SpinLock()
+
+ private var _queue: Queue<I> = Queue(capacity: 0)
+
+ private var _isExecuting: Bool = false
+ private var _hasFaulted: Bool = false
+
+ // lock {
+ func lock() {
+ self._lock.lock()
+ }
+
+ func unlock() {
+ self._lock.unlock()
+ }
+ // }
+
+ private func enqueue(_ action: I) -> I? {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if self._hasFaulted {
+ return nil
+ }
+
+ if self._isExecuting {
+ self._queue.enqueue(action)
+ return nil
+ }
+
+ self._isExecuting = true
+
+ return action
+ // }
+ }
+
+ private func dequeue() -> I? {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if !self._queue.isEmpty {
+ return self._queue.dequeue()
+ }
+ else {
+ self._isExecuting = false
+ return nil
+ }
+ // }
+ }
+
+ func invoke(_ action: I) {
+ let firstEnqueuedAction = self.enqueue(action)
+
+ if let firstEnqueuedAction = firstEnqueuedAction {
+ firstEnqueuedAction.invoke()
+ }
+ else {
+ // action is enqueued, it's somebody else's concern now
+ return
+ }
+
+ while true {
+ let nextAction = self.dequeue()
+
+ if let nextAction = nextAction {
+ nextAction.invoke()
+ }
+ else {
+ return
+ }
+ }
+ }
+
+ func dispose() {
+ self.synchronizedDispose()
+ }
+
+ func _synchronized_dispose() {
+ self._queue = Queue(capacity: 0)
+ self._hasFaulted = true
+ }
+}
--- /dev/null
+//
+// AsyncSubject.swift
+// RxSwift
+//
+// Created by Victor Galán on 07/01/2017.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+/// An AsyncSubject emits the last value (and only the last value) emitted by the source Observable,
+/// and only after that source Observable completes.
+///
+/// (If the source Observable does not emit any values, the AsyncSubject also completes without emitting any values.)
+public final class AsyncSubject<Element>
+ : Observable<Element>
+ , SubjectType
+ , ObserverType
+ , SynchronizedUnsubscribeType {
+ public typealias SubjectObserverType = AsyncSubject<Element>
+
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ /// Indicates whether the subject has any observers
+ public var hasObservers: Bool {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._observers.count > 0
+ }
+
+ let _lock = RecursiveLock()
+
+ // state
+ private var _observers = Observers()
+ private var _isStopped = false
+ private var _stoppedEvent = nil as Event<Element>? {
+ didSet {
+ self._isStopped = self._stoppedEvent != nil
+ }
+ }
+ private var _lastElement: Element?
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+
+ /// Creates a subject.
+ public override init() {
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ super.init()
+ }
+
+ /// Notifies all subscribed observers about next event.
+ ///
+ /// - parameter event: Event to send to the observers.
+ public func on(_ event: Event<Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ let (observers, event) = self._synchronized_on(event)
+ switch event {
+ case .next:
+ dispatch(observers, event)
+ dispatch(observers, .completed)
+ case .completed:
+ dispatch(observers, event)
+ case .error:
+ dispatch(observers, event)
+ }
+ }
+
+ func _synchronized_on(_ event: Event<Element>) -> (Observers, Event<Element>) {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._isStopped {
+ return (Observers(), .completed)
+ }
+
+ switch event {
+ case .next(let element):
+ self._lastElement = element
+ return (Observers(), .completed)
+ case .error:
+ self._stoppedEvent = event
+
+ let observers = self._observers
+ self._observers.removeAll()
+
+ return (observers, event)
+ case .completed:
+
+ let observers = self._observers
+ self._observers.removeAll()
+
+ if let lastElement = self._lastElement {
+ self._stoppedEvent = .next(lastElement)
+ return (observers, .next(lastElement))
+ }
+ else {
+ self._stoppedEvent = event
+ return (observers, .completed)
+ }
+ }
+ }
+
+ /// Subscribes an observer to the subject.
+ ///
+ /// - parameter observer: Observer to subscribe to the subject.
+ /// - returns: Disposable object that can be used to unsubscribe the observer from the subject.
+ public override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._synchronized_subscribe(observer)
+ }
+
+ func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if let stoppedEvent = self._stoppedEvent {
+ switch stoppedEvent {
+ case .next:
+ observer.on(stoppedEvent)
+ observer.on(.completed)
+ case .completed:
+ observer.on(stoppedEvent)
+ case .error:
+ observer.on(stoppedEvent)
+ }
+ return Disposables.create()
+ }
+
+ let key = self._observers.insert(observer.on)
+
+ return SubscriptionDisposable(owner: self, key: key)
+ }
+
+ func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock(); defer { self._lock.unlock() }
+ self._synchronized_unsubscribe(disposeKey)
+ }
+
+ func _synchronized_unsubscribe(_ disposeKey: DisposeKey) {
+ _ = self._observers.removeKey(disposeKey)
+ }
+
+ /// Returns observer interface for subject.
+ public func asObserver() -> AsyncSubject<Element> {
+ return self
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
+
--- /dev/null
+//
+// AtomicInt.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 10/28/18.
+// Copyright © 2018 Krunoslav Zaher. All rights reserved.
+//
+
+import class Foundation.NSLock
+
+final class AtomicInt: NSLock {
+ fileprivate var value: Int32
+ public init(_ value: Int32 = 0) {
+ self.value = value
+ }
+}
+
+@discardableResult
+@inline(__always)
+func add(_ this: AtomicInt, _ value: Int32) -> Int32 {
+ this.lock()
+ let oldValue = this.value
+ this.value += value
+ this.unlock()
+ return oldValue
+}
+
+@discardableResult
+@inline(__always)
+func sub(_ this: AtomicInt, _ value: Int32) -> Int32 {
+ this.lock()
+ let oldValue = this.value
+ this.value -= value
+ this.unlock()
+ return oldValue
+}
+
+@discardableResult
+@inline(__always)
+func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
+ this.lock()
+ let oldValue = this.value
+ this.value |= mask
+ this.unlock()
+ return oldValue
+}
+
+@inline(__always)
+func load(_ this: AtomicInt) -> Int32 {
+ this.lock()
+ let oldValue = this.value
+ this.unlock()
+ return oldValue
+}
+
+@discardableResult
+@inline(__always)
+func increment(_ this: AtomicInt) -> Int32 {
+ return add(this, 1)
+}
+
+@discardableResult
+@inline(__always)
+func decrement(_ this: AtomicInt) -> Int32 {
+ return sub(this, 1)
+}
+
+@inline(__always)
+func isFlagSet(_ this: AtomicInt, _ mask: Int32) -> Bool {
+ return (load(this) & mask) != 0
+}
--- /dev/null
+//
+// Bag+Rx.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/19/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+
+// MARK: forEach
+
+@inline(__always)
+func dispatch<Element>(_ bag: Bag<(Event<Element>) -> Void>, _ event: Event<Element>) {
+ bag._value0?(event)
+
+ if bag._onlyFastPath {
+ return
+ }
+
+ let pairs = bag._pairs
+ for i in 0 ..< pairs.count {
+ pairs[i].value(event)
+ }
+
+ if let dictionary = bag._dictionary {
+ for element in dictionary.values {
+ element(event)
+ }
+ }
+}
+
+/// Dispatches `dispose` to all disposables contained inside bag.
+func disposeAll(in bag: Bag<Disposable>) {
+ bag._value0?.dispose()
+
+ if bag._onlyFastPath {
+ return
+ }
+
+ let pairs = bag._pairs
+ for i in 0 ..< pairs.count {
+ pairs[i].value.dispose()
+ }
+
+ if let dictionary = bag._dictionary {
+ for element in dictionary.values {
+ element.dispose()
+ }
+ }
+}
--- /dev/null
+//
+// Bag.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 2/28/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import Swift
+
+let arrayDictionaryMaxSize = 30
+
+struct BagKey {
+ /**
+ Unique identifier for object added to `Bag`.
+
+ It's underlying type is UInt64. If we assume there in an idealized CPU that works at 4GHz,
+ it would take ~150 years of continuous running time for it to overflow.
+ */
+ fileprivate let rawValue: UInt64
+}
+
+/**
+Data structure that represents a bag of elements typed `T`.
+
+Single element can be stored multiple times.
+
+Time and space complexity of insertion and deletion is O(n).
+
+It is suitable for storing small number of elements.
+*/
+struct Bag<T> : CustomDebugStringConvertible {
+ /// Type of identifier for inserted elements.
+ typealias KeyType = BagKey
+
+ typealias Entry = (key: BagKey, value: T)
+
+ private var _nextKey: BagKey = BagKey(rawValue: 0)
+
+ // data
+
+ // first fill inline variables
+ var _key0: BagKey?
+ var _value0: T?
+
+ // then fill "array dictionary"
+ var _pairs = ContiguousArray<Entry>()
+
+ // last is sparse dictionary
+ var _dictionary: [BagKey: T]?
+
+ var _onlyFastPath = true
+
+ /// Creates new empty `Bag`.
+ init() {
+ }
+
+ /**
+ Inserts `value` into bag.
+
+ - parameter element: Element to insert.
+ - returns: Key that can be used to remove element from bag.
+ */
+ mutating func insert(_ element: T) -> BagKey {
+ let key = _nextKey
+
+ _nextKey = BagKey(rawValue: _nextKey.rawValue &+ 1)
+
+ if _key0 == nil {
+ _key0 = key
+ _value0 = element
+ return key
+ }
+
+ _onlyFastPath = false
+
+ if _dictionary != nil {
+ _dictionary![key] = element
+ return key
+ }
+
+ if _pairs.count < arrayDictionaryMaxSize {
+ _pairs.append((key: key, value: element))
+ return key
+ }
+
+ _dictionary = [key: element]
+
+ return key
+ }
+
+ /// - returns: Number of elements in bag.
+ var count: Int {
+ let dictionaryCount: Int = _dictionary?.count ?? 0
+ return (_value0 != nil ? 1 : 0) + _pairs.count + dictionaryCount
+ }
+
+ /// Removes all elements from bag and clears capacity.
+ mutating func removeAll() {
+ _key0 = nil
+ _value0 = nil
+
+ _pairs.removeAll(keepingCapacity: false)
+ _dictionary?.removeAll(keepingCapacity: false)
+ }
+
+ /**
+ Removes element with a specific `key` from bag.
+
+ - parameter key: Key that identifies element to remove from bag.
+ - returns: Element that bag contained, or nil in case element was already removed.
+ */
+ mutating func removeKey(_ key: BagKey) -> T? {
+ if _key0 == key {
+ _key0 = nil
+ let value = _value0!
+ _value0 = nil
+ return value
+ }
+
+ if let existingObject = _dictionary?.removeValue(forKey: key) {
+ return existingObject
+ }
+
+ for i in 0 ..< _pairs.count where _pairs[i].key == key {
+ let value = _pairs[i].value
+ _pairs.remove(at: i)
+ return value
+ }
+
+ return nil
+ }
+}
+
+extension Bag {
+ /// A textual representation of `self`, suitable for debugging.
+ var debugDescription : String {
+ return "\(self.count) elements in Bag"
+ }
+}
+
+extension Bag {
+ /// Enumerates elements inside the bag.
+ ///
+ /// - parameter action: Enumeration closure.
+ func forEach(_ action: (T) -> Void) {
+ if _onlyFastPath {
+ if let value0 = _value0 {
+ action(value0)
+ }
+ return
+ }
+
+ let value0 = _value0
+ let dictionary = _dictionary
+
+ if let value0 = value0 {
+ action(value0)
+ }
+
+ for i in 0 ..< _pairs.count {
+ action(_pairs[i].value)
+ }
+
+ if dictionary?.count ?? 0 > 0 {
+ for element in dictionary!.values {
+ action(element)
+ }
+ }
+ }
+}
+
+extension BagKey: Hashable {
+ func hash(into hasher: inout Hasher) {
+ hasher.combine(rawValue)
+ }
+}
+
+func ==(lhs: BagKey, rhs: BagKey) -> Bool {
+ return lhs.rawValue == rhs.rawValue
+}
--- /dev/null
+//
+// BehaviorSubject.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/23/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a value that changes over time.
+///
+/// Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications.
+public final class BehaviorSubject<Element>
+ : Observable<Element>
+ , SubjectType
+ , ObserverType
+ , SynchronizedUnsubscribeType
+ , Cancelable {
+ public typealias SubjectObserverType = BehaviorSubject<Element>
+
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ /// Indicates whether the subject has any observers
+ public var hasObservers: Bool {
+ self._lock.lock()
+ let value = self._observers.count > 0
+ self._lock.unlock()
+ return value
+ }
+
+ let _lock = RecursiveLock()
+
+ // state
+ private var _isDisposed = false
+ private var _element: Element
+ private var _observers = Observers()
+ private var _stoppedEvent: Event<Element>?
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ /// Indicates whether the subject has been disposed.
+ public var isDisposed: Bool {
+ return self._isDisposed
+ }
+
+ /// Initializes a new instance of the subject that caches its last value and starts with the specified value.
+ ///
+ /// - parameter value: Initial value sent to observers when no other value has been received by the subject yet.
+ public init(value: Element) {
+ self._element = value
+
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ /// Gets the current value or throws an error.
+ ///
+ /// - returns: Latest value.
+ public func value() throws -> Element {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if self._isDisposed {
+ throw RxError.disposed(object: self)
+ }
+
+ if let error = self._stoppedEvent?.error {
+ // intentionally throw exception
+ throw error
+ }
+ else {
+ return self._element
+ }
+ //}
+ }
+
+ /// Notifies all subscribed observers about next event.
+ ///
+ /// - parameter event: Event to send to the observers.
+ public func on(_ event: Event<Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ dispatch(self._synchronized_on(event), event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) -> Observers {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._stoppedEvent != nil || self._isDisposed {
+ return Observers()
+ }
+
+ switch event {
+ case .next(let element):
+ self._element = element
+ case .error, .completed:
+ self._stoppedEvent = event
+ }
+
+ return self._observers
+ }
+
+ /// Subscribes an observer to the subject.
+ ///
+ /// - parameter observer: Observer to subscribe to the subject.
+ /// - returns: Disposable object that can be used to unsubscribe the observer from the subject.
+ public override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock()
+ let subscription = self._synchronized_subscribe(observer)
+ self._lock.unlock()
+ return subscription
+ }
+
+ func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if self._isDisposed {
+ observer.on(.error(RxError.disposed(object: self)))
+ return Disposables.create()
+ }
+
+ if let stoppedEvent = self._stoppedEvent {
+ observer.on(stoppedEvent)
+ return Disposables.create()
+ }
+
+ let key = self._observers.insert(observer.on)
+ observer.on(.next(self._element))
+
+ return SubscriptionDisposable(owner: self, key: key)
+ }
+
+ func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock()
+ self._synchronized_unsubscribe(disposeKey)
+ self._lock.unlock()
+ }
+
+ func _synchronized_unsubscribe(_ disposeKey: DisposeKey) {
+ if self._isDisposed {
+ return
+ }
+
+ _ = self._observers.removeKey(disposeKey)
+ }
+
+ /// Returns observer interface for subject.
+ public func asObserver() -> BehaviorSubject<Element> {
+ return self
+ }
+
+ /// Unsubscribe all observers and release resources.
+ public func dispose() {
+ self._lock.lock()
+ self._isDisposed = true
+ self._observers.removeAll()
+ self._stoppedEvent = nil
+ self._lock.unlock()
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
--- /dev/null
+//
+// BinaryDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents two disposable resources that are disposed together.
+private final class BinaryDisposable : DisposeBase, Cancelable {
+
+ private let _isDisposed = AtomicInt(0)
+
+ // state
+ private var _disposable1: Disposable?
+ private var _disposable2: Disposable?
+
+ /// - returns: Was resource disposed.
+ var isDisposed: Bool {
+ return isFlagSet(self._isDisposed, 1)
+ }
+
+ /// Constructs new binary disposable from two disposables.
+ ///
+ /// - parameter disposable1: First disposable
+ /// - parameter disposable2: Second disposable
+ init(_ disposable1: Disposable, _ disposable2: Disposable) {
+ self._disposable1 = disposable1
+ self._disposable2 = disposable2
+ super.init()
+ }
+
+ /// Calls the disposal action if and only if the current instance hasn't been disposed yet.
+ ///
+ /// After invoking disposal action, disposal action will be dereferenced.
+ func dispose() {
+ if fetchOr(self._isDisposed, 1) == 0 {
+ self._disposable1?.dispose()
+ self._disposable2?.dispose()
+ self._disposable1 = nil
+ self._disposable2 = nil
+ }
+ }
+}
+
+extension Disposables {
+
+ /// Creates a disposable with the given disposables.
+ public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable {
+ return BinaryDisposable(disposable1, disposable2)
+ }
+
+}
--- /dev/null
+//
+// BooleanDisposable.swift
+// RxSwift
+//
+// Created by Junior B. on 10/29/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a disposable resource that can be checked for disposal status.
+public final class BooleanDisposable : Cancelable {
+
+ internal static let BooleanDisposableTrue = BooleanDisposable(isDisposed: true)
+ private var _isDisposed = false
+
+ /// Initializes a new instance of the `BooleanDisposable` class
+ public init() {
+ }
+
+ /// Initializes a new instance of the `BooleanDisposable` class with given value
+ public init(isDisposed: Bool) {
+ self._isDisposed = isDisposed
+ }
+
+ /// - returns: Was resource disposed.
+ public var isDisposed: Bool {
+ return self._isDisposed
+ }
+
+ /// Sets the status to disposed, which can be observer through the `isDisposed` property.
+ public func dispose() {
+ self._isDisposed = true
+ }
+}
--- /dev/null
+//
+// Buffer.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/13/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence into a buffer that's sent out when either it's full or a given amount of time has elapsed, using the specified scheduler to run timers.
+
+ A useful real-world analogy of this overload is the behavior of a ferry leaving the dock when all seats are taken, or at the scheduled time of departure, whichever event occurs first.
+
+ - seealso: [buffer operator on reactivex.io](http://reactivex.io/documentation/operators/buffer.html)
+
+ - parameter timeSpan: Maximum time length of a buffer.
+ - parameter count: Maximum element count of a buffer.
+ - parameter scheduler: Scheduler to run buffering timers on.
+ - returns: An observable sequence of buffers.
+ */
+ public func buffer(timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType)
+ -> Observable<[Element]> {
+ return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler)
+ }
+}
+
+final private class BufferTimeCount<Element>: Producer<[Element]> {
+
+ fileprivate let _timeSpan: RxTimeInterval
+ fileprivate let _count: Int
+ fileprivate let _scheduler: SchedulerType
+ fileprivate let _source: Observable<Element>
+
+ init(source: Observable<Element>, timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) {
+ self._source = source
+ self._timeSpan = timeSpan
+ self._count = count
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == [Element] {
+ let sink = BufferTimeCountSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class BufferTimeCountSink<Element, Observer: ObserverType>
+ : Sink<Observer>
+ , LockOwnerType
+ , ObserverType
+ , SynchronizedOnType where Observer.Element == [Element] {
+ typealias Parent = BufferTimeCount<Element>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ // state
+ private let _timerD = SerialDisposable()
+ private var _buffer = [Element]()
+ private var _windowID = 0
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ self.createTimer(self._windowID)
+ return Disposables.create(_timerD, _parent._source.subscribe(self))
+ }
+
+ func startNewWindowAndSendCurrentOne() {
+ self._windowID = self._windowID &+ 1
+ let windowID = self._windowID
+
+ let buffer = self._buffer
+ self._buffer = []
+ self.forwardOn(.next(buffer))
+
+ self.createTimer(windowID)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ self._buffer.append(element)
+
+ if self._buffer.count == self._parent._count {
+ self.startNewWindowAndSendCurrentOne()
+ }
+
+ case .error(let error):
+ self._buffer = []
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.next(self._buffer))
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+
+ func createTimer(_ windowID: Int) {
+ if self._timerD.isDisposed {
+ return
+ }
+
+ if self._windowID != windowID {
+ return
+ }
+
+ let nextTimer = SingleAssignmentDisposable()
+
+ self._timerD.disposable = nextTimer
+
+ let disposable = self._parent._scheduler.scheduleRelative(windowID, dueTime: self._parent._timeSpan) { previousWindowID in
+ self._lock.performLocked {
+ if previousWindowID != self._windowID {
+ return
+ }
+
+ self.startNewWindowAndSendCurrentOne()
+ }
+
+ return Disposables.create()
+ }
+
+ nextTimer.setDisposable(disposable)
+ }
+}
--- /dev/null
+//
+// Cancelable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents disposable resource with state tracking.
+public protocol Cancelable : Disposable {
+ /// Was resource disposed.
+ var isDisposed: Bool { get }
+}
--- /dev/null
+//
+// Catch.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/19/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Continues an observable sequence that is terminated by an error with the observable sequence produced by the handler.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - parameter handler: Error handler function, producing another observable sequence.
+ - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred.
+ */
+ public func catchError(_ handler: @escaping (Swift.Error) throws -> Observable<Element>)
+ -> Observable<Element> {
+ return Catch(source: self.asObservable(), handler: handler)
+ }
+
+ /**
+ Continues an observable sequence that is terminated by an error with a single element.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - parameter element: Last element in an observable sequence in case error occurs.
+ - returns: An observable sequence containing the source sequence's elements, followed by the `element` in case an error occurred.
+ */
+ public func catchErrorJustReturn(_ element: Element)
+ -> Observable<Element> {
+ return Catch(source: self.asObservable(), handler: { _ in Observable.just(element) })
+ }
+
+}
+
+extension ObservableType {
+ /**
+ Continues an observable sequence that is terminated by an error with the next observable sequence.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - returns: An observable sequence containing elements from consecutive source sequences until a source sequence terminates successfully.
+ */
+ public static func catchError<Sequence: Swift.Sequence>(_ sequence: Sequence) -> Observable<Element>
+ where Sequence.Element == Observable<Element> {
+ return CatchSequence(sources: sequence)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Repeats the source observable sequence until it successfully terminates.
+
+ **This could potentially create an infinite sequence.**
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - returns: Observable sequence to repeat until it successfully terminates.
+ */
+ public func retry() -> Observable<Element> {
+ return CatchSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()))
+ }
+
+ /**
+ Repeats the source observable sequence the specified number of times in case of an error or until it successfully terminates.
+
+ If you encounter an error and want it to retry once, then you must use `retry(2)`
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter maxAttemptCount: Maximum number of times to repeat the sequence.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully.
+ */
+ public func retry(_ maxAttemptCount: Int)
+ -> Observable<Element> {
+ return CatchSequence(sources: Swift.repeatElement(self.asObservable(), count: maxAttemptCount))
+ }
+}
+
+// catch with callback
+
+final private class CatchSinkProxy<Observer: ObserverType>: ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = CatchSink<Observer>
+
+ private let _parent: Parent
+
+ init(parent: Parent) {
+ self._parent = parent
+ }
+
+ func on(_ event: Event<Element>) {
+ self._parent.forwardOn(event)
+
+ switch event {
+ case .next:
+ break
+ case .error, .completed:
+ self._parent.dispose()
+ }
+ }
+}
+
+final private class CatchSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = Catch<Element>
+
+ private let _parent: Parent
+ private let _subscription = SerialDisposable()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let d1 = SingleAssignmentDisposable()
+ self._subscription.disposable = d1
+ d1.setDisposable(self._parent._source.subscribe(self))
+
+ return self._subscription
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ case .error(let error):
+ do {
+ let catchSequence = try self._parent._handler(error)
+
+ let observer = CatchSinkProxy(parent: self)
+
+ self._subscription.disposable = catchSequence.subscribe(observer)
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ }
+ }
+}
+
+final private class Catch<Element>: Producer<Element> {
+ typealias Handler = (Swift.Error) throws -> Observable<Element>
+
+ fileprivate let _source: Observable<Element>
+ fileprivate let _handler: Handler
+
+ init(source: Observable<Element>, handler: @escaping Handler) {
+ self._source = source
+ self._handler = handler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = CatchSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+// catch enumerable
+
+final private class CatchSequenceSink<Sequence: Swift.Sequence, Observer: ObserverType>
+ : TailRecursiveSink<Sequence, Observer>
+ , ObserverType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias Parent = CatchSequence<Sequence>
+
+ private var _lastError: Swift.Error?
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .error(let error):
+ self._lastError = error
+ self.schedule(.moveNext)
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+ override func subscribeToNext(_ source: Observable<Element>) -> Disposable {
+ return source.subscribe(self)
+ }
+
+ override func done() {
+ if let lastError = self._lastError {
+ self.forwardOn(.error(lastError))
+ }
+ else {
+ self.forwardOn(.completed)
+ }
+
+ self.dispose()
+ }
+
+ override func extract(_ observable: Observable<Element>) -> SequenceGenerator? {
+ if let onError = observable as? CatchSequence<Sequence> {
+ return (onError.sources.makeIterator(), nil)
+ }
+ else {
+ return nil
+ }
+ }
+}
+
+final private class CatchSequence<Sequence: Swift.Sequence>: Producer<Sequence.Element.Element> where Sequence.Element: ObservableConvertibleType {
+ typealias Element = Sequence.Element.Element
+
+ let sources: Sequence
+
+ init(sources: Sequence) {
+ self.sources = sources
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = CatchSequenceSink<Sequence, Observer>(observer: observer, cancel: cancel)
+ let subscription = sink.run((self.sources.makeIterator(), nil))
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// CombineLatest+Collection.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/29/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combinelatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<Collection: Swift.Collection>(_ collection: Collection, resultSelector: @escaping ([Collection.Element.Element]) throws -> Element) -> Observable<Element>
+ where Collection.Element: ObservableType {
+ return CombineLatestCollectionType(sources: collection, resultSelector: resultSelector)
+ }
+
+ /**
+ Merges the specified observable sequences into one observable sequence whenever any of the observable sequences produces an element.
+
+ - seealso: [combinelatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<Collection: Swift.Collection>(_ collection: Collection) -> Observable<[Element]>
+ where Collection.Element: ObservableType, Collection.Element.Element == Element {
+ return CombineLatestCollectionType(sources: collection, resultSelector: { $0 })
+ }
+}
+
+final private class CombineLatestCollectionTypeSink<Collection: Swift.Collection, Observer: ObserverType>
+ : Sink<Observer> where Collection.Element: ObservableConvertibleType {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatestCollectionType<Collection, Result>
+ typealias SourceElement = Collection.Element.Element
+
+ let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ // state
+ var _numberOfValues = 0
+ var _values: [SourceElement?]
+ var _isDone: [Bool]
+ var _numberOfDone = 0
+ var _subscriptions: [SingleAssignmentDisposable]
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._values = [SourceElement?](repeating: nil, count: parent._count)
+ self._isDone = [Bool](repeating: false, count: parent._count)
+ self._subscriptions = [SingleAssignmentDisposable]()
+ self._subscriptions.reserveCapacity(parent._count)
+
+ for _ in 0 ..< parent._count {
+ self._subscriptions.append(SingleAssignmentDisposable())
+ }
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceElement>, atIndex: Int) {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ switch event {
+ case .next(let element):
+ if self._values[atIndex] == nil {
+ self._numberOfValues += 1
+ }
+
+ self._values[atIndex] = element
+
+ if self._numberOfValues < self._parent._count {
+ let numberOfOthersThatAreDone = self._numberOfDone - (self._isDone[atIndex] ? 1 : 0)
+ if numberOfOthersThatAreDone == self._parent._count - 1 {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ return
+ }
+
+ do {
+ let result = try self._parent._resultSelector(self._values.map { $0! })
+ self.forwardOn(.next(result))
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ if self._isDone[atIndex] {
+ return
+ }
+
+ self._isDone[atIndex] = true
+ self._numberOfDone += 1
+
+ if self._numberOfDone == self._parent._count {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ else {
+ self._subscriptions[atIndex].dispose()
+ }
+ }
+ // }
+ }
+
+ func run() -> Disposable {
+ var j = 0
+ for i in self._parent._sources {
+ let index = j
+ let source = i.asObservable()
+ let disposable = source.subscribe(AnyObserver { event in
+ self.on(event, atIndex: index)
+ })
+
+ self._subscriptions[j].setDisposable(disposable)
+
+ j += 1
+ }
+
+ if self._parent._sources.isEmpty {
+ do {
+ let result = try self._parent._resultSelector([])
+ self.forwardOn(.next(result))
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+
+ return Disposables.create(_subscriptions)
+ }
+}
+
+final private class CombineLatestCollectionType<Collection: Swift.Collection, Result>: Producer<Result> where Collection.Element: ObservableConvertibleType {
+ typealias ResultSelector = ([Collection.Element.Element]) throws -> Result
+
+ let _sources: Collection
+ let _resultSelector: ResultSelector
+ let _count: Int
+
+ init(sources: Collection, resultSelector: @escaping ResultSelector) {
+ self._sources = sources
+ self._resultSelector = resultSelector
+ self._count = self._sources.count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestCollectionTypeSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+// This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project
+//
+// CombineLatest+arity.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/22/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+
+
+// 2
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType>
+ (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest2(
+ source1: source1.asObservable(), source2: source2.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType>
+ (_ source1: O1, _ source2: O2)
+ -> Observable<(O1.Element, O2.Element)> {
+ return CombineLatest2(
+ source1: source1.asObservable(), source2: source2.asObservable(),
+ resultSelector: { ($0, $1) }
+ )
+ }
+}
+
+final class CombineLatestSink2_<E1, E2, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest2<E1, E2, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 2, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+
+ return Disposables.create([
+ subscription1,
+ subscription2
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2)
+ }
+}
+
+final class CombineLatest2<E1, E2, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2) throws -> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink2_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 3
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest3(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3)
+ -> Observable<(O1.Element, O2.Element, O3.Element)> {
+ return CombineLatest3(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(),
+ resultSelector: { ($0, $1, $2) }
+ )
+ }
+}
+
+final class CombineLatestSink3_<E1, E2, E3, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest3<E1, E2, E3, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 3, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3)
+ }
+}
+
+final class CombineLatest3<E1, E2, E3, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink3_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 4
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest4(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element)> {
+ return CombineLatest4(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(),
+ resultSelector: { ($0, $1, $2, $3) }
+ )
+ }
+}
+
+final class CombineLatestSink4_<E1, E2, E3, E4, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest4<E1, E2, E3, E4, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+ var _latestElement4: E4! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 4, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+ let observer4 = CombineLatestObserver(lock: self._lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent._source4.subscribe(observer4))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3, self._latestElement4)
+ }
+}
+
+final class CombineLatest4<E1, E2, E3, E4, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+ let _source4: Observable<E4>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+ self._source4 = source4
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink4_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 5
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest5(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)> {
+ return CombineLatest5(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4) }
+ )
+ }
+}
+
+final class CombineLatestSink5_<E1, E2, E3, E4, E5, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest5<E1, E2, E3, E4, E5, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+ var _latestElement4: E4! = nil
+ var _latestElement5: E5! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 5, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+ let observer4 = CombineLatestObserver(lock: self._lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
+ let observer5 = CombineLatestObserver(lock: self._lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent._source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent._source5.subscribe(observer5))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3, self._latestElement4, self._latestElement5)
+ }
+}
+
+final class CombineLatest5<E1, E2, E3, E4, E5, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+ let _source4: Observable<E4>
+ let _source5: Observable<E5>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+ self._source4 = source4
+ self._source5 = source5
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink5_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 6
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest6(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)> {
+ return CombineLatest6(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5) }
+ )
+ }
+}
+
+final class CombineLatestSink6_<E1, E2, E3, E4, E5, E6, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest6<E1, E2, E3, E4, E5, E6, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+ var _latestElement4: E4! = nil
+ var _latestElement5: E5! = nil
+ var _latestElement6: E6! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 6, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+ let observer4 = CombineLatestObserver(lock: self._lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
+ let observer5 = CombineLatestObserver(lock: self._lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
+ let observer6 = CombineLatestObserver(lock: self._lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent._source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent._source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent._source6.subscribe(observer6))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3, self._latestElement4, self._latestElement5, self._latestElement6)
+ }
+}
+
+final class CombineLatest6<E1, E2, E3, E4, E5, E6, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+ let _source4: Observable<E4>
+ let _source5: Observable<E5>
+ let _source6: Observable<E6>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+ self._source4 = source4
+ self._source5 = source5
+ self._source6 = source6
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink6_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 7
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest7(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)> {
+ return CombineLatest7(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5, $6) }
+ )
+ }
+}
+
+final class CombineLatestSink7_<E1, E2, E3, E4, E5, E6, E7, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest7<E1, E2, E3, E4, E5, E6, E7, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+ var _latestElement4: E4! = nil
+ var _latestElement5: E5! = nil
+ var _latestElement6: E6! = nil
+ var _latestElement7: E7! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 7, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+ let subscription7 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+ let observer4 = CombineLatestObserver(lock: self._lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
+ let observer5 = CombineLatestObserver(lock: self._lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
+ let observer6 = CombineLatestObserver(lock: self._lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
+ let observer7 = CombineLatestObserver(lock: self._lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent._source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent._source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent._source6.subscribe(observer6))
+ subscription7.setDisposable(self._parent._source7.subscribe(observer7))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6,
+ subscription7
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3, self._latestElement4, self._latestElement5, self._latestElement6, self._latestElement7)
+ }
+}
+
+final class CombineLatest7<E1, E2, E3, E4, E5, E6, E7, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+ let _source4: Observable<E4>
+ let _source5: Observable<E5>
+ let _source6: Observable<E6>
+ let _source7: Observable<E7>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+ self._source4 = source4
+ self._source5 = source5
+ self._source6 = source6
+ self._source7 = source7
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink7_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 8
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter resultSelector: Function to invoke whenever any of the sources produces an element.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType, O8: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element)
+ -> Observable<Element> {
+ return CombineLatest8(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever any of the observable sequences produces an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType, O8: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)> {
+ return CombineLatest8(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5, $6, $7) }
+ )
+ }
+}
+
+final class CombineLatestSink8_<E1, E2, E3, E4, E5, E6, E7, E8, Observer: ObserverType> : CombineLatestSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = CombineLatest8<E1, E2, E3, E4, E5, E6, E7, E8, Result>
+
+ let _parent: Parent
+
+ var _latestElement1: E1! = nil
+ var _latestElement2: E2! = nil
+ var _latestElement3: E3! = nil
+ var _latestElement4: E4! = nil
+ var _latestElement5: E5! = nil
+ var _latestElement6: E6! = nil
+ var _latestElement7: E7! = nil
+ var _latestElement8: E8! = nil
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 8, observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+ let subscription7 = SingleAssignmentDisposable()
+ let subscription8 = SingleAssignmentDisposable()
+
+ let observer1 = CombineLatestObserver(lock: self._lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
+ let observer2 = CombineLatestObserver(lock: self._lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
+ let observer3 = CombineLatestObserver(lock: self._lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
+ let observer4 = CombineLatestObserver(lock: self._lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
+ let observer5 = CombineLatestObserver(lock: self._lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
+ let observer6 = CombineLatestObserver(lock: self._lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
+ let observer7 = CombineLatestObserver(lock: self._lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7)
+ let observer8 = CombineLatestObserver(lock: self._lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8)
+
+ subscription1.setDisposable(self._parent._source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent._source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent._source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent._source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent._source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent._source6.subscribe(observer6))
+ subscription7.setDisposable(self._parent._source7.subscribe(observer7))
+ subscription8.setDisposable(self._parent._source8.subscribe(observer8))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6,
+ subscription7,
+ subscription8
+ ])
+ }
+
+ override func getResult() throws-> Result {
+ return try self._parent._resultSelector(self._latestElement1, self._latestElement2, self._latestElement3, self._latestElement4, self._latestElement5, self._latestElement6, self._latestElement7, self._latestElement8)
+ }
+}
+
+final class CombineLatest8<E1, E2, E3, E4, E5, E6, E7, E8, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws-> Result
+
+ let _source1: Observable<E1>
+ let _source2: Observable<E2>
+ let _source3: Observable<E3>
+ let _source4: Observable<E4>
+ let _source5: Observable<E5>
+ let _source6: Observable<E6>
+ let _source7: Observable<E7>
+ let _source8: Observable<E8>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, source8: Observable<E8>, resultSelector: @escaping ResultSelector) {
+ self._source1 = source1
+ self._source2 = source2
+ self._source3 = source3
+ self._source4 = source4
+ self._source5 = source5
+ self._source6 = source6
+ self._source7 = source7
+ self._source8 = source8
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = CombineLatestSink8_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
--- /dev/null
+//
+// CombineLatest.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol CombineLatestProtocol : class {
+ func next(_ index: Int)
+ func fail(_ error: Swift.Error)
+ func done(_ index: Int)
+}
+
+class CombineLatestSink<Observer: ObserverType>
+ : Sink<Observer>
+ , CombineLatestProtocol {
+ typealias Element = Observer.Element
+
+ let _lock = RecursiveLock()
+
+ private let _arity: Int
+ private var _numberOfValues = 0
+ private var _numberOfDone = 0
+ private var _hasValue: [Bool]
+ private var _isDone: [Bool]
+
+ init(arity: Int, observer: Observer, cancel: Cancelable) {
+ self._arity = arity
+ self._hasValue = [Bool](repeating: false, count: arity)
+ self._isDone = [Bool](repeating: false, count: arity)
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func getResult() throws -> Element {
+ rxAbstractMethod()
+ }
+
+ func next(_ index: Int) {
+ if !self._hasValue[index] {
+ self._hasValue[index] = true
+ self._numberOfValues += 1
+ }
+
+ if self._numberOfValues == self._arity {
+ do {
+ let result = try self.getResult()
+ self.forwardOn(.next(result))
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ }
+ else {
+ var allOthersDone = true
+
+ for i in 0 ..< self._arity {
+ if i != index && !self._isDone[i] {
+ allOthersDone = false
+ break
+ }
+ }
+
+ if allOthersDone {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ }
+
+ func fail(_ error: Swift.Error) {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+
+ func done(_ index: Int) {
+ if self._isDone[index] {
+ return
+ }
+
+ self._isDone[index] = true
+ self._numberOfDone += 1
+
+ if self._numberOfDone == self._arity {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final class CombineLatestObserver<Element>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias ValueSetter = (Element) -> Void
+
+ private let _parent: CombineLatestProtocol
+
+ let _lock: RecursiveLock
+ private let _index: Int
+ private let _this: Disposable
+ private let _setLatestValue: ValueSetter
+
+ init(lock: RecursiveLock, parent: CombineLatestProtocol, index: Int, setLatestValue: @escaping ValueSetter, this: Disposable) {
+ self._lock = lock
+ self._parent = parent
+ self._index = index
+ self._this = this
+ self._setLatestValue = setLatestValue
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ self._setLatestValue(value)
+ self._parent.next(self._index)
+ case .error(let error):
+ self._this.dispose()
+ self._parent.fail(error)
+ case .completed:
+ self._this.dispose()
+ self._parent.done(self._index)
+ }
+ }
+}
--- /dev/null
+//
+// CompactMap.swift
+// RxSwift
+//
+// Created by Michael Long on 04/09/2019.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence into an optional form and filters all optional results.
+
+ Equivalent to:
+
+ func compactMap<Result>(_ transform: @escaping (Self.E) throws -> Result?) -> RxSwift.Observable<Result> {
+ return self.map { try? transform($0) }.filter { $0 != nil }.map { $0! }
+ }
+
+ - parameter transform: A transform function to apply to each source element and which returns an element or nil.
+ - returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source.
+
+ */
+ public func compactMap<Result>(_ transform: @escaping (Element) throws -> Result?)
+ -> Observable<Result> {
+ return CompactMap(source: self.asObservable(), transform: transform)
+ }
+}
+
+final private class CompactMapSink<SourceType, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Transform = (SourceType) throws -> ResultType?
+
+ typealias ResultType = Observer.Element
+ typealias Element = SourceType
+
+ private let _transform: Transform
+
+ init(transform: @escaping Transform, observer: Observer, cancel: Cancelable) {
+ self._transform = transform
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case .next(let element):
+ do {
+ if let mappedElement = try self._transform(element) {
+ self.forwardOn(.next(mappedElement))
+ }
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class CompactMap<SourceType, ResultType>: Producer<ResultType> {
+ typealias Transform = (SourceType) throws -> ResultType?
+
+ private let _source: Observable<SourceType>
+
+ private let _transform: Transform
+
+ init(source: Observable<SourceType>, transform: @escaping Transform) {
+ self._source = source
+ self._transform = transform
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == ResultType {
+ let sink = CompactMapSink(transform: self._transform, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Completable+AndThen.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 7/2/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+extension PrimitiveSequenceType where Trait == CompletableTrait, Element == Never {
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func andThen<Element>(_ second: Single<Element>) -> Single<Element> {
+ let completable = self.primitiveSequence.asObservable()
+ return Single(raw: ConcatCompletable(completable: completable, second: second.asObservable()))
+ }
+
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func andThen<Element>(_ second: Maybe<Element>) -> Maybe<Element> {
+ let completable = self.primitiveSequence.asObservable()
+ return Maybe(raw: ConcatCompletable(completable: completable, second: second.asObservable()))
+ }
+
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func andThen(_ second: Completable) -> Completable {
+ let completable = self.primitiveSequence.asObservable()
+ return Completable(raw: ConcatCompletable(completable: completable, second: second.asObservable()))
+ }
+
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func andThen<Element>(_ second: Observable<Element>) -> Observable<Element> {
+ let completable = self.primitiveSequence.asObservable()
+ return ConcatCompletable(completable: completable, second: second.asObservable())
+ }
+}
+
+final private class ConcatCompletable<Element>: Producer<Element> {
+ fileprivate let _completable: Observable<Never>
+ fileprivate let _second: Observable<Element>
+
+ init(completable: Observable<Never>, second: Observable<Element>) {
+ self._completable = completable
+ self._second = second
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ConcatCompletableSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class ConcatCompletableSink<Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType {
+ typealias Element = Never
+ typealias Parent = ConcatCompletable<Observer.Element>
+
+ private let _parent: Parent
+ private let _subscription = SerialDisposable()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .next:
+ break
+ case .completed:
+ let otherSink = ConcatCompletableSinkOther(parent: self)
+ self._subscription.disposable = self._parent._second.subscribe(otherSink)
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription = SingleAssignmentDisposable()
+ self._subscription.disposable = subscription
+ subscription.setDisposable(self._parent._completable.subscribe(self))
+ return self._subscription
+ }
+}
+
+final private class ConcatCompletableSinkOther<Observer: ObserverType>
+ : ObserverType {
+ typealias Element = Observer.Element
+
+ typealias Parent = ConcatCompletableSink<Observer>
+
+ private let _parent: Parent
+
+ init(parent: Parent) {
+ self._parent = parent
+ }
+
+ func on(_ event: Event<Observer.Element>) {
+ self._parent.forwardOn(event)
+ if event.isStopEvent {
+ self._parent.dispose()
+ }
+ }
+}
--- /dev/null
+//
+// Completable.swift
+// RxSwift
+//
+// Created by sergdort on 19/08/2017.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+#if DEBUG
+import Foundation
+#endif
+
+/// Sequence containing 0 elements
+public enum CompletableTrait { }
+/// Represents a push style sequence containing 0 elements.
+public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
+
+public enum CompletableEvent {
+ /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
+ case error(Swift.Error)
+
+ /// Sequence completed successfully.
+ case completed
+}
+
+extension PrimitiveSequenceType where Trait == CompletableTrait, Element == Swift.Never {
+ public typealias CompletableObserver = (CompletableEvent) -> Void
+
+ /**
+ Creates an observable sequence from a specified subscribe method implementation.
+
+ - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
+
+ - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
+ - returns: The observable sequence with the specified implementation for the `subscribe` method.
+ */
+ public static func create(subscribe: @escaping (@escaping CompletableObserver) -> Disposable) -> PrimitiveSequence<Trait, Element> {
+ let source = Observable<Element>.create { observer in
+ return subscribe { event in
+ switch event {
+ case .error(let error):
+ observer.on(.error(error))
+ case .completed:
+ observer.on(.completed)
+ }
+ }
+ }
+
+ return PrimitiveSequence(raw: source)
+ }
+
+ /**
+ Subscribes `observer` to receive events for this sequence.
+
+ - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources.
+ */
+ public func subscribe(_ observer: @escaping (CompletableEvent) -> Void) -> Disposable {
+ var stopped = false
+ return self.primitiveSequence.asObservable().subscribe { event in
+ if stopped { return }
+ stopped = true
+
+ switch event {
+ case .next:
+ rxFatalError("Completables can't emit values")
+ case .error(let error):
+ observer(.error(error))
+ case .completed:
+ observer(.completed)
+ }
+ }
+ }
+
+ /**
+ Subscribes a completion handler and an error handler for this sequence.
+
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - returns: Subscription object used to unsubscribe from the observable sequence.
+ */
+ public func subscribe(onCompleted: (() -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil) -> Disposable {
+ #if DEBUG
+ let callStack = Hooks.recordCallStackOnError ? Thread.callStackSymbols : []
+ #else
+ let callStack = [String]()
+ #endif
+
+ return self.primitiveSequence.subscribe { event in
+ switch event {
+ case .error(let error):
+ if let onError = onError {
+ onError(error)
+ } else {
+ Hooks.defaultErrorHandler(callStack, error)
+ }
+ case .completed:
+ onCompleted?()
+ }
+ }
+ }
+}
+
+extension PrimitiveSequenceType where Trait == CompletableTrait, Element == Swift.Never {
+ /**
+ Returns an observable sequence that terminates with an `error`.
+
+ - seealso: [throw operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: The observable sequence that terminates with specified error.
+ */
+ public static func error(_ error: Swift.Error) -> Completable {
+ return PrimitiveSequence(raw: Observable.error(error))
+ }
+
+ /**
+ Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
+
+ - seealso: [never operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence whose observers will never get called.
+ */
+ public static func never() -> Completable {
+ return PrimitiveSequence(raw: Observable.never())
+ }
+
+ /**
+ Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message.
+
+ - seealso: [empty operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence with no elements.
+ */
+ public static func empty() -> Completable {
+ return Completable(raw: Observable.empty())
+ }
+
+}
+
+extension PrimitiveSequenceType where Trait == CompletableTrait, Element == Swift.Never {
+ /**
+ Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter onNext: Action to invoke for each element in the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter afterError: Action to invoke after errored termination of the observable sequence.
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - parameter afterCompleted: Action to invoke after graceful termination of the observable sequence.
+ - parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
+ - parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
+ - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
+ - returns: The source sequence with the side-effecting behavior applied.
+ */
+ public func `do`(onError: ((Swift.Error) throws -> Void)? = nil,
+ afterError: ((Swift.Error) throws -> Void)? = nil,
+ onCompleted: (() throws -> Void)? = nil,
+ afterCompleted: (() throws -> Void)? = nil,
+ onSubscribe: (() -> Void)? = nil,
+ onSubscribed: (() -> Void)? = nil,
+ onDispose: (() -> Void)? = nil)
+ -> Completable {
+ return Completable(raw: self.primitiveSequence.source.do(
+ onError: onError,
+ afterError: afterError,
+ onCompleted: onCompleted,
+ afterCompleted: afterCompleted,
+ onSubscribe: onSubscribe,
+ onSubscribed: onSubscribed,
+ onDispose: onDispose)
+ )
+ }
+
+
+
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func concat(_ second: Completable) -> Completable {
+ return Completable.concat(self.primitiveSequence, second)
+ }
+
+ /**
+ Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat<Sequence: Swift.Sequence>(_ sequence: Sequence) -> Completable
+ where Sequence.Element == Completable {
+ let source = Observable.concat(sequence.lazy.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+
+ /**
+ Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat<Collection: Swift.Collection>(_ collection: Collection) -> Completable
+ where Collection.Element == Completable {
+ let source = Observable.concat(collection.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+
+ /**
+ Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat(_ sources: Completable ...) -> Completable {
+ let source = Observable.concat(sources.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+
+ /**
+ Merges the completion of all Completables from a collection into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+ - note: For `Completable`, `zip` is an alias for `merge`.
+
+ - parameter sources: Collection of Completables to merge.
+ - returns: A Completable that merges the completion of all Completables.
+ */
+ public static func zip<Collection: Swift.Collection>(_ sources: Collection) -> Completable
+ where Collection.Element == Completable {
+ let source = Observable.merge(sources.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+
+ /**
+ Merges the completion of all Completables from an array into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+ - note: For `Completable`, `zip` is an alias for `merge`.
+
+ - parameter sources: Array of observable sequences to merge.
+ - returns: A Completable that merges the completion of all Completables.
+ */
+ public static func zip(_ sources: [Completable]) -> Completable {
+ let source = Observable.merge(sources.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+
+ /**
+ Merges the completion of all Completables into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+ - note: For `Completable`, `zip` is an alias for `merge`.
+
+ - parameter sources: Collection of observable sequences to merge.
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ public static func zip(_ sources: Completable...) -> Completable {
+ let source = Observable.merge(sources.map { $0.asObservable() })
+ return Completable(raw: source)
+ }
+}
--- /dev/null
+//
+// CompositeDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/20/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a group of disposable resources that are disposed together.
+public final class CompositeDisposable : DisposeBase, Cancelable {
+ /// Key used to remove disposable from composite disposable
+ public struct DisposeKey {
+ fileprivate let key: BagKey
+ fileprivate init(key: BagKey) {
+ self.key = key
+ }
+ }
+
+ private var _lock = SpinLock()
+
+ // state
+ private var _disposables: Bag<Disposable>? = Bag()
+
+ public var isDisposed: Bool {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._disposables == nil
+ }
+
+ public override init() {
+ }
+
+ /// Initializes a new instance of composite disposable with the specified number of disposables.
+ public init(_ disposable1: Disposable, _ disposable2: Disposable) {
+ // This overload is here to make sure we are using optimized version up to 4 arguments.
+ _ = self._disposables!.insert(disposable1)
+ _ = self._disposables!.insert(disposable2)
+ }
+
+ /// Initializes a new instance of composite disposable with the specified number of disposables.
+ public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) {
+ // This overload is here to make sure we are using optimized version up to 4 arguments.
+ _ = self._disposables!.insert(disposable1)
+ _ = self._disposables!.insert(disposable2)
+ _ = self._disposables!.insert(disposable3)
+ }
+
+ /// Initializes a new instance of composite disposable with the specified number of disposables.
+ public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposable4: Disposable, _ disposables: Disposable...) {
+ // This overload is here to make sure we are using optimized version up to 4 arguments.
+ _ = self._disposables!.insert(disposable1)
+ _ = self._disposables!.insert(disposable2)
+ _ = self._disposables!.insert(disposable3)
+ _ = self._disposables!.insert(disposable4)
+
+ for disposable in disposables {
+ _ = self._disposables!.insert(disposable)
+ }
+ }
+
+ /// Initializes a new instance of composite disposable with the specified number of disposables.
+ public init(disposables: [Disposable]) {
+ for disposable in disposables {
+ _ = self._disposables!.insert(disposable)
+ }
+ }
+
+ /**
+ Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed.
+
+ - parameter disposable: Disposable to add.
+ - returns: Key that can be used to remove disposable from composite disposable. In case dispose bag was already
+ disposed `nil` will be returned.
+ */
+ public func insert(_ disposable: Disposable) -> DisposeKey? {
+ let key = self._insert(disposable)
+
+ if key == nil {
+ disposable.dispose()
+ }
+
+ return key
+ }
+
+ private func _insert(_ disposable: Disposable) -> DisposeKey? {
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ let bagKey = self._disposables?.insert(disposable)
+ return bagKey.map(DisposeKey.init)
+ }
+
+ /// - returns: Gets the number of disposables contained in the `CompositeDisposable`.
+ public var count: Int {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._disposables?.count ?? 0
+ }
+
+ /// Removes and disposes the disposable identified by `disposeKey` from the CompositeDisposable.
+ ///
+ /// - parameter disposeKey: Key used to identify disposable to be removed.
+ public func remove(for disposeKey: DisposeKey) {
+ self._remove(for: disposeKey)?.dispose()
+ }
+
+ private func _remove(for disposeKey: DisposeKey) -> Disposable? {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._disposables?.removeKey(disposeKey.key)
+ }
+
+ /// Disposes all disposables in the group and removes them from the group.
+ public func dispose() {
+ if let disposables = self._dispose() {
+ disposeAll(in: disposables)
+ }
+ }
+
+ private func _dispose() -> Bag<Disposable>? {
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ let disposeBag = self._disposables
+ self._disposables = nil
+
+ return disposeBag
+ }
+}
+
+extension Disposables {
+
+ /// Creates a disposable with the given disposables.
+ public static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) -> Cancelable {
+ return CompositeDisposable(disposable1, disposable2, disposable3)
+ }
+
+ /// Creates a disposable with the given disposables.
+ public static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposables: Disposable ...) -> Cancelable {
+ var disposables = disposables
+ disposables.append(disposable1)
+ disposables.append(disposable2)
+ disposables.append(disposable3)
+ return CompositeDisposable(disposables: disposables)
+ }
+
+ /// Creates a disposable with the given disposables.
+ public static func create(_ disposables: [Disposable]) -> Cancelable {
+ switch disposables.count {
+ case 2:
+ return Disposables.create(disposables[0], disposables[1])
+ default:
+ return CompositeDisposable(disposables: disposables)
+ }
+ }
+}
--- /dev/null
+//
+// Concat.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Concatenates the second observable sequence to `self` upon successful termination of `self`.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - parameter second: Second observable sequence.
+ - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence.
+ */
+ public func concat<Source: ObservableConvertibleType>(_ second: Source) -> Observable<Element> where Source.Element == Element {
+ return Observable.concat([self.asObservable(), second.asObservable()])
+ }
+}
+
+extension ObservableType {
+ /**
+ Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully.
+
+ This operator has tail recursive optimizations that will prevent stack overflow.
+
+ Optimizations will be performed in cases equivalent to following:
+
+ [1, [2, [3, .....].concat()].concat].concat()
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat<Sequence: Swift.Sequence>(_ sequence: Sequence) -> Observable<Element>
+ where Sequence.Element == Observable<Element> {
+ return Concat(sources: sequence, count: nil)
+ }
+
+ /**
+ Concatenates all observable sequences in the given collection, as long as the previous observable sequence terminated successfully.
+
+ This operator has tail recursive optimizations that will prevent stack overflow.
+
+ Optimizations will be performed in cases equivalent to following:
+
+ [1, [2, [3, .....].concat()].concat].concat()
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat<Collection: Swift.Collection>(_ collection: Collection) -> Observable<Element>
+ where Collection.Element == Observable<Element> {
+ return Concat(sources: collection, count: Int64(collection.count))
+ }
+
+ /**
+ Concatenates all observable sequences in the given collection, as long as the previous observable sequence terminated successfully.
+
+ This operator has tail recursive optimizations that will prevent stack overflow.
+
+ Optimizations will be performed in cases equivalent to following:
+
+ [1, [2, [3, .....].concat()].concat].concat()
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each given sequence, in sequential order.
+ */
+ public static func concat(_ sources: Observable<Element> ...) -> Observable<Element> {
+ return Concat(sources: sources, count: Int64(sources.count))
+ }
+}
+
+final private class ConcatSink<Sequence: Swift.Sequence, Observer: ObserverType>
+ : TailRecursiveSink<Sequence, Observer>
+ , ObserverType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element {
+ typealias Element = Observer.Element
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>){
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.schedule(.moveNext)
+ }
+ }
+
+ override func subscribeToNext(_ source: Observable<Element>) -> Disposable {
+ return source.subscribe(self)
+ }
+
+ override func extract(_ observable: Observable<Element>) -> SequenceGenerator? {
+ if let source = observable as? Concat<Sequence> {
+ return (source._sources.makeIterator(), source._count)
+ }
+ else {
+ return nil
+ }
+ }
+}
+
+final private class Concat<Sequence: Swift.Sequence>: Producer<Sequence.Element.Element> where Sequence.Element: ObservableConvertibleType {
+ typealias Element = Sequence.Element.Element
+
+ fileprivate let _sources: Sequence
+ fileprivate let _count: IntMax?
+
+ init(sources: Sequence, count: IntMax?) {
+ self._sources = sources
+ self._count = count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ConcatSink<Sequence, Observer>(observer: observer, cancel: cancel)
+ let subscription = sink.run((self._sources.makeIterator(), self._count))
+ return (sink: sink, subscription: subscription)
+ }
+}
+
--- /dev/null
+//
+// ConcurrentDispatchQueueScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 7/5/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+import struct Foundation.TimeInterval
+import Dispatch
+
+/// Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems.
+///
+/// This scheduler is suitable when some work needs to be performed in background.
+public class ConcurrentDispatchQueueScheduler: SchedulerType {
+ public typealias TimeInterval = Foundation.TimeInterval
+ public typealias Time = Date
+
+ public var now : Date {
+ return Date()
+ }
+
+ let configuration: DispatchQueueConfiguration
+
+ /// Constructs new `ConcurrentDispatchQueueScheduler` that wraps `queue`.
+ ///
+ /// - parameter queue: Target dispatch queue.
+ /// - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ public init(queue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ self.configuration = DispatchQueueConfiguration(queue: queue, leeway: leeway)
+ }
+
+ /// Convenience init for scheduler that wraps one of the global concurrent dispatch queues.
+ ///
+ /// - parameter qos: Target global dispatch queue, by quality of service class.
+ /// - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ @available(iOS 8, OSX 10.10, *)
+ public convenience init(qos: DispatchQoS, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ self.init(queue: DispatchQueue(
+ label: "rxswift.queue.\(qos)",
+ qos: qos,
+ attributes: [DispatchQueue.Attributes.concurrent],
+ target: nil),
+ leeway: leeway
+ )
+ }
+
+ /**
+ Schedules an action to be executed immediately.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public final func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.configuration.schedule(state, action: action)
+ }
+
+ /**
+ Schedules an action to be executed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public final func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action)
+ }
+
+ /**
+ Schedules a periodic piece of work.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter startAfter: Period after which initial work should be run.
+ - parameter period: Period for running the work periodically.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
+ return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
+ }
+}
--- /dev/null
+//
+// ConcurrentMainScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/17/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+import struct Foundation.TimeInterval
+import Dispatch
+
+/**
+Abstracts work that needs to be performed on `MainThread`. In case `schedule` methods are called from main thread, it will perform action immediately without scheduling.
+
+This scheduler is optimized for `subscribeOn` operator. If you want to observe observable sequence elements on main thread using `observeOn` operator,
+`MainScheduler` is more suitable for that purpose.
+*/
+public final class ConcurrentMainScheduler : SchedulerType {
+ public typealias TimeInterval = Foundation.TimeInterval
+ public typealias Time = Date
+
+ private let _mainScheduler: MainScheduler
+ private let _mainQueue: DispatchQueue
+
+ /// - returns: Current time.
+ public var now: Date {
+ return self._mainScheduler.now as Date
+ }
+
+ private init(mainScheduler: MainScheduler) {
+ self._mainQueue = DispatchQueue.main
+ self._mainScheduler = mainScheduler
+ }
+
+ /// Singleton instance of `ConcurrentMainScheduler`
+ public static let instance = ConcurrentMainScheduler(mainScheduler: MainScheduler.instance)
+
+ /**
+ Schedules an action to be executed immediately.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ if DispatchQueue.isMain {
+ return action(state)
+ }
+
+ let cancel = SingleAssignmentDisposable()
+
+ self._mainQueue.async {
+ if cancel.isDisposed {
+ return
+ }
+
+ cancel.setDisposable(action(state))
+ }
+
+ return cancel
+ }
+
+ /**
+ Schedules an action to be executed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public final func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self._mainScheduler.scheduleRelative(state, dueTime: dueTime, action: action)
+ }
+
+ /**
+ Schedules a periodic piece of work.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter startAfter: Period after which initial work should be run.
+ - parameter period: Period for running the work periodically.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
+ return self._mainScheduler.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
+ }
+}
--- /dev/null
+//
+// ConnectableObservableType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/1/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+Represents an observable sequence wrapper that can be connected and disconnected from its underlying observable sequence.
+*/
+public protocol ConnectableObservableType : ObservableType {
+ /**
+ Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established.
+
+ - returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence.
+ */
+ func connect() -> Disposable
+}
--- /dev/null
+//
+// Create.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ // MARK: create
+
+ /**
+ Creates an observable sequence from a specified subscribe method implementation.
+
+ - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
+
+ - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
+ - returns: The observable sequence with the specified implementation for the `subscribe` method.
+ */
+ public static func create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element> {
+ return AnonymousObservable(subscribe)
+ }
+}
+
+final private class AnonymousObservableSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = AnonymousObservable<Element>
+
+ // state
+ private let _isStopped = AtomicInt(0)
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ switch event {
+ case .next:
+ if load(self._isStopped) == 1 {
+ return
+ }
+ self.forwardOn(event)
+ case .error, .completed:
+ if fetchOr(self._isStopped, 1) == 0 {
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+ }
+
+ func run(_ parent: Parent) -> Disposable {
+ return parent._subscribeHandler(AnyObserver(self))
+ }
+}
+
+final private class AnonymousObservable<Element>: Producer<Element> {
+ typealias SubscribeHandler = (AnyObserver<Element>) -> Disposable
+
+ let _subscribeHandler: SubscribeHandler
+
+ init(_ subscribeHandler: @escaping SubscribeHandler) {
+ self._subscribeHandler = subscribeHandler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
+ let subscription = sink.run(self)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// CurrentThreadScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import class Foundation.NSObject
+import protocol Foundation.NSCopying
+import class Foundation.Thread
+import Dispatch
+
+#if os(Linux)
+ import struct Foundation.pthread_key_t
+ import func Foundation.pthread_setspecific
+ import func Foundation.pthread_getspecific
+ import func Foundation.pthread_key_create
+
+ fileprivate enum CurrentThreadSchedulerQueueKey {
+ fileprivate static let instance = "RxSwift.CurrentThreadScheduler.Queue"
+ }
+#else
+ private class CurrentThreadSchedulerQueueKey: NSObject, NSCopying {
+ static let instance = CurrentThreadSchedulerQueueKey()
+ private override init() {
+ super.init()
+ }
+
+ override var hash: Int {
+ return 0
+ }
+
+ public func copy(with zone: NSZone? = nil) -> Any {
+ return self
+ }
+ }
+#endif
+
+/// Represents an object that schedules units of work on the current thread.
+///
+/// This is the default scheduler for operators that generate elements.
+///
+/// This scheduler is also sometimes called `trampoline scheduler`.
+public class CurrentThreadScheduler : ImmediateSchedulerType {
+ typealias ScheduleQueue = RxMutableBox<Queue<ScheduledItemType>>
+
+ /// The singleton instance of the current thread scheduler.
+ public static let instance = CurrentThreadScheduler()
+
+ private static var isScheduleRequiredKey: pthread_key_t = { () -> pthread_key_t in
+ let key = UnsafeMutablePointer<pthread_key_t>.allocate(capacity: 1)
+ defer { key.deallocate() }
+
+ guard pthread_key_create(key, nil) == 0 else {
+ rxFatalError("isScheduleRequired key creation failed")
+ }
+
+ return key.pointee
+ }()
+
+ private static var scheduleInProgressSentinel: UnsafeRawPointer = { () -> UnsafeRawPointer in
+ return UnsafeRawPointer(UnsafeMutablePointer<Int>.allocate(capacity: 1))
+ }()
+
+ static var queue : ScheduleQueue? {
+ get {
+ return Thread.getThreadLocalStorageValueForKey(CurrentThreadSchedulerQueueKey.instance)
+ }
+ set {
+ Thread.setThreadLocalStorageValue(newValue, forKey: CurrentThreadSchedulerQueueKey.instance)
+ }
+ }
+
+ /// Gets a value that indicates whether the caller must call a `schedule` method.
+ public static private(set) var isScheduleRequired: Bool {
+ get {
+ return pthread_getspecific(CurrentThreadScheduler.isScheduleRequiredKey) == nil
+ }
+ set(isScheduleRequired) {
+ if pthread_setspecific(CurrentThreadScheduler.isScheduleRequiredKey, isScheduleRequired ? nil : scheduleInProgressSentinel) != 0 {
+ rxFatalError("pthread_setspecific failed")
+ }
+ }
+ }
+
+ /**
+ Schedules an action to be executed as soon as possible on current thread.
+
+ If this method is called on some thread that doesn't have `CurrentThreadScheduler` installed, scheduler will be
+ automatically installed and uninstalled after all work is performed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ if CurrentThreadScheduler.isScheduleRequired {
+ CurrentThreadScheduler.isScheduleRequired = false
+
+ let disposable = action(state)
+
+ defer {
+ CurrentThreadScheduler.isScheduleRequired = true
+ CurrentThreadScheduler.queue = nil
+ }
+
+ guard let queue = CurrentThreadScheduler.queue else {
+ return disposable
+ }
+
+ while let latest = queue.value.dequeue() {
+ if latest.isDisposed {
+ continue
+ }
+ latest.invoke()
+ }
+
+ return disposable
+ }
+
+ let existingQueue = CurrentThreadScheduler.queue
+
+ let queue: RxMutableBox<Queue<ScheduledItemType>>
+ if let existingQueue = existingQueue {
+ queue = existingQueue
+ }
+ else {
+ queue = RxMutableBox(Queue<ScheduledItemType>(capacity: 1))
+ CurrentThreadScheduler.queue = queue
+ }
+
+ let scheduledItem = ScheduledItem(action: action, state: state)
+ queue.value.enqueue(scheduledItem)
+
+ return scheduledItem
+ }
+}
--- /dev/null
+//
+// Date+Dispatch.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/14/19.
+// Copyright © 2019 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+import struct Foundation.TimeInterval
+import enum Dispatch.DispatchTimeInterval
+
+extension DispatchTimeInterval {
+ var convertToSecondsFactor: Double {
+ switch self {
+ case .nanoseconds: return 1_000_000_000.0
+ case .microseconds: return 1_000_000.0
+ case .milliseconds: return 1_000.0
+ case .seconds: return 1.0
+ case .never: fatalError()
+ @unknown default: fatalError()
+ }
+ }
+
+ func map(_ transform: (Int, Double) -> Int) -> DispatchTimeInterval {
+ switch self {
+ case .nanoseconds(let value): return .nanoseconds(transform(value, 1_000_000_000.0))
+ case .microseconds(let value): return .microseconds(transform(value, 1_000_000.0))
+ case .milliseconds(let value): return .milliseconds(transform(value, 1_000.0))
+ case .seconds(let value): return .seconds(transform(value, 1.0))
+ case .never: return .never
+ @unknown default: fatalError()
+ }
+ }
+
+ var isNow: Bool {
+ switch self {
+ case .nanoseconds(let value), .microseconds(let value), .milliseconds(let value), .seconds(let value): return value == 0
+ case .never: return false
+ @unknown default: fatalError()
+ }
+ }
+
+ internal func reduceWithSpanBetween(earlierDate: Date, laterDate: Date) -> DispatchTimeInterval {
+ return self.map { value, factor in
+ let interval = laterDate.timeIntervalSince(earlierDate)
+ let remainder = Double(value) - interval * factor
+ guard remainder > 0 else { return 0 }
+ return Int(remainder.rounded(.toNearestOrAwayFromZero))
+ }
+ }
+}
+
+extension Date {
+
+ internal func addingDispatchInterval(_ dispatchInterval: DispatchTimeInterval) -> Date {
+ switch dispatchInterval {
+ case .nanoseconds(let value), .microseconds(let value), .milliseconds(let value), .seconds(let value):
+ return self.addingTimeInterval(TimeInterval(value) / dispatchInterval.convertToSecondsFactor)
+ case .never: return Date.distantFuture
+ @unknown default: fatalError()
+ }
+ }
+
+}
--- /dev/null
+//
+// Debounce.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/11/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers.
+
+ - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html)
+
+ - parameter dueTime: Throttling duration for each element.
+ - parameter scheduler: Scheduler to run the throttle timers on.
+ - returns: The throttled sequence.
+ */
+ public func debounce(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Debounce(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler)
+ }
+}
+
+final private class DebounceSink<Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Element = Observer.Element
+ typealias ParentType = Debounce<Element>
+
+ private let _parent: ParentType
+
+ let _lock = RecursiveLock()
+
+ // state
+ private var _id = 0 as UInt64
+ private var _value: Element?
+
+ let cancellable = SerialDisposable()
+
+ init(parent: ParentType, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription = self._parent._source.subscribe(self)
+
+ return Disposables.create(subscription, cancellable)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ self._id = self._id &+ 1
+ let currentId = self._id
+ self._value = element
+
+
+ let scheduler = self._parent._scheduler
+ let dueTime = self._parent._dueTime
+
+ let d = SingleAssignmentDisposable()
+ self.cancellable.disposable = d
+ d.setDisposable(scheduler.scheduleRelative(currentId, dueTime: dueTime, action: self.propagate))
+ case .error:
+ self._value = nil
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if let value = self._value {
+ self._value = nil
+ self.forwardOn(.next(value))
+ }
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+
+ func propagate(_ currentId: UInt64) -> Disposable {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ let originalValue = self._value
+
+ if let value = originalValue, self._id == currentId {
+ self._value = nil
+ self.forwardOn(.next(value))
+ }
+ // }
+ return Disposables.create()
+ }
+}
+
+final private class Debounce<Element>: Producer<Element> {
+ fileprivate let _source: Observable<Element>
+ fileprivate let _dueTime: RxTimeInterval
+ fileprivate let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
+ self._source = source
+ self._dueTime = dueTime
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = DebounceSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+
+}
--- /dev/null
+//
+// Debug.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/2/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+import class Foundation.DateFormatter
+
+extension ObservableType {
+
+ /**
+ Prints received events for all observers on standard output.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter identifier: Identifier that is printed together with event description to standard output.
+ - parameter trimOutput: Should output be trimmed to max 40 characters.
+ - returns: An observable sequence whose events are printed to standard output.
+ */
+ public func debug(_ identifier: String? = nil, trimOutput: Bool = false, file: String = #file, line: UInt = #line, function: String = #function)
+ -> Observable<Element> {
+ return Debug(source: self, identifier: identifier, trimOutput: trimOutput, file: file, line: line, function: function)
+ }
+}
+
+private let dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
+
+private func logEvent(_ identifier: String, dateFormat: DateFormatter, content: String) {
+ print("\(dateFormat.string(from: Date())): \(identifier) -> \(content)")
+}
+
+final private class DebugSink<Source: ObservableType, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == Source.Element {
+ typealias Element = Observer.Element
+ typealias Parent = Debug<Source>
+
+ private let _parent: Parent
+ private let _timestampFormatter = DateFormatter()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._timestampFormatter.dateFormat = dateFormat
+
+ logEvent(self._parent._identifier, dateFormat: self._timestampFormatter, content: "subscribed")
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ let maxEventTextLength = 40
+ let eventText = "\(event)"
+
+ let eventNormalized = (eventText.count > maxEventTextLength) && self._parent._trimOutput
+ ? String(eventText.prefix(maxEventTextLength / 2)) + "..." + String(eventText.suffix(maxEventTextLength / 2))
+ : eventText
+
+ logEvent(self._parent._identifier, dateFormat: self._timestampFormatter, content: "Event \(eventNormalized)")
+
+ self.forwardOn(event)
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+
+ override func dispose() {
+ if !self.disposed {
+ logEvent(self._parent._identifier, dateFormat: self._timestampFormatter, content: "isDisposed")
+ }
+ super.dispose()
+ }
+}
+
+final private class Debug<Source: ObservableType>: Producer<Source.Element> {
+ fileprivate let _identifier: String
+ fileprivate let _trimOutput: Bool
+ private let _source: Source
+
+ init(source: Source, identifier: String?, trimOutput: Bool, file: String, line: UInt, function: String) {
+ self._trimOutput = trimOutput
+ if let identifier = identifier {
+ self._identifier = identifier
+ }
+ else {
+ let trimmedFile: String
+ if let lastIndex = file.lastIndex(of: "/") {
+ trimmedFile = String(file[file.index(after: lastIndex) ..< file.endIndex])
+ }
+ else {
+ trimmedFile = file
+ }
+ self._identifier = "\(trimmedFile):\(line) (\(function))"
+ }
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Source.Element {
+ let sink = DebugSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// DefaultIfEmpty.swift
+// RxSwift
+//
+// Created by sergdort on 23/12/2016.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Emits elements from the source observable sequence, or a default element if the source observable sequence is empty.
+
+ - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html)
+
+ - parameter default: Default element to be sent if the source does not emit any elements
+ - returns: An observable sequence which emits default element end completes in case the original sequence is empty
+ */
+ public func ifEmpty(default: Element) -> Observable<Element> {
+ return DefaultIfEmpty(source: self.asObservable(), default: `default`)
+ }
+}
+
+final private class DefaultIfEmptySink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ private let _default: Element
+ private var _isEmpty = true
+
+ init(default: Element, observer: Observer, cancel: Cancelable) {
+ self._default = `default`
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._isEmpty = false
+ self.forwardOn(event)
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if self._isEmpty {
+ self.forwardOn(.next(self._default))
+ }
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class DefaultIfEmpty<SourceType>: Producer<SourceType> {
+ private let _source: Observable<SourceType>
+ private let _default: SourceType
+
+ init(source: Observable<SourceType>, `default`: SourceType) {
+ self._source = source
+ self._default = `default`
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceType {
+ let sink = DefaultIfEmptySink(default: self._default, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Deferred.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/19/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes.
+
+ - seealso: [defer operator on reactivex.io](http://reactivex.io/documentation/operators/defer.html)
+
+ - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence.
+ - returns: An observable sequence whose observers trigger an invocation of the given observable factory function.
+ */
+ public static func deferred(_ observableFactory: @escaping () throws -> Observable<Element>)
+ -> Observable<Element> {
+ return Deferred(observableFactory: observableFactory)
+ }
+}
+
+final private class DeferredSink<Source: ObservableType, Observer: ObserverType>: Sink<Observer>, ObserverType where Source.Element == Observer.Element {
+ typealias Element = Observer.Element
+
+ private let _observableFactory: () throws -> Source
+
+ init(observableFactory: @escaping () throws -> Source, observer: Observer, cancel: Cancelable) {
+ self._observableFactory = observableFactory
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ do {
+ let result = try self._observableFactory()
+ return result.subscribe(self)
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return Disposables.create()
+ }
+ }
+
+ func on(_ event: Event<Element>) {
+ self.forwardOn(event)
+
+ switch event {
+ case .next:
+ break
+ case .error:
+ self.dispose()
+ case .completed:
+ self.dispose()
+ }
+ }
+}
+
+final private class Deferred<Source: ObservableType>: Producer<Source.Element> {
+ typealias Factory = () throws -> Source
+
+ private let _observableFactory : Factory
+
+ init(observableFactory: @escaping Factory) {
+ self._observableFactory = observableFactory
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable)
+ where Observer.Element == Source.Element {
+ let sink = DeferredSink(observableFactory: self._observableFactory, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Delay.swift
+// RxSwift
+//
+// Created by tarunon on 2016/02/09.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+
+extension ObservableType {
+
+ /**
+ Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the source by.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: the source Observable shifted in time by the specified delay.
+ */
+ public func delay(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Delay(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler)
+ }
+}
+
+final private class DelaySink<Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType {
+ typealias Element = Observer.Element
+ typealias Source = Observable<Element>
+ typealias DisposeKey = Bag<Disposable>.KeyType
+
+ private let _lock = RecursiveLock()
+
+ private let _dueTime: RxTimeInterval
+ private let _scheduler: SchedulerType
+
+ private let _sourceSubscription = SingleAssignmentDisposable()
+ private let _cancelable = SerialDisposable()
+
+ // is scheduled some action
+ private var _active = false
+ // is "run loop" on different scheduler running
+ private var _running = false
+ private var _errorEvent: Event<Element>?
+
+ // state
+ private var _queue = Queue<(eventTime: RxTime, event: Event<Element>)>(capacity: 0)
+ private var _disposed = false
+
+ init(observer: Observer, dueTime: RxTimeInterval, scheduler: SchedulerType, cancel: Cancelable) {
+ self._dueTime = dueTime
+ self._scheduler = scheduler
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ // All of these complications in this method are caused by the fact that
+ // error should be propagated immediately. Error can be potentially received on different
+ // scheduler so this process needs to be synchronized somehow.
+ //
+ // Another complication is that scheduler is potentially concurrent so internal queue is used.
+ func drainQueue(state: (), scheduler: AnyRecursiveScheduler<()>) {
+
+ self._lock.lock() // {
+ let hasFailed = self._errorEvent != nil
+ if !hasFailed {
+ self._running = true
+ }
+ self._lock.unlock() // }
+
+ if hasFailed {
+ return
+ }
+
+ var ranAtLeastOnce = false
+
+ while true {
+ self._lock.lock() // {
+ let errorEvent = self._errorEvent
+
+ let eventToForwardImmediately = ranAtLeastOnce ? nil : self._queue.dequeue()?.event
+ let nextEventToScheduleOriginalTime: Date? = ranAtLeastOnce && !self._queue.isEmpty ? self._queue.peek().eventTime : nil
+
+ if errorEvent == nil {
+ if eventToForwardImmediately != nil {
+ }
+ else if nextEventToScheduleOriginalTime != nil {
+ self._running = false
+ }
+ else {
+ self._running = false
+ self._active = false
+ }
+ }
+ self._lock.unlock() // {
+
+ if let errorEvent = errorEvent {
+ self.forwardOn(errorEvent)
+ self.dispose()
+ return
+ }
+ else {
+ if let eventToForwardImmediately = eventToForwardImmediately {
+ ranAtLeastOnce = true
+ self.forwardOn(eventToForwardImmediately)
+ if case .completed = eventToForwardImmediately {
+ self.dispose()
+ return
+ }
+ }
+ else if let nextEventToScheduleOriginalTime = nextEventToScheduleOriginalTime {
+ scheduler.schedule((), dueTime: self._dueTime.reduceWithSpanBetween(earlierDate: nextEventToScheduleOriginalTime, laterDate: self._scheduler.now))
+ return
+ }
+ else {
+ return
+ }
+ }
+ }
+ }
+
+ func on(_ event: Event<Element>) {
+ if event.isStopEvent {
+ self._sourceSubscription.dispose()
+ }
+
+ switch event {
+ case .error:
+ self._lock.lock() // {
+ let shouldSendImmediately = !self._running
+ self._queue = Queue(capacity: 0)
+ self._errorEvent = event
+ self._lock.unlock() // }
+
+ if shouldSendImmediately {
+ self.forwardOn(event)
+ self.dispose()
+ }
+ default:
+ self._lock.lock() // {
+ let shouldSchedule = !self._active
+ self._active = true
+ self._queue.enqueue((self._scheduler.now, event))
+ self._lock.unlock() // }
+
+ if shouldSchedule {
+ self._cancelable.disposable = self._scheduler.scheduleRecursive((), dueTime: self._dueTime, action: self.drainQueue)
+ }
+ }
+ }
+
+ func run(source: Observable<Element>) -> Disposable {
+ self._sourceSubscription.setDisposable(source.subscribe(self))
+ return Disposables.create(_sourceSubscription, _cancelable)
+ }
+}
+
+final private class Delay<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+ private let _dueTime: RxTimeInterval
+ private let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
+ self._source = source
+ self._dueTime = dueTime
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = DelaySink(observer: observer, dueTime: self._dueTime, scheduler: self._scheduler, cancel: cancel)
+ let subscription = sink.run(source: self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// DelaySubscription.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the subscription.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: Time-shifted sequence.
+ */
+ public func delaySubscription(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return DelaySubscription(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler)
+ }
+}
+
+final private class DelaySubscriptionSink<Observer: ObserverType>
+ : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+
+ func on(_ event: Event<Element>) {
+ self.forwardOn(event)
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+
+}
+
+final private class DelaySubscription<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+ private let _dueTime: RxTimeInterval
+ private let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
+ self._source = source
+ self._dueTime = dueTime
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = DelaySubscriptionSink(observer: observer, cancel: cancel)
+ let subscription = self._scheduler.scheduleRelative((), dueTime: self._dueTime) { _ in
+ return self._source.subscribe(sink)
+ }
+
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Dematerialize.swift
+// RxSwift
+//
+// Created by Jamie Pinkham on 3/13/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType where Element: EventConvertible {
+ /**
+ Convert any previously materialized Observable into it's original form.
+ - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html)
+ - returns: The dematerialized observable sequence.
+ */
+ public func dematerialize() -> Observable<Element.Element> {
+ return Dematerialize(source: self.asObservable())
+ }
+
+}
+
+private final class DematerializeSink<T: EventConvertible, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == T.Element {
+ fileprivate func on(_ event: Event<T>) {
+ switch event {
+ case .next(let element):
+ self.forwardOn(element.event)
+ if element.event.isStopEvent {
+ self.dispose()
+ }
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+}
+
+final private class Dematerialize<T: EventConvertible>: Producer<T.Element> {
+ private let _source: Observable<T>
+
+ init(source: Observable<T>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == T.Element {
+ let sink = DematerializeSink<T, Observer>(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Deprecated.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/5/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+import Foundation
+
+extension Observable {
+ /**
+ Converts a optional to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - parameter optional: Optional element in the resulting observable sequence.
+ - returns: An observable sequence containing the wrapped value or not from given optional.
+ */
+ @available(*, deprecated, message: "Implicit conversions from any type to optional type are allowed and that is causing issues with `from` operator overloading.", renamed: "from(optional:)")
+ public static func from(_ optional: Element?) -> Observable<Element> {
+ return Observable.from(optional: optional)
+ }
+
+ /**
+ Converts a optional to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - parameter optional: Optional element in the resulting observable sequence.
+ - parameter scheduler: Scheduler to send the optional element on.
+ - returns: An observable sequence containing the wrapped value or not from given optional.
+ */
+ @available(*, deprecated, message: "Implicit conversions from any type to optional type are allowed and that is causing issues with `from` operator overloading.", renamed: "from(optional:scheduler:)")
+ public static func from(_ optional: Element?, scheduler: ImmediateSchedulerType) -> Observable<Element> {
+ return Observable.from(optional: optional, scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+ /**
+
+ Projects each element of an observable sequence into a new form by incorporating the element's index.
+
+ - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)
+
+ - parameter selector: A transform function to apply to each source element; the second parameter of the function represents the index of the source element.
+ - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.
+ */
+ @available(*, deprecated, message: "Please use enumerated().map()")
+ public func mapWithIndex<Result>(_ selector: @escaping (Element, Int) throws -> Result)
+ -> Observable<Result> {
+ return self.enumerated().map { try selector($0.element, $0.index) }
+ }
+
+
+ /**
+
+ Projects each element of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ @available(*, deprecated, message: "Please use enumerated().flatMap()")
+ public func flatMapWithIndex<Source: ObservableConvertibleType>(_ selector: @escaping (Element, Int) throws -> Source)
+ -> Observable<Source.Element> {
+ return self.enumerated().flatMap { try selector($0.element, $0.index) }
+ }
+
+ /**
+
+ Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements.
+ The element's index is used in the logic of the predicate function.
+
+ - seealso: [skipWhile operator on reactivex.io](http://reactivex.io/documentation/operators/skipwhile.html)
+
+ - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element.
+ - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate.
+ */
+ @available(*, deprecated, message: "Please use enumerated().skipWhile().map()")
+ public func skipWhileWithIndex(_ predicate: @escaping (Element, Int) throws -> Bool) -> Observable<Element> {
+ return self.enumerated().skipWhile { try predicate($0.element, $0.index) }.map { $0.element }
+ }
+
+
+ /**
+
+ Returns elements from an observable sequence as long as a specified condition is true.
+
+ The element's index is used in the logic of the predicate function.
+
+ - seealso: [takeWhile operator on reactivex.io](http://reactivex.io/documentation/operators/takewhile.html)
+
+ - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element.
+ - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.
+ */
+ @available(*, deprecated, message: "Please use enumerated().takeWhile().map()")
+ public func takeWhileWithIndex(_ predicate: @escaping (Element, Int) throws -> Bool) -> Observable<Element> {
+ return self.enumerated().takeWhile { try predicate($0.element, $0.index) }.map { $0.element }
+ }
+}
+
+extension Disposable {
+ /// Deprecated in favor of `disposed(by:)`
+ ///
+ ///
+ /// Adds `self` to `bag`.
+ ///
+ /// - parameter bag: `DisposeBag` to add `self` to.
+ @available(*, deprecated, message: "use disposed(by:) instead", renamed: "disposed(by:)")
+ public func addDisposableTo(_ bag: DisposeBag) {
+ self.disposed(by: bag)
+ }
+}
+
+
+extension ObservableType {
+
+ /**
+ Returns an observable sequence that shares a single subscription to the underlying sequence, and immediately upon subscription replays latest element in buffer.
+
+ This operator is a specialization of replay which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.
+
+ - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html)
+
+ - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
+ */
+ @available(*, deprecated, message: "use share(replay: 1) instead", renamed: "share(replay:)")
+ public func shareReplayLatestWhileConnected()
+ -> Observable<Element> {
+ return self.share(replay: 1, scope: .whileConnected)
+ }
+}
+
+
+extension ObservableType {
+
+ /**
+ Returns an observable sequence that shares a single subscription to the underlying sequence, and immediately upon subscription replays maximum number of elements in buffer.
+
+ This operator is a specialization of replay which creates a subscription when the number of observers goes from zero to one, then shares that subscription with all subsequent observers until the number of observers returns to zero, at which point the subscription is disposed.
+
+ - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html)
+
+ - parameter bufferSize: Maximum element count of the replay buffer.
+ - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
+ */
+ @available(*, deprecated, message: "Suggested replacement is `share(replay: 1)`. In case old 3.x behavior of `shareReplay` is required please use `share(replay: 1, scope: .forever)` instead.", renamed: "share(replay:)")
+ public func shareReplay(_ bufferSize: Int)
+ -> Observable<Element> {
+ return self.share(replay: bufferSize, scope: .forever)
+ }
+}
+
+/// Variable is a wrapper for `BehaviorSubject`.
+///
+/// Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated
+/// it will complete its observable sequence (`asObservable`).
+///
+/// **This concept will be deprecated from RxSwift but offical migration path hasn't been decided yet.**
+/// https://github.com/ReactiveX/RxSwift/issues/1501
+///
+/// Current recommended replacement for this API is `RxCocoa.BehaviorRelay` because:
+/// * `Variable` isn't a standard cross platform concept, hence it's out of place in RxSwift target.
+/// * It doesn't have a counterpart for handling events (`PublishRelay`). It models state only.
+/// * It doesn't have a consistent naming with *Relay or other Rx concepts.
+/// * It has an inconsistent memory management model compared to other parts of RxSwift (completes on `deinit`).
+///
+/// Once plans are finalized, official availability attribute will be added in one of upcoming versions.
+@available(*, deprecated, message: "Variable is deprecated. Please use `BehaviorRelay` as a replacement.")
+public final class Variable<Element> {
+ private let _subject: BehaviorSubject<Element>
+
+ private var _lock = SpinLock()
+
+ // state
+ private var _value: Element
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ /// Gets or sets current value of variable.
+ ///
+ /// Whenever a new value is set, all the observers are notified of the change.
+ ///
+ /// Even if the newly set value is same as the old value, observers are still notified for change.
+ public var value: Element {
+ get {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._value
+ }
+ set(newValue) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .variable)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ self._lock.lock()
+ self._value = newValue
+ self._lock.unlock()
+
+ self._subject.on(.next(newValue))
+ }
+ }
+
+ /// Initializes variable with initial value.
+ ///
+ /// - parameter value: Initial variable value.
+ public init(_ value: Element) {
+ self._value = value
+ self._subject = BehaviorSubject(value: value)
+ }
+
+ /// - returns: Canonical interface for push style sequence
+ public func asObservable() -> Observable<Element> {
+ return self._subject
+ }
+
+ deinit {
+ self._subject.on(.completed)
+ }
+}
+
+extension ObservableType {
+ /**
+ Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the source by.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: the source Observable shifted in time by the specified delay.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "delay(_:scheduler:)")
+ public func delay(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return self.delay(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Applies a timeout policy for each element in the observable sequence. If the next element isn't received within the specified timeout duration starting from its predecessor, a TimeoutError is propagated to the observer.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: An observable sequence with a `RxError.timeout` in case of a timeout.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timeout(_:scheduler:)")
+ public func timeout(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return timeout(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence, using the specified scheduler to run timeout timers. If the next element isn't received within the specified timeout duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter other: Sequence to return in case of a timeout.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: The source sequence switching to the other sequence in case of a timeout.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timeout(_:other:scheduler:)")
+ public func timeout<OtherSource: ObservableConvertibleType>(_ dueTime: Foundation.TimeInterval, other: OtherSource, scheduler: SchedulerType)
+ -> Observable<Element> where Element == OtherSource.Element {
+ return timeout(.milliseconds(Int(dueTime * 1000.0)), other: other, scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers.
+
+ - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html)
+
+ - parameter duration: Duration for skipping elements from the start of the sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence with the elements skipped during the specified duration from the start of the source sequence.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "skip(_:scheduler:)")
+ public func skip(_ duration: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return skip(.milliseconds(Int(duration * 1000.0)), scheduler: scheduler)
+ }
+}
+
+extension ObservableType where Element : RxAbstractInteger {
+ /**
+ Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages.
+
+ - seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html)
+
+ - parameter period: Period for producing the values in the resulting sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence that produces a value after each period.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "interval(_:scheduler:)")
+ public static func interval(_ period: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return interval(.milliseconds(Int(period * 1000.0)), scheduler: scheduler)
+ }
+}
+
+extension ObservableType where Element: RxAbstractInteger {
+ /**
+ Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers.
+
+ - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html)
+
+ - parameter dueTime: Relative time at which to produce the first value.
+ - parameter period: Period to produce subsequent values.
+ - parameter scheduler: Scheduler to run timers on.
+ - returns: An observable sequence that produces a value after due time has elapsed and then each period.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timer(_:period:scheduler:)")
+ public static func timer(_ dueTime: Foundation.TimeInterval, period: Foundation.TimeInterval? = nil, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return timer(.milliseconds(Int(dueTime * 1000.0)), period: period.map { .milliseconds(Int($0 * 1000.0)) }, scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration.
+
+ This operator makes sure that no two elements are emitted in less then dueTime.
+
+ - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html)
+
+ - parameter dueTime: Throttling duration for each element.
+ - parameter latest: Should latest element received in a dueTime wide time window since last element emission be emitted.
+ - parameter scheduler: Scheduler to run the throttle timers on.
+ - returns: The throttled sequence.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "throttle(_:latest:scheduler:)")
+ public func throttle(_ dueTime: Foundation.TimeInterval, latest: Bool = true, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return throttle(.milliseconds(Int(dueTime * 1000.0)), latest: latest, scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Takes elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers.
+
+ - seealso: [take operator on reactivex.io](http://reactivex.io/documentation/operators/take.html)
+
+ - parameter duration: Duration for taking elements from the start of the sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence with the elements taken during the specified duration from the start of the source sequence.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "take(_:scheduler:)")
+ public func take(_ duration: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return take(.milliseconds(Int(duration * 1000.0)), scheduler: scheduler)
+ }
+}
+
+
+extension ObservableType {
+
+ /**
+ Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the subscription.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: Time-shifted sequence.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "delaySubscription(_:scheduler:)")
+ public func delaySubscription(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return delaySubscription(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence into a window that is completed when either it’s full or a given amount of time has elapsed.
+
+ - seealso: [window operator on reactivex.io](http://reactivex.io/documentation/operators/window.html)
+
+ - parameter timeSpan: Maximum time length of a window.
+ - parameter count: Maximum element count of a window.
+ - parameter scheduler: Scheduler to run windowing timers on.
+ - returns: An observable sequence of windows (instances of `Observable`).
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "window(_:)")
+ public func window(timeSpan: Foundation.TimeInterval, count: Int, scheduler: SchedulerType)
+ -> Observable<Observable<Element>> {
+ return window(timeSpan: .milliseconds(Int(timeSpan * 1000.0)), count: count, scheduler: scheduler)
+ }
+}
+
+
+extension PrimitiveSequence {
+ /**
+ Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the source by.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: the source Observable shifted in time by the specified delay.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "delay(_:scheduler:)")
+ public func delay(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return delay(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+
+ /**
+ Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the subscription.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: Time-shifted sequence.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "delaySubscription(_:scheduler:)")
+ public func delaySubscription(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return delaySubscription(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence. If the next element isn't received within the specified timeout duration starting from its predecessor, a TimeoutError is propagated to the observer.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: An observable sequence with a `RxError.timeout` in case of a timeout.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timeout(_:scheduler:)")
+ public func timeout(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return timeout(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence, using the specified scheduler to run timeout timers. If the next element isn't received within the specified timeout duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter other: Sequence to return in case of a timeout.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: The source sequence switching to the other sequence in case of a timeout.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timeout(_:other:scheduler:)")
+ public func timeout(_ dueTime: Foundation.TimeInterval,
+ other: PrimitiveSequence<Trait, Element>,
+ scheduler: SchedulerType) -> PrimitiveSequence<Trait, Element> {
+ return timeout(.milliseconds(Int(dueTime * 1000.0)), other: other, scheduler: scheduler)
+ }
+}
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+
+ /**
+ Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter onNext: Action to invoke for each element in the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
+ - parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
+ - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
+ - returns: The source sequence with the side-effecting behavior applied.
+ */
+ @available(*, deprecated, renamed: "do(onSuccess:onError:onSubscribe:onSubscribed:onDispose:)")
+ public func `do`(onNext: ((Element) throws -> Void)?,
+ onError: ((Swift.Error) throws -> Void)? = nil,
+ onSubscribe: (() -> Void)? = nil,
+ onSubscribed: (() -> Void)? = nil,
+ onDispose: (() -> Void)? = nil)
+ -> Single<Element> {
+ return self.`do`(
+ onSuccess: onNext,
+ onError: onError,
+ onSubscribe: onSubscribe,
+ onSubscribed: onSubscribed,
+ onDispose: onDispose
+ )
+ }
+}
+
+extension ObservableType {
+ /**
+ Projects each element of an observable sequence into a buffer that's sent out when either it's full or a given amount of time has elapsed, using the specified scheduler to run timers.
+
+ A useful real-world analogy of this overload is the behavior of a ferry leaving the dock when all seats are taken, or at the scheduled time of departure, whichever event occurs first.
+
+ - seealso: [buffer operator on reactivex.io](http://reactivex.io/documentation/operators/buffer.html)
+
+ - parameter timeSpan: Maximum time length of a buffer.
+ - parameter count: Maximum element count of a buffer.
+ - parameter scheduler: Scheduler to run buffering timers on.
+ - returns: An observable sequence of buffers.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "buffer(timeSpan:count:scheduler:)")
+ public func buffer(timeSpan: Foundation.TimeInterval, count: Int, scheduler: SchedulerType)
+ -> Observable<[Element]> {
+ return buffer(timeSpan: .milliseconds(Int(timeSpan * 1000.0)), count: count, scheduler: scheduler)
+ }
+}
+
+extension PrimitiveSequenceType where Element: RxAbstractInteger
+{
+ /**
+ Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers.
+
+ - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html)
+
+ - parameter dueTime: Relative time at which to produce the first value.
+ - parameter scheduler: Scheduler to run timers on.
+ - returns: An observable sequence that produces a value after due time has elapsed and then each period.
+ */
+ @available(*, deprecated, message: "Use DispatchTimeInterval overload instead.", renamed: "timer(_:scheduler:)")
+ public static func timer(_ dueTime: Foundation.TimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return timer(.milliseconds(Int(dueTime * 1000.0)), scheduler: scheduler)
+ }
+}
+
+extension Completable {
+ /**
+ Merges the completion of all Completables from a collection into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Collection of Completables to merge.
+ - returns: A Completable that merges the completion of all Completables.
+ */
+ @available(*, deprecated, message: "Use Completable.zip instead.", renamed: "zip")
+ public static func merge<Collection: Swift.Collection>(_ sources: Collection) -> Completable
+ where Collection.Element == Completable {
+ return zip(sources)
+ }
+
+ /**
+ Merges the completion of all Completables from an array into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Array of observable sequences to merge.
+ - returns: A Completable that merges the completion of all Completables.
+ */
+ @available(*, deprecated, message: "Use Completable.zip instead.", renamed: "zip")
+ public static func merge(_ sources: [Completable]) -> Completable {
+ return zip(sources)
+ }
+
+ /**
+ Merges the completion of all Completables into a single Completable.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Collection of observable sequences to merge.
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ @available(*, deprecated, message: "Use Completable.zip instead.", renamed: "zip")
+ public static func merge(_ sources: Completable...) -> Completable {
+ return zip(sources)
+ }
+}
--- /dev/null
+//
+// DispatchQueue+Extensions.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 10/22/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+import Dispatch
+
+extension DispatchQueue {
+ private static var token: DispatchSpecificKey<()> = {
+ let key = DispatchSpecificKey<()>()
+ DispatchQueue.main.setSpecific(key: key, value: ())
+ return key
+ }()
+
+ static var isMain: Bool {
+ return DispatchQueue.getSpecific(key: token) != nil
+ }
+}
--- /dev/null
+//
+// DispatchQueueConfiguration.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 7/23/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+import Dispatch
+import struct Foundation.TimeInterval
+
+struct DispatchQueueConfiguration {
+ let queue: DispatchQueue
+ let leeway: DispatchTimeInterval
+}
+
+extension DispatchQueueConfiguration {
+ func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let cancel = SingleAssignmentDisposable()
+
+ self.queue.async {
+ if cancel.isDisposed {
+ return
+ }
+
+
+ cancel.setDisposable(action(state))
+ }
+
+ return cancel
+ }
+
+ func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let deadline = DispatchTime.now() + dueTime
+
+ let compositeDisposable = CompositeDisposable()
+
+ let timer = DispatchSource.makeTimerSource(queue: self.queue)
+ timer.schedule(deadline: deadline, leeway: self.leeway)
+
+ // TODO:
+ // This looks horrible, and yes, it is.
+ // It looks like Apple has made a conceputal change here, and I'm unsure why.
+ // Need more info on this.
+ // It looks like just setting timer to fire and not holding a reference to it
+ // until deadline causes timer cancellation.
+ var timerReference: DispatchSourceTimer? = timer
+ let cancelTimer = Disposables.create {
+ timerReference?.cancel()
+ timerReference = nil
+ }
+
+ timer.setEventHandler(handler: {
+ if compositeDisposable.isDisposed {
+ return
+ }
+ _ = compositeDisposable.insert(action(state))
+ cancelTimer.dispose()
+ })
+ timer.resume()
+
+ _ = compositeDisposable.insert(cancelTimer)
+
+ return compositeDisposable
+ }
+
+ func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
+ let initial = DispatchTime.now() + startAfter
+
+ var timerState = state
+
+ let timer = DispatchSource.makeTimerSource(queue: self.queue)
+ timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
+
+ // TODO:
+ // This looks horrible, and yes, it is.
+ // It looks like Apple has made a conceputal change here, and I'm unsure why.
+ // Need more info on this.
+ // It looks like just setting timer to fire and not holding a reference to it
+ // until deadline causes timer cancellation.
+ var timerReference: DispatchSourceTimer? = timer
+ let cancelTimer = Disposables.create {
+ timerReference?.cancel()
+ timerReference = nil
+ }
+
+ timer.setEventHandler(handler: {
+ if cancelTimer.isDisposed {
+ return
+ }
+ timerState = action(timerState)
+ })
+ timer.resume()
+
+ return cancelTimer
+ }
+}
--- /dev/null
+//
+// Disposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a disposable resource.
+public protocol Disposable {
+ /// Dispose resource.
+ func dispose()
+}
--- /dev/null
+//
+// Disposables.swift
+// RxSwift
+//
+// Created by Mohsen Ramezanpoor on 01/08/2016.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+/// A collection of utility methods for common disposable operations.
+public struct Disposables {
+ private init() {}
+}
+
--- /dev/null
+//
+// DisposeBag.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension Disposable {
+ /// Adds `self` to `bag`
+ ///
+ /// - parameter bag: `DisposeBag` to add `self` to.
+ public func disposed(by bag: DisposeBag) {
+ bag.insert(self)
+ }
+}
+
+/**
+Thread safe bag that disposes added disposables on `deinit`.
+
+This returns ARC (RAII) like resource management to `RxSwift`.
+
+In case contained disposables need to be disposed, just put a different dispose bag
+or create a new one in its place.
+
+ self.existingDisposeBag = DisposeBag()
+
+In case explicit disposal is necessary, there is also `CompositeDisposable`.
+*/
+public final class DisposeBag: DisposeBase {
+
+ private var _lock = SpinLock()
+
+ // state
+ private var _disposables = [Disposable]()
+ private var _isDisposed = false
+
+ /// Constructs new empty dispose bag.
+ public override init() {
+ super.init()
+ }
+
+ /// Adds `disposable` to be disposed when dispose bag is being deinited.
+ ///
+ /// - parameter disposable: Disposable to add.
+ public func insert(_ disposable: Disposable) {
+ self._insert(disposable)?.dispose()
+ }
+
+ private func _insert(_ disposable: Disposable) -> Disposable? {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._isDisposed {
+ return disposable
+ }
+
+ self._disposables.append(disposable)
+
+ return nil
+ }
+
+ /// This is internal on purpose, take a look at `CompositeDisposable` instead.
+ private func dispose() {
+ let oldDisposables = self._dispose()
+
+ for disposable in oldDisposables {
+ disposable.dispose()
+ }
+ }
+
+ private func _dispose() -> [Disposable] {
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ let disposables = self._disposables
+
+ self._disposables.removeAll(keepingCapacity: false)
+ self._isDisposed = true
+
+ return disposables
+ }
+
+ deinit {
+ self.dispose()
+ }
+}
+
+extension DisposeBag {
+
+ /// Convenience init allows a list of disposables to be gathered for disposal.
+ public convenience init(disposing disposables: Disposable...) {
+ self.init()
+ self._disposables += disposables
+ }
+
+ /// Convenience init allows an array of disposables to be gathered for disposal.
+ public convenience init(disposing disposables: [Disposable]) {
+ self.init()
+ self._disposables += disposables
+ }
+
+ /// Convenience function allows a list of disposables to be gathered for disposal.
+ public func insert(_ disposables: Disposable...) {
+ self.insert(disposables)
+ }
+
+ /// Convenience function allows an array of disposables to be gathered for disposal.
+ public func insert(_ disposables: [Disposable]) {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._isDisposed {
+ disposables.forEach { $0.dispose() }
+ } else {
+ self._disposables += disposables
+ }
+ }
+}
--- /dev/null
+//
+// DisposeBase.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/4/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Base class for all disposables.
+public class DisposeBase {
+ init() {
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ }
+
+ deinit {
+#if TRACE_RESOURCES
+ _ = Resources.decrementTotal()
+#endif
+ }
+}
--- /dev/null
+//
+// DistinctUntilChanged.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType where Element: Equatable {
+
+ /**
+ Returns an observable sequence that contains only distinct contiguous elements according to equality operator.
+
+ - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
+
+ - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
+ */
+ public func distinctUntilChanged()
+ -> Observable<Element> {
+ return self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) })
+ }
+}
+
+extension ObservableType {
+ /**
+ Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`.
+
+ - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
+
+ - parameter keySelector: A function to compute the comparison key for each element.
+ - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence.
+ */
+ public func distinctUntilChanged<Key: Equatable>(_ keySelector: @escaping (Element) throws -> Key)
+ -> Observable<Element> {
+ return self.distinctUntilChanged(keySelector, comparer: { $0 == $1 })
+ }
+
+ /**
+ Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`.
+
+ - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
+
+ - parameter comparer: Equality comparer for computed key values.
+ - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence.
+ */
+ public func distinctUntilChanged(_ comparer: @escaping (Element, Element) throws -> Bool)
+ -> Observable<Element> {
+ return self.distinctUntilChanged({ $0 }, comparer: comparer)
+ }
+
+ /**
+ Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer.
+
+ - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
+
+ - parameter keySelector: A function to compute the comparison key for each element.
+ - parameter comparer: Equality comparer for computed key values.
+ - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence.
+ */
+ public func distinctUntilChanged<K>(_ keySelector: @escaping (Element) throws -> K, comparer: @escaping (K, K) throws -> Bool)
+ -> Observable<Element> {
+ return DistinctUntilChanged(source: self.asObservable(), selector: keySelector, comparer: comparer)
+ }
+}
+
+final private class DistinctUntilChangedSink<Observer: ObserverType, Key>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+
+ private let _parent: DistinctUntilChanged<Element, Key>
+ private var _currentKey: Key?
+
+ init(parent: DistinctUntilChanged<Element, Key>, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ do {
+ let key = try self._parent._selector(value)
+ var areEqual = false
+ if let currentKey = self._currentKey {
+ areEqual = try self._parent._comparer(currentKey, key)
+ }
+
+ if areEqual {
+ return
+ }
+
+ self._currentKey = key
+
+ self.forwardOn(event)
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ case .error, .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+}
+
+final private class DistinctUntilChanged<Element, Key>: Producer<Element> {
+ typealias KeySelector = (Element) throws -> Key
+ typealias EqualityComparer = (Key, Key) throws -> Bool
+
+ private let _source: Observable<Element>
+ fileprivate let _selector: KeySelector
+ fileprivate let _comparer: EqualityComparer
+
+ init(source: Observable<Element>, selector: @escaping KeySelector, comparer: @escaping EqualityComparer) {
+ self._source = source
+ self._selector = selector
+ self._comparer = comparer
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Do.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter onNext: Action to invoke for each element in the observable sequence.
+ - parameter afterNext: Action to invoke for each element after the observable has passed an onNext event along to its downstream.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter afterError: Action to invoke after errored termination of the observable sequence.
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - parameter afterCompleted: Action to invoke after graceful termination of the observable sequence.
+ - parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
+ - parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
+ - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
+ - returns: The source sequence with the side-effecting behavior applied.
+ */
+ public func `do`(onNext: ((Element) throws -> Void)? = nil, afterNext: ((Element) throws -> Void)? = nil, onError: ((Swift.Error) throws -> Void)? = nil, afterError: ((Swift.Error) throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil, afterCompleted: (() throws -> Void)? = nil, onSubscribe: (() -> Void)? = nil, onSubscribed: (() -> Void)? = nil, onDispose: (() -> Void)? = nil)
+ -> Observable<Element> {
+ return Do(source: self.asObservable(), eventHandler: { e in
+ switch e {
+ case .next(let element):
+ try onNext?(element)
+ case .error(let e):
+ try onError?(e)
+ case .completed:
+ try onCompleted?()
+ }
+ }, afterEventHandler: { e in
+ switch e {
+ case .next(let element):
+ try afterNext?(element)
+ case .error(let e):
+ try afterError?(e)
+ case .completed:
+ try afterCompleted?()
+ }
+ }, onSubscribe: onSubscribe, onSubscribed: onSubscribed, onDispose: onDispose)
+ }
+}
+
+final private class DoSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias EventHandler = (Event<Element>) throws -> Void
+ typealias AfterEventHandler = (Event<Element>) throws -> Void
+
+ private let _eventHandler: EventHandler
+ private let _afterEventHandler: AfterEventHandler
+
+ init(eventHandler: @escaping EventHandler, afterEventHandler: @escaping AfterEventHandler, observer: Observer, cancel: Cancelable) {
+ self._eventHandler = eventHandler
+ self._afterEventHandler = afterEventHandler
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ do {
+ try self._eventHandler(event)
+ self.forwardOn(event)
+ try self._afterEventHandler(event)
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+}
+
+final private class Do<Element>: Producer<Element> {
+ typealias EventHandler = (Event<Element>) throws -> Void
+ typealias AfterEventHandler = (Event<Element>) throws -> Void
+
+ private let _source: Observable<Element>
+ private let _eventHandler: EventHandler
+ private let _afterEventHandler: AfterEventHandler
+ private let _onSubscribe: (() -> Void)?
+ private let _onSubscribed: (() -> Void)?
+ private let _onDispose: (() -> Void)?
+
+ init(source: Observable<Element>, eventHandler: @escaping EventHandler, afterEventHandler: @escaping AfterEventHandler, onSubscribe: (() -> Void)?, onSubscribed: (() -> Void)?, onDispose: (() -> Void)?) {
+ self._source = source
+ self._eventHandler = eventHandler
+ self._afterEventHandler = afterEventHandler
+ self._onSubscribe = onSubscribe
+ self._onSubscribed = onSubscribed
+ self._onDispose = onDispose
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ self._onSubscribe?()
+ let sink = DoSink(eventHandler: self._eventHandler, afterEventHandler: self._afterEventHandler, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ self._onSubscribed?()
+ let onDispose = self._onDispose
+ let allSubscriptions = Disposables.create {
+ subscription.dispose()
+ onDispose?()
+ }
+ return (sink: sink, subscription: allSubscriptions)
+ }
+}
--- /dev/null
+//
+// ElementAt.swift
+// RxSwift
+//
+// Created by Junior B. on 21/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns a sequence emitting only element _n_ emitted by an Observable
+
+ - seealso: [elementAt operator on reactivex.io](http://reactivex.io/documentation/operators/elementat.html)
+
+ - parameter index: The index of the required element (starting from 0).
+ - returns: An observable sequence that emits the desired element as its own sole emission.
+ */
+ public func elementAt(_ index: Int)
+ -> Observable<Element> {
+ return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: true)
+ }
+}
+
+final private class ElementAtSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias SourceType = Observer.Element
+ typealias Parent = ElementAt<SourceType>
+
+ let _parent: Parent
+ var _i: Int
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._i = parent._index
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case .next:
+
+ if self._i == 0 {
+ self.forwardOn(event)
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+
+ do {
+ _ = try decrementChecked(&self._i)
+ } catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return
+ }
+
+ case .error(let e):
+ self.forwardOn(.error(e))
+ self.dispose()
+ case .completed:
+ if self._parent._throwOnEmpty {
+ self.forwardOn(.error(RxError.argumentOutOfRange))
+ } else {
+ self.forwardOn(.completed)
+ }
+
+ self.dispose()
+ }
+ }
+}
+
+final private class ElementAt<SourceType>: Producer<SourceType> {
+ let _source: Observable<SourceType>
+ let _throwOnEmpty: Bool
+ let _index: Int
+
+ init(source: Observable<SourceType>, index: Int, throwOnEmpty: Bool) {
+ if index < 0 {
+ rxFatalError("index can't be negative")
+ }
+
+ self._source = source
+ self._index = index
+ self._throwOnEmpty = throwOnEmpty
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceType {
+ let sink = ElementAtSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Empty.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message.
+
+ - seealso: [empty operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence with no elements.
+ */
+ public static func empty() -> Observable<Element> {
+ return EmptyProducer<Element>()
+ }
+}
+
+final private class EmptyProducer<Element>: Producer<Element> {
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ observer.on(.completed)
+ return Disposables.create()
+ }
+}
--- /dev/null
+//
+// Enumerated.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/6/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Enumerates the elements of an observable sequence.
+
+ - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)
+
+ - returns: An observable sequence that contains tuples of source sequence elements and their indexes.
+ */
+ public func enumerated()
+ -> Observable<(index: Int, element: Element)> {
+ return Enumerated(source: self.asObservable())
+ }
+}
+
+final private class EnumeratedSink<Element, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == (index: Int, element: Element) {
+ var index = 0
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ do {
+ let nextIndex = try incrementChecked(&self.index)
+ let next = (index: nextIndex, element: value)
+ self.forwardOn(.next(next))
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+}
+
+final private class Enumerated<Element>: Producer<(index: Int, element: Element)> {
+ private let _source: Observable<Element>
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == (index: Int, element: Element) {
+ let sink = EnumeratedSink<Element, Observer>(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Error.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Returns an observable sequence that terminates with an `error`.
+
+ - seealso: [throw operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: The observable sequence that terminates with specified error.
+ */
+ public static func error(_ error: Swift.Error) -> Observable<Element> {
+ return ErrorProducer(error: error)
+ }
+}
+
+final private class ErrorProducer<Element>: Producer<Element> {
+ private let _error: Swift.Error
+
+ init(error: Swift.Error) {
+ self._error = error
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ observer.on(.error(self._error))
+ return Disposables.create()
+ }
+}
--- /dev/null
+//
+// Errors.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/28/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+let RxErrorDomain = "RxErrorDomain"
+let RxCompositeFailures = "RxCompositeFailures"
+
+/// Generic Rx error codes.
+public enum RxError
+ : Swift.Error
+ , CustomDebugStringConvertible {
+ /// Unknown error occurred.
+ case unknown
+ /// Performing an action on disposed object.
+ case disposed(object: AnyObject)
+ /// Arithmetic overflow error.
+ case overflow
+ /// Argument out of range error.
+ case argumentOutOfRange
+ /// Sequence doesn't contain any elements.
+ case noElements
+ /// Sequence contains more than one element.
+ case moreThanOneElement
+ /// Timeout error.
+ case timeout
+}
+
+extension RxError {
+ /// A textual representation of `self`, suitable for debugging.
+ public var debugDescription: String {
+ switch self {
+ case .unknown:
+ return "Unknown error occurred."
+ case .disposed(let object):
+ return "Object `\(object)` was already disposed."
+ case .overflow:
+ return "Arithmetic overflow occurred."
+ case .argumentOutOfRange:
+ return "Argument out of range."
+ case .noElements:
+ return "Sequence doesn't contain any elements."
+ case .moreThanOneElement:
+ return "Sequence contains more than one element."
+ case .timeout:
+ return "Sequence timeout."
+ }
+ }
+}
--- /dev/null
+//
+// Event.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a sequence event.
+///
+/// Sequence grammar:
+/// **next\* (error | completed)**
+public enum Event<Element> {
+ /// Next element is produced.
+ case next(Element)
+
+ /// Sequence terminated with an error.
+ case error(Swift.Error)
+
+ /// Sequence completed successfully.
+ case completed
+}
+
+extension Event: CustomDebugStringConvertible {
+ /// Description of event.
+ public var debugDescription: String {
+ switch self {
+ case .next(let value):
+ return "next(\(value))"
+ case .error(let error):
+ return "error(\(error))"
+ case .completed:
+ return "completed"
+ }
+ }
+}
+
+extension Event {
+ /// Is `completed` or `error` event.
+ public var isStopEvent: Bool {
+ switch self {
+ case .next: return false
+ case .error, .completed: return true
+ }
+ }
+
+ /// If `next` event, returns element value.
+ public var element: Element? {
+ if case .next(let value) = self {
+ return value
+ }
+ return nil
+ }
+
+ /// If `error` event, returns error.
+ public var error: Swift.Error? {
+ if case .error(let error) = self {
+ return error
+ }
+ return nil
+ }
+
+ /// If `completed` event, returns `true`.
+ public var isCompleted: Bool {
+ if case .completed = self {
+ return true
+ }
+ return false
+ }
+}
+
+extension Event {
+ /// Maps sequence elements using transform. If error happens during the transform, `.error`
+ /// will be returned as value.
+ public func map<Result>(_ transform: (Element) throws -> Result) -> Event<Result> {
+ do {
+ switch self {
+ case let .next(element):
+ return .next(try transform(element))
+ case let .error(error):
+ return .error(error)
+ case .completed:
+ return .completed
+ }
+ }
+ catch let e {
+ return .error(e)
+ }
+ }
+}
+
+/// A type that can be converted to `Event<Element>`.
+public protocol EventConvertible {
+ /// Type of element in event
+ associatedtype Element
+
+ @available(*, deprecated, renamed: "Element")
+ typealias ElementType = Element
+
+ /// Event representation of this instance
+ var event: Event<Element> { get }
+}
+
+extension Event: EventConvertible {
+ /// Event representation of this instance
+ public var event: Event<Element> {
+ return self
+ }
+}
--- /dev/null
+//
+// RXFilter.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/17/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Filters the elements of an observable sequence based on a predicate.
+
+ - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html)
+
+ - parameter predicate: A function to test each source element for a condition.
+ - returns: An observable sequence that contains elements from the input sequence that satisfy the condition.
+ */
+ public func filter(_ predicate: @escaping (Element) throws -> Bool)
+ -> Observable<Element> {
+ return RXFilter(source: self.asObservable(), predicate: predicate)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Skips elements and completes (or errors) when the observable sequence completes (or errors). Equivalent to filter that always returns false.
+
+ - seealso: [ignoreElements operator on reactivex.io](http://reactivex.io/documentation/operators/ignoreelements.html)
+
+ - returns: An observable sequence that skips all elements of the source sequence.
+ */
+ public func ignoreElements()
+ -> Completable {
+ return self.flatMap { _ in
+ return Observable<Never>.empty()
+ }
+ .asCompletable()
+ }
+}
+
+final private class FilterSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Predicate = (Element) throws -> Bool
+ typealias Element = Observer.Element
+
+ private let _predicate: Predicate
+
+ init(predicate: @escaping Predicate, observer: Observer, cancel: Cancelable) {
+ self._predicate = predicate
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ do {
+ let satisfies = try self._predicate(value)
+ if satisfies {
+ self.forwardOn(.next(value))
+ }
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .completed, .error:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+}
+
+final private class RXFilter<Element>: Producer<Element> {
+ typealias Predicate = (Element) throws -> Bool
+
+ private let _source: Observable<Element>
+ private let _predicate: Predicate
+
+ init(source: Observable<Element>, predicate: @escaping Predicate) {
+ self._source = source
+ self._predicate = predicate
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = FilterSink(predicate: self._predicate, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// First.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 7/31/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+private final class FirstSink<Element, Observer: ObserverType> : Sink<Observer>, ObserverType where Observer.Element == Element? {
+ typealias Parent = First<Element>
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ self.forwardOn(.next(value))
+ self.forwardOn(.completed)
+ self.dispose()
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.next(nil))
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final class First<Element>: Producer<Element?> {
+ private let _source: Observable<Element>
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element? {
+ let sink = FirstSink(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Generate.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/2/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Generates an observable sequence by running a state-driven loop producing the sequence's elements, using the specified scheduler
+ to run the loop send out observer messages.
+
+ - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
+
+ - parameter initialState: Initial state.
+ - parameter condition: Condition to terminate generation (upon returning `false`).
+ - parameter iterate: Iteration step function.
+ - parameter scheduler: Scheduler on which to run the generator loop.
+ - returns: The generated sequence.
+ */
+ public static func generate(initialState: Element, condition: @escaping (Element) throws -> Bool, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance, iterate: @escaping (Element) throws -> Element) -> Observable<Element> {
+ return Generate(initialState: initialState, condition: condition, iterate: iterate, resultSelector: { $0 }, scheduler: scheduler)
+ }
+}
+
+final private class GenerateSink<Sequence, Observer: ObserverType>: Sink<Observer> {
+ typealias Parent = Generate<Sequence, Observer.Element>
+
+ private let _parent: Parent
+
+ private var _state: Sequence
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._state = parent._initialState
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.scheduleRecursive(true) { isFirst, recurse -> Void in
+ do {
+ if !isFirst {
+ self._state = try self._parent._iterate(self._state)
+ }
+
+ if try self._parent._condition(self._state) {
+ let result = try self._parent._resultSelector(self._state)
+ self.forwardOn(.next(result))
+
+ recurse(false)
+ }
+ else {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+ }
+}
+
+final private class Generate<Sequence, Element>: Producer<Element> {
+ fileprivate let _initialState: Sequence
+ fileprivate let _condition: (Sequence) throws -> Bool
+ fileprivate let _iterate: (Sequence) throws -> Sequence
+ fileprivate let _resultSelector: (Sequence) throws -> Element
+ fileprivate let _scheduler: ImmediateSchedulerType
+
+ init(initialState: Sequence, condition: @escaping (Sequence) throws -> Bool, iterate: @escaping (Sequence) throws -> Sequence, resultSelector: @escaping (Sequence) throws -> Element, scheduler: ImmediateSchedulerType) {
+ self._initialState = initialState
+ self._condition = condition
+ self._iterate = iterate
+ self._resultSelector = resultSelector
+ self._scheduler = scheduler
+ super.init()
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = GenerateSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// GroupBy.swift
+// RxSwift
+//
+// Created by Tomi Koskinen on 01/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /*
+ Groups the elements of an observable sequence according to a specified key selector function.
+
+ - seealso: [groupBy operator on reactivex.io](http://reactivex.io/documentation/operators/groupby.html)
+
+ - parameter keySelector: A function to extract the key for each element.
+ - returns: A sequence of observable groups, each of which corresponds to a unique key value, containing all elements that share that same key value.
+ */
+ public func groupBy<Key: Hashable>(keySelector: @escaping (Element) throws -> Key)
+ -> Observable<GroupedObservable<Key, Element>> {
+ return GroupBy(source: self.asObservable(), selector: keySelector)
+ }
+}
+
+final private class GroupedObservableImpl<Element>: Observable<Element> {
+ private var _subject: PublishSubject<Element>
+ private var _refCount: RefCountDisposable
+
+ init(subject: PublishSubject<Element>, refCount: RefCountDisposable) {
+ self._subject = subject
+ self._refCount = refCount
+ }
+
+ override public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ let release = self._refCount.retain()
+ let subscription = self._subject.subscribe(observer)
+ return Disposables.create(release, subscription)
+ }
+}
+
+
+final private class GroupBySink<Key: Hashable, Element, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType where Observer.Element == GroupedObservable<Key, Element> {
+ typealias ResultType = Observer.Element
+ typealias Parent = GroupBy<Key, Element>
+
+ private let _parent: Parent
+ private let _subscription = SingleAssignmentDisposable()
+ private var _refCountDisposable: RefCountDisposable!
+ private var _groupedSubjectTable: [Key: PublishSubject<Element>]
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._groupedSubjectTable = [Key: PublishSubject<Element>]()
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ self._refCountDisposable = RefCountDisposable(disposable: self._subscription)
+
+ self._subscription.setDisposable(self._parent._source.subscribe(self))
+
+ return self._refCountDisposable
+ }
+
+ private func onGroupEvent(key: Key, value: Element) {
+ if let writer = self._groupedSubjectTable[key] {
+ writer.on(.next(value))
+ } else {
+ let writer = PublishSubject<Element>()
+ self._groupedSubjectTable[key] = writer
+
+ let group = GroupedObservable(
+ key: key,
+ source: GroupedObservableImpl(subject: writer, refCount: _refCountDisposable)
+ )
+
+ self.forwardOn(.next(group))
+ writer.on(.next(value))
+ }
+ }
+
+ final func on(_ event: Event<Element>) {
+ switch event {
+ case let .next(value):
+ do {
+ let groupKey = try self._parent._selector(value)
+ self.onGroupEvent(key: groupKey, value: value)
+ }
+ catch let e {
+ self.error(e)
+ return
+ }
+ case let .error(e):
+ self.error(e)
+ case .completed:
+ self.forwardOnGroups(event: .completed)
+ self.forwardOn(.completed)
+ self._subscription.dispose()
+ self.dispose()
+ }
+ }
+
+ final func error(_ error: Swift.Error) {
+ self.forwardOnGroups(event: .error(error))
+ self.forwardOn(.error(error))
+ self._subscription.dispose()
+ self.dispose()
+ }
+
+ final func forwardOnGroups(event: Event<Element>) {
+ for writer in self._groupedSubjectTable.values {
+ writer.on(event)
+ }
+ }
+}
+
+final private class GroupBy<Key: Hashable, Element>: Producer<GroupedObservable<Key,Element>> {
+ typealias KeySelector = (Element) throws -> Key
+
+ fileprivate let _source: Observable<Element>
+ fileprivate let _selector: KeySelector
+
+ init(source: Observable<Element>, selector: @escaping KeySelector) {
+ self._source = source
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == GroupedObservable<Key,Element> {
+ let sink = GroupBySink(parent: self, observer: observer, cancel: cancel)
+ return (sink: sink, subscription: sink.run())
+ }
+}
--- /dev/null
+//
+// GroupedObservable.swift
+// RxSwift
+//
+// Created by Tomi Koskinen on 01/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an observable sequence of elements that have a common key.
+public struct GroupedObservable<Key, Element> : ObservableType {
+ /// Gets the common key.
+ public let key: Key
+
+ private let source: Observable<Element>
+
+ /// Initializes grouped observable sequence with key and source observable sequence.
+ ///
+ /// - parameter key: Grouped observable sequence key
+ /// - parameter source: Observable sequence that represents sequence of elements for the key
+ /// - returns: Grouped observable sequence of elements for the specific key
+ public init(key: Key, source: Observable<Element>) {
+ self.key = key
+ self.source = source
+ }
+
+ /// Subscribes `observer` to receive events for this sequence.
+ public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ return self.source.subscribe(observer)
+ }
+
+ /// Converts `self` to `Observable` sequence.
+ public func asObservable() -> Observable<Element> {
+ return self.source
+ }
+}
--- /dev/null
+//
+// HistoricalScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 12/27/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+
+/// Provides a virtual time scheduler that uses `Date` for absolute time and `NSTimeInterval` for relative time.
+public class HistoricalScheduler : VirtualTimeScheduler<HistoricalSchedulerTimeConverter> {
+
+ /**
+ Creates a new historical scheduler with initial clock value.
+
+ - parameter initialClock: Initial value for virtual clock.
+ */
+ public init(initialClock: RxTime = Date(timeIntervalSince1970: 0)) {
+ super.init(initialClock: initialClock, converter: HistoricalSchedulerTimeConverter())
+ }
+}
--- /dev/null
+//
+// HistoricalSchedulerTimeConverter.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 12/27/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import Foundation
+
+/// Converts historical virtual time into real time.
+///
+/// Since historical virtual time is also measured in `Date`, this converter is identity function.
+public struct HistoricalSchedulerTimeConverter : VirtualTimeConverterType {
+ /// Virtual time unit used that represents ticks of virtual clock.
+ public typealias VirtualTimeUnit = RxTime
+
+ /// Virtual time unit used to represent differences of virtual times.
+ public typealias VirtualTimeIntervalUnit = TimeInterval
+
+ /// Returns identical value of argument passed because historical virtual time is equal to real time, just
+ /// decoupled from local machine clock.
+ public func convertFromVirtualTime(_ virtualTime: VirtualTimeUnit) -> RxTime {
+ return virtualTime
+ }
+
+ /// Returns identical value of argument passed because historical virtual time is equal to real time, just
+ /// decoupled from local machine clock.
+ public func convertToVirtualTime(_ time: RxTime) -> VirtualTimeUnit {
+ return time
+ }
+
+ /// Returns identical value of argument passed because historical virtual time is equal to real time, just
+ /// decoupled from local machine clock.
+ public func convertFromVirtualTimeInterval(_ virtualTimeInterval: VirtualTimeIntervalUnit) -> TimeInterval {
+ return virtualTimeInterval
+ }
+
+ /// Returns identical value of argument passed because historical virtual time is equal to real time, just
+ /// decoupled from local machine clock.
+ public func convertToVirtualTimeInterval(_ timeInterval: TimeInterval) -> VirtualTimeIntervalUnit {
+ return timeInterval
+ }
+
+ /**
+ Offsets `Date` by time interval.
+
+ - parameter time: Time.
+ - parameter timeInterval: Time interval offset.
+ - returns: Time offsetted by time interval.
+ */
+ public func offsetVirtualTime(_ time: VirtualTimeUnit, offset: VirtualTimeIntervalUnit) -> VirtualTimeUnit {
+ return time.addingTimeInterval(offset)
+ }
+
+ /// Compares two `Date`s.
+ public func compareVirtualTime(_ lhs: VirtualTimeUnit, _ rhs: VirtualTimeUnit) -> VirtualTimeComparison {
+ switch lhs.compare(rhs as Date) {
+ case .orderedAscending:
+ return .lessThan
+ case .orderedSame:
+ return .equal
+ case .orderedDescending:
+ return .greaterThan
+ }
+ }
+}
--- /dev/null
+//
+// ImmediateSchedulerType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/31/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an object that immediately schedules units of work.
+public protocol ImmediateSchedulerType {
+ /**
+ Schedules an action to be executed immediately.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable
+}
+
+extension ImmediateSchedulerType {
+ /**
+ Schedules an action to be executed recursively.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func scheduleRecursive<State>(_ state: State, action: @escaping (_ state: State, _ recurse: (State) -> Void) -> Void) -> Disposable {
+ let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self)
+
+ recursiveScheduler.schedule(state)
+
+ return Disposables.create(with: recursiveScheduler.dispose)
+ }
+}
--- /dev/null
+//
+// InfiniteSequence.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 6/13/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Sequence that repeats `repeatedValue` infinite number of times.
+struct InfiniteSequence<Element> : Sequence {
+ typealias Iterator = AnyIterator<Element>
+
+ private let _repeatedValue: Element
+
+ init(repeatedValue: Element) {
+ _repeatedValue = repeatedValue
+ }
+
+ func makeIterator() -> Iterator {
+ let repeatedValue = _repeatedValue
+ return AnyIterator {
+ return repeatedValue
+ }
+ }
+}
--- /dev/null
+//
+// InvocableScheduledItem.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 11/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+struct InvocableScheduledItem<I: InvocableWithValueType> : InvocableType {
+
+ let _invocable: I
+ let _state: I.Value
+
+ init(invocable: I, state: I.Value) {
+ self._invocable = invocable
+ self._state = state
+ }
+
+ func invoke() {
+ self._invocable.invoke(self._state)
+ }
+}
--- /dev/null
+//
+// InvocableType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 11/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol InvocableType {
+ func invoke()
+}
+
+protocol InvocableWithValueType {
+ associatedtype Value
+
+ func invoke(_ value: Value)
+}
--- /dev/null
+//
+// Just.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element) -> Observable<Element> {
+ return Just(element: element)
+ }
+
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - parameter scheduler: Scheduler to send the single element on.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element, scheduler: ImmediateSchedulerType) -> Observable<Element> {
+ return JustScheduled(element: element, scheduler: scheduler)
+ }
+}
+
+final private class JustScheduledSink<Observer: ObserverType>: Sink<Observer> {
+ typealias Parent = JustScheduled<Observer.Element>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let scheduler = self._parent._scheduler
+ return scheduler.schedule(self._parent._element) { element in
+ self.forwardOn(.next(element))
+ return scheduler.schedule(()) { _ in
+ self.forwardOn(.completed)
+ self.dispose()
+ return Disposables.create()
+ }
+ }
+ }
+}
+
+final private class JustScheduled<Element>: Producer<Element> {
+ fileprivate let _scheduler: ImmediateSchedulerType
+ fileprivate let _element: Element
+
+ init(element: Element, scheduler: ImmediateSchedulerType) {
+ self._scheduler = scheduler
+ self._element = element
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = JustScheduledSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class Just<Element>: Producer<Element> {
+ private let _element: Element
+
+ init(element: Element) {
+ self._element = element
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ observer.on(.next(self._element))
+ observer.on(.completed)
+ return Disposables.create()
+ }
+}
--- /dev/null
+//
+// Lock.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/31/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol Lock {
+ func lock()
+ func unlock()
+}
+
+// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000321.html
+typealias SpinLock = RecursiveLock
+
+extension RecursiveLock : Lock {
+ @inline(__always)
+ final func performLocked(_ action: () -> Void) {
+ self.lock(); defer { self.unlock() }
+ action()
+ }
+
+ @inline(__always)
+ final func calculateLocked<T>(_ action: () -> T) -> T {
+ self.lock(); defer { self.unlock() }
+ return action()
+ }
+
+ @inline(__always)
+ final func calculateLockedOrFail<T>(_ action: () throws -> T) throws -> T {
+ self.lock(); defer { self.unlock() }
+ let result = try action()
+ return result
+ }
+}
--- /dev/null
+//
+// LockOwnerType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol LockOwnerType : class, Lock {
+ var _lock: RecursiveLock { get }
+}
+
+extension LockOwnerType {
+ func lock() {
+ self._lock.lock()
+ }
+
+ func unlock() {
+ self._lock.unlock()
+ }
+}
--- /dev/null
+//
+// MainScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import Dispatch
+#if !os(Linux)
+ import Foundation
+#endif
+
+/**
+Abstracts work that needs to be performed on `DispatchQueue.main`. In case `schedule` methods are called from `DispatchQueue.main`, it will perform action immediately without scheduling.
+
+This scheduler is usually used to perform UI work.
+
+Main scheduler is a specialization of `SerialDispatchQueueScheduler`.
+
+This scheduler is optimized for `observeOn` operator. To ensure observable sequence is subscribed on main thread using `subscribeOn`
+operator please use `ConcurrentMainScheduler` because it is more optimized for that purpose.
+*/
+public final class MainScheduler : SerialDispatchQueueScheduler {
+
+ private let _mainQueue: DispatchQueue
+
+ let numberEnqueued = AtomicInt(0)
+
+ /// Initializes new instance of `MainScheduler`.
+ public init() {
+ self._mainQueue = DispatchQueue.main
+ super.init(serialQueue: self._mainQueue)
+ }
+
+ /// Singleton instance of `MainScheduler`
+ public static let instance = MainScheduler()
+
+ /// Singleton instance of `MainScheduler` that always schedules work asynchronously
+ /// and doesn't perform optimizations for calls scheduled from main queue.
+ public static let asyncInstance = SerialDispatchQueueScheduler(serialQueue: DispatchQueue.main)
+
+ /// In case this method is called on a background thread it will throw an exception.
+ public class func ensureExecutingOnScheduler(errorMessage: String? = nil) {
+ if !DispatchQueue.isMain {
+ rxFatalError(errorMessage ?? "Executing on background thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread.")
+ }
+ }
+
+ /// In case this method is running on a background thread it will throw an exception.
+ public class func ensureRunningOnMainThread(errorMessage: String? = nil) {
+ #if !os(Linux) // isMainThread is not implemented in Linux Foundation
+ guard Thread.isMainThread else {
+ rxFatalError(errorMessage ?? "Running on background thread.")
+ }
+ #endif
+ }
+
+ override func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let previousNumberEnqueued = increment(self.numberEnqueued)
+
+ if DispatchQueue.isMain && previousNumberEnqueued == 0 {
+ let disposable = action(state)
+ decrement(self.numberEnqueued)
+ return disposable
+ }
+
+ let cancel = SingleAssignmentDisposable()
+
+ self._mainQueue.async {
+ if !cancel.isDisposed {
+ _ = action(state)
+ }
+
+ decrement(self.numberEnqueued)
+ }
+
+ return cancel
+ }
+}
--- /dev/null
+//
+// Map.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence into a new form.
+
+ - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)
+
+ - parameter transform: A transform function to apply to each source element.
+ - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.
+
+ */
+ public func map<Result>(_ transform: @escaping (Element) throws -> Result)
+ -> Observable<Result> {
+ return Map(source: self.asObservable(), transform: transform)
+ }
+}
+
+final private class MapSink<SourceType, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Transform = (SourceType) throws -> ResultType
+
+ typealias ResultType = Observer.Element
+ typealias Element = SourceType
+
+ private let _transform: Transform
+
+ init(transform: @escaping Transform, observer: Observer, cancel: Cancelable) {
+ self._transform = transform
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case .next(let element):
+ do {
+ let mappedElement = try self._transform(element)
+ self.forwardOn(.next(mappedElement))
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class Map<SourceType, ResultType>: Producer<ResultType> {
+ typealias Transform = (SourceType) throws -> ResultType
+
+ private let _source: Observable<SourceType>
+
+ private let _transform: Transform
+
+ init(source: Observable<SourceType>, transform: @escaping Transform) {
+ self._source = source
+ self._transform = transform
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == ResultType {
+ let sink = MapSink(transform: self._transform, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Materialize.swift
+// RxSwift
+//
+// Created by sergdort on 08/03/2017.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Convert any Observable into an Observable of its events.
+ - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html)
+ - returns: An observable sequence that wraps events in an Event<E>. The returned Observable never errors, but it does complete after observing all of the events of the underlying Observable.
+ */
+ public func materialize() -> Observable<Event<Element>> {
+ return Materialize(source: self.asObservable())
+ }
+}
+
+private final class MaterializeSink<Element, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == Event<Element> {
+
+ func on(_ event: Event<Element>) {
+ self.forwardOn(.next(event))
+ if event.isStopEvent {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class Materialize<T>: Producer<Event<T>> {
+ private let _source: Observable<T>
+
+ init(source: Observable<T>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = MaterializeSink(observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Maybe.swift
+// RxSwift
+//
+// Created by sergdort on 19/08/2017.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+#if DEBUG
+import Foundation
+#endif
+
+/// Sequence containing 0 or 1 elements
+public enum MaybeTrait { }
+/// Represents a push style sequence containing 0 or 1 element.
+public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>
+
+public enum MaybeEvent<Element> {
+ /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
+ case success(Element)
+
+ /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
+ case error(Swift.Error)
+
+ /// Sequence completed successfully.
+ case completed
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ public typealias MaybeObserver = (MaybeEvent<Element>) -> Void
+
+ /**
+ Creates an observable sequence from a specified subscribe method implementation.
+
+ - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
+
+ - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
+ - returns: The observable sequence with the specified implementation for the `subscribe` method.
+ */
+ public static func create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<Trait, Element> {
+ let source = Observable<Element>.create { observer in
+ return subscribe { event in
+ switch event {
+ case .success(let element):
+ observer.on(.next(element))
+ observer.on(.completed)
+ case .error(let error):
+ observer.on(.error(error))
+ case .completed:
+ observer.on(.completed)
+ }
+ }
+ }
+
+ return PrimitiveSequence(raw: source)
+ }
+
+ /**
+ Subscribes `observer` to receive events for this sequence.
+
+ - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources.
+ */
+ public func subscribe(_ observer: @escaping (MaybeEvent<Element>) -> Void) -> Disposable {
+ var stopped = false
+ return self.primitiveSequence.asObservable().subscribe { event in
+ if stopped { return }
+ stopped = true
+
+ switch event {
+ case .next(let element):
+ observer(.success(element))
+ case .error(let error):
+ observer(.error(error))
+ case .completed:
+ observer(.completed)
+ }
+ }
+ }
+
+ /**
+ Subscribes a success handler, an error handler, and a completion handler for this sequence.
+
+ - parameter onSuccess: Action to invoke for each element in the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - returns: Subscription object used to unsubscribe from the observable sequence.
+ */
+ public func subscribe(onSuccess: ((Element) -> Void)? = nil,
+ onError: ((Swift.Error) -> Void)? = nil,
+ onCompleted: (() -> Void)? = nil) -> Disposable {
+ #if DEBUG
+ let callStack = Hooks.recordCallStackOnError ? Thread.callStackSymbols : []
+ #else
+ let callStack = [String]()
+ #endif
+
+ return self.primitiveSequence.subscribe { event in
+ switch event {
+ case .success(let element):
+ onSuccess?(element)
+ case .error(let error):
+ if let onError = onError {
+ onError(error)
+ } else {
+ Hooks.defaultErrorHandler(callStack, error)
+ }
+ case .completed:
+ onCompleted?()
+ }
+ }
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element) -> Maybe<Element> {
+ return Maybe(raw: Observable.just(element))
+ }
+
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - parameter scheduler: Scheduler to send the single element on.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element, scheduler: ImmediateSchedulerType) -> Maybe<Element> {
+ return Maybe(raw: Observable.just(element, scheduler: scheduler))
+ }
+
+ /**
+ Returns an observable sequence that terminates with an `error`.
+
+ - seealso: [throw operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: The observable sequence that terminates with specified error.
+ */
+ public static func error(_ error: Swift.Error) -> Maybe<Element> {
+ return PrimitiveSequence(raw: Observable.error(error))
+ }
+
+ /**
+ Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
+
+ - seealso: [never operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence whose observers will never get called.
+ */
+ public static func never() -> Maybe<Element> {
+ return PrimitiveSequence(raw: Observable.never())
+ }
+
+ /**
+ Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message.
+
+ - seealso: [empty operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence with no elements.
+ */
+ public static func empty() -> Maybe<Element> {
+ return Maybe(raw: Observable.empty())
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter onNext: Action to invoke for each element in the observable sequence.
+ - parameter afterNext: Action to invoke for each element after the observable has passed an onNext event along to its downstream.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter afterError: Action to invoke after errored termination of the observable sequence.
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - parameter afterCompleted: Action to invoke after graceful termination of the observable sequence.
+ - parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
+ - parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
+ - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
+ - returns: The source sequence with the side-effecting behavior applied.
+ */
+ public func `do`(onNext: ((Element) throws -> Void)? = nil,
+ afterNext: ((Element) throws -> Void)? = nil,
+ onError: ((Swift.Error) throws -> Void)? = nil,
+ afterError: ((Swift.Error) throws -> Void)? = nil,
+ onCompleted: (() throws -> Void)? = nil,
+ afterCompleted: (() throws -> Void)? = nil,
+ onSubscribe: (() -> Void)? = nil,
+ onSubscribed: (() -> Void)? = nil,
+ onDispose: (() -> Void)? = nil)
+ -> Maybe<Element> {
+ return Maybe(raw: self.primitiveSequence.source.do(
+ onNext: onNext,
+ afterNext: afterNext,
+ onError: onError,
+ afterError: afterError,
+ onCompleted: onCompleted,
+ afterCompleted: afterCompleted,
+ onSubscribe: onSubscribe,
+ onSubscribed: onSubscribed,
+ onDispose: onDispose)
+ )
+ }
+
+ /**
+ Filters the elements of an observable sequence based on a predicate.
+
+ - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html)
+
+ - parameter predicate: A function to test each source element for a condition.
+ - returns: An observable sequence that contains elements from the input sequence that satisfy the condition.
+ */
+ public func filter(_ predicate: @escaping (Element) throws -> Bool)
+ -> Maybe<Element> {
+ return Maybe(raw: self.primitiveSequence.source.filter(predicate))
+ }
+
+ /**
+ Projects each element of an observable sequence into a new form.
+
+ - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)
+
+ - parameter transform: A transform function to apply to each source element.
+ - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.
+
+ */
+ public func map<Result>(_ transform: @escaping (Element) throws -> Result)
+ -> Maybe<Result> {
+ return Maybe(raw: self.primitiveSequence.source.map(transform))
+ }
+
+ /**
+ Projects each element of an observable sequence into an optional form and filters all optional results.
+
+ - parameter transform: A transform function to apply to each source element.
+ - returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source.
+
+ */
+ public func compactMap<Result>(_ transform: @escaping (Element) throws -> Result?)
+ -> Maybe<Result> {
+ return Maybe(raw: self.primitiveSequence.source.compactMap(transform))
+ }
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ public func flatMap<Result>(_ selector: @escaping (Element) throws -> Maybe<Result>)
+ -> Maybe<Result> {
+ return Maybe<Result>(raw: self.primitiveSequence.source.flatMap(selector))
+ }
+
+ /**
+ Emits elements from the source observable sequence, or a default element if the source observable sequence is empty.
+
+ - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html)
+
+ - parameter default: Default element to be sent if the source does not emit any elements
+ - returns: An observable sequence which emits default element end completes in case the original sequence is empty
+ */
+ public func ifEmpty(default: Element) -> Single<Element> {
+ return Single(raw: self.primitiveSequence.source.ifEmpty(default: `default`))
+ }
+
+ /**
+ Returns the elements of the specified sequence or `switchTo` sequence if the sequence is empty.
+
+ - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html)
+
+ - parameter switchTo: Observable sequence being returned when source sequence is empty.
+ - returns: Observable sequence that contains elements from switchTo sequence if source is empty, otherwise returns source sequence elements.
+ */
+ public func ifEmpty(switchTo other: Maybe<Element>) -> Maybe<Element> {
+ return Maybe(raw: self.primitiveSequence.source.ifEmpty(switchTo: other.primitiveSequence.source))
+ }
+
+ /**
+ Returns the elements of the specified sequence or `switchTo` sequence if the sequence is empty.
+
+ - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html)
+
+ - parameter switchTo: Observable sequence being returned when source sequence is empty.
+ - returns: Observable sequence that contains elements from switchTo sequence if source is empty, otherwise returns source sequence elements.
+ */
+ public func ifEmpty(switchTo other: Single<Element>) -> Single<Element> {
+ return Single(raw: self.primitiveSequence.source.ifEmpty(switchTo: other.primitiveSequence.source))
+ }
+
+ /**
+ Continues an observable sequence that is terminated by an error with a single element.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - parameter element: Last element in an observable sequence in case error occurs.
+ - returns: An observable sequence containing the source sequence's elements, followed by the `element` in case an error occurred.
+ */
+ public func catchErrorJustReturn(_ element: Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.primitiveSequence.source.catchErrorJustReturn(element))
+ }
+}
--- /dev/null
+//
+// Merge.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/28/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ public func flatMap<Source: ObservableConvertibleType>(_ selector: @escaping (Element) throws -> Source)
+ -> Observable<Source.Element> {
+ return FlatMap(source: self.asObservable(), selector: selector)
+ }
+
+}
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+ If element is received while there is some projected observable sequence being merged it will simply be ignored.
+
+ - seealso: [flatMapFirst operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated.
+ */
+ public func flatMapFirst<Source: ObservableConvertibleType>(_ selector: @escaping (Element) throws -> Source)
+ -> Observable<Source.Element> {
+ return FlatMapFirst(source: self.asObservable(), selector: selector)
+ }
+}
+
+extension ObservableType where Element : ObservableConvertibleType {
+
+ /**
+ Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ public func merge() -> Observable<Element.Element> {
+ return Merge(source: self.asObservable())
+ }
+
+ /**
+ Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently.
+ - returns: The observable sequence that merges the elements of the inner sequences.
+ */
+ public func merge(maxConcurrent: Int)
+ -> Observable<Element.Element> {
+ return MergeLimited(source: self.asObservable(), maxConcurrent: maxConcurrent)
+ }
+}
+
+extension ObservableType where Element : ObservableConvertibleType {
+
+ /**
+ Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order.
+ */
+ public func concat() -> Observable<Element.Element> {
+ return self.merge(maxConcurrent: 1)
+ }
+}
+
+extension ObservableType {
+ /**
+ Merges elements from all observable sequences from collection into a single observable sequence.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Collection of observable sequences to merge.
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ public static func merge<Collection: Swift.Collection>(_ sources: Collection) -> Observable<Element> where Collection.Element == Observable<Element> {
+ return MergeArray(sources: Array(sources))
+ }
+
+ /**
+ Merges elements from all observable sequences from array into a single observable sequence.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Array of observable sequences to merge.
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ public static func merge(_ sources: [Observable<Element>]) -> Observable<Element> {
+ return MergeArray(sources: sources)
+ }
+
+ /**
+ Merges elements from all observable sequences into a single observable sequence.
+
+ - seealso: [merge operator on reactivex.io](http://reactivex.io/documentation/operators/merge.html)
+
+ - parameter sources: Collection of observable sequences to merge.
+ - returns: The observable sequence that merges the elements of the observable sequences.
+ */
+ public static func merge(_ sources: Observable<Element>...) -> Observable<Element> {
+ return MergeArray(sources: sources)
+ }
+}
+
+// MARK: concatMap
+
+extension ObservableType {
+ /**
+ Projects each element of an observable sequence to an observable sequence and concatenates the resulting observable sequences into one observable sequence.
+
+ - seealso: [concat operator on reactivex.io](http://reactivex.io/documentation/operators/concat.html)
+
+ - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order.
+ */
+
+ public func concatMap<Source: ObservableConvertibleType>(_ selector: @escaping (Element) throws -> Source)
+ -> Observable<Source.Element> {
+ return ConcatMap(source: self.asObservable(), selector: selector)
+ }
+}
+
+private final class MergeLimitedSinkIter<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType where SourceSequence.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias DisposeKey = CompositeDisposable.DisposeKey
+ typealias Parent = MergeLimitedSink<SourceElement, SourceSequence, Observer>
+
+ private let _parent: Parent
+ private let _disposeKey: DisposeKey
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ init(parent: Parent, disposeKey: DisposeKey) {
+ self._parent = parent
+ self._disposeKey = disposeKey
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent.forwardOn(event)
+ case .error:
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ case .completed:
+ self._parent._group.remove(for: self._disposeKey)
+ if let next = self._parent._queue.dequeue() {
+ self._parent.subscribe(next, group: self._parent._group)
+ }
+ else {
+ self._parent._activeCount -= 1
+
+ if self._parent._stopped && self._parent._activeCount == 0 {
+ self._parent.forwardOn(.completed)
+ self._parent.dispose()
+ }
+ }
+ }
+ }
+}
+
+private final class ConcatMapSink<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType>: MergeLimitedSink<SourceElement, SourceSequence, Observer> where Observer.Element == SourceSequence.Element {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _selector: Selector
+
+ init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) {
+ self._selector = selector
+ super.init(maxConcurrent: 1, observer: observer, cancel: cancel)
+ }
+
+ override func performMap(_ element: SourceElement) throws -> SourceSequence {
+ return try self._selector(element)
+ }
+}
+
+private final class MergeLimitedBasicSink<SourceSequence: ObservableConvertibleType, Observer: ObserverType>: MergeLimitedSink<SourceSequence, SourceSequence, Observer> where Observer.Element == SourceSequence.Element {
+
+ override func performMap(_ element: SourceSequence) throws -> SourceSequence {
+ return element
+ }
+}
+
+private class MergeLimitedSink<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType where Observer.Element == SourceSequence.Element {
+ typealias QueueType = Queue<SourceSequence>
+
+ let _maxConcurrent: Int
+
+ let _lock = RecursiveLock()
+
+ // state
+ var _stopped = false
+ var _activeCount = 0
+ var _queue = QueueType(capacity: 2)
+
+ let _sourceSubscription = SingleAssignmentDisposable()
+ let _group = CompositeDisposable()
+
+ init(maxConcurrent: Int, observer: Observer, cancel: Cancelable) {
+ self._maxConcurrent = maxConcurrent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run(_ source: Observable<SourceElement>) -> Disposable {
+ _ = self._group.insert(self._sourceSubscription)
+
+ let disposable = source.subscribe(self)
+ self._sourceSubscription.setDisposable(disposable)
+ return self._group
+ }
+
+ func subscribe(_ innerSource: SourceSequence, group: CompositeDisposable) {
+ let subscription = SingleAssignmentDisposable()
+
+ let key = group.insert(subscription)
+
+ if let key = key {
+ let observer = MergeLimitedSinkIter(parent: self, disposeKey: key)
+
+ let disposable = innerSource.asObservable().subscribe(observer)
+ subscription.setDisposable(disposable)
+ }
+ }
+
+ func performMap(_ element: SourceElement) throws -> SourceSequence {
+ rxAbstractMethod()
+ }
+
+ @inline(__always)
+ final private func nextElementArrived(element: SourceElement) -> SourceSequence? {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ let subscribe: Bool
+ if self._activeCount < self._maxConcurrent {
+ self._activeCount += 1
+ subscribe = true
+ }
+ else {
+ do {
+ let value = try self.performMap(element)
+ self._queue.enqueue(value)
+ } catch {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ subscribe = false
+ }
+
+ if subscribe {
+ do {
+ return try self.performMap(element)
+ } catch {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+
+ return nil
+ // }
+ }
+
+ func on(_ event: Event<SourceElement>) {
+ switch event {
+ case .next(let element):
+ if let sequence = self.nextElementArrived(element: element) {
+ self.subscribe(sequence, group: self._group)
+ }
+ case .error(let error):
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ if self._activeCount == 0 {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ else {
+ self._sourceSubscription.dispose()
+ }
+
+ self._stopped = true
+ }
+ }
+}
+
+final private class MergeLimited<SourceSequence: ObservableConvertibleType>: Producer<SourceSequence.Element> {
+ private let _source: Observable<SourceSequence>
+ private let _maxConcurrent: Int
+
+ init(source: Observable<SourceSequence>, maxConcurrent: Int) {
+ self._source = source
+ self._maxConcurrent = maxConcurrent
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceSequence.Element {
+ let sink = MergeLimitedBasicSink<SourceSequence, Observer>(maxConcurrent: self._maxConcurrent, observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+// MARK: Merge
+
+private final class MergeBasicSink<Source: ObservableConvertibleType, Observer: ObserverType> : MergeSink<Source, Source, Observer> where Observer.Element == Source.Element {
+ override func performMap(_ element: Source) throws -> Source {
+ return element
+ }
+}
+
+// MARK: flatMap
+
+private final class FlatMapSink<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType> : MergeSink<SourceElement, SourceSequence, Observer> where Observer.Element == SourceSequence.Element {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _selector: Selector
+
+ init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) {
+ self._selector = selector
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ override func performMap(_ element: SourceElement) throws -> SourceSequence {
+ return try self._selector(element)
+ }
+}
+
+// MARK: FlatMapFirst
+
+private final class FlatMapFirstSink<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType> : MergeSink<SourceElement, SourceSequence, Observer> where Observer.Element == SourceSequence.Element {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _selector: Selector
+
+ override var subscribeNext: Bool {
+ return self._activeCount == 0
+ }
+
+ init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) {
+ self._selector = selector
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ override func performMap(_ element: SourceElement) throws -> SourceSequence {
+ return try self._selector(element)
+ }
+}
+
+private final class MergeSinkIter<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType> : ObserverType where Observer.Element == SourceSequence.Element {
+ typealias Parent = MergeSink<SourceElement, SourceSequence, Observer>
+ typealias DisposeKey = CompositeDisposable.DisposeKey
+ typealias Element = Observer.Element
+
+ private let _parent: Parent
+ private let _disposeKey: DisposeKey
+
+ init(parent: Parent, disposeKey: DisposeKey) {
+ self._parent = parent
+ self._disposeKey = disposeKey
+ }
+
+ func on(_ event: Event<Element>) {
+ self._parent._lock.lock(); defer { self._parent._lock.unlock() } // lock {
+ switch event {
+ case .next(let value):
+ self._parent.forwardOn(.next(value))
+ case .error(let error):
+ self._parent.forwardOn(.error(error))
+ self._parent.dispose()
+ case .completed:
+ self._parent._group.remove(for: self._disposeKey)
+ self._parent._activeCount -= 1
+ self._parent.checkCompleted()
+ }
+ // }
+ }
+}
+
+
+private class MergeSink<SourceElement, SourceSequence: ObservableConvertibleType, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType where Observer.Element == SourceSequence.Element {
+ typealias ResultType = Observer.Element
+ typealias Element = SourceElement
+
+ let _lock = RecursiveLock()
+
+ var subscribeNext: Bool {
+ return true
+ }
+
+ // state
+ let _group = CompositeDisposable()
+ let _sourceSubscription = SingleAssignmentDisposable()
+
+ var _activeCount = 0
+ var _stopped = false
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func performMap(_ element: SourceElement) throws -> SourceSequence {
+ rxAbstractMethod()
+ }
+
+ @inline(__always)
+ final private func nextElementArrived(element: SourceElement) -> SourceSequence? {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if !self.subscribeNext {
+ return nil
+ }
+
+ do {
+ let value = try self.performMap(element)
+ self._activeCount += 1
+ return value
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return nil
+ }
+ // }
+ }
+
+ func on(_ event: Event<SourceElement>) {
+ switch event {
+ case .next(let element):
+ if let value = self.nextElementArrived(element: element) {
+ self.subscribeInner(value.asObservable())
+ }
+ case .error(let error):
+ self._lock.lock(); defer { self._lock.unlock() }
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self._lock.lock(); defer { self._lock.unlock() }
+ self._stopped = true
+ self._sourceSubscription.dispose()
+ self.checkCompleted()
+ }
+ }
+
+ func subscribeInner(_ source: Observable<Observer.Element>) {
+ let iterDisposable = SingleAssignmentDisposable()
+ if let disposeKey = self._group.insert(iterDisposable) {
+ let iter = MergeSinkIter(parent: self, disposeKey: disposeKey)
+ let subscription = source.subscribe(iter)
+ iterDisposable.setDisposable(subscription)
+ }
+ }
+
+ func run(_ sources: [Observable<Observer.Element>]) -> Disposable {
+ self._activeCount += sources.count
+
+ for source in sources {
+ self.subscribeInner(source)
+ }
+
+ self._stopped = true
+
+ self.checkCompleted()
+
+ return self._group
+ }
+
+ @inline(__always)
+ func checkCompleted() {
+ if self._stopped && self._activeCount == 0 {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+
+ func run(_ source: Observable<SourceElement>) -> Disposable {
+ _ = self._group.insert(self._sourceSubscription)
+
+ let subscription = source.subscribe(self)
+ self._sourceSubscription.setDisposable(subscription)
+
+ return self._group
+ }
+}
+
+// MARK: Producers
+
+final private class FlatMap<SourceElement, SourceSequence: ObservableConvertibleType>: Producer<SourceSequence.Element> {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _source: Observable<SourceElement>
+
+ private let _selector: Selector
+
+ init(source: Observable<SourceElement>, selector: @escaping Selector) {
+ self._source = source
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceSequence.Element {
+ let sink = FlatMapSink(selector: self._selector, observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class FlatMapFirst<SourceElement, SourceSequence: ObservableConvertibleType>: Producer<SourceSequence.Element> {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _source: Observable<SourceElement>
+
+ private let _selector: Selector
+
+ init(source: Observable<SourceElement>, selector: @escaping Selector) {
+ self._source = source
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceSequence.Element {
+ let sink = FlatMapFirstSink<SourceElement, SourceSequence, Observer>(selector: self._selector, observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final class ConcatMap<SourceElement, SourceSequence: ObservableConvertibleType>: Producer<SourceSequence.Element> {
+ typealias Selector = (SourceElement) throws -> SourceSequence
+
+ private let _source: Observable<SourceElement>
+ private let _selector: Selector
+
+ init(source: Observable<SourceElement>, selector: @escaping Selector) {
+ self._source = source
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceSequence.Element {
+ let sink = ConcatMapSink<SourceElement, SourceSequence, Observer>(selector: self._selector, observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final class Merge<SourceSequence: ObservableConvertibleType> : Producer<SourceSequence.Element> {
+ private let _source: Observable<SourceSequence>
+
+ init(source: Observable<SourceSequence>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == SourceSequence.Element {
+ let sink = MergeBasicSink<SourceSequence, Observer>(observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class MergeArray<Element>: Producer<Element> {
+ private let _sources: [Observable<Element>]
+
+ init(sources: [Observable<Element>]) {
+ self._sources = sources
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = MergeBasicSink<Observable<Element>, Observer>(observer: observer, cancel: cancel)
+ let subscription = sink.run(self._sources)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Multicast.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/27/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+ Represents an observable wrapper that can be connected and disconnected from its underlying observable sequence.
+ */
+public class ConnectableObservable<Element>
+ : Observable<Element>
+ , ConnectableObservableType {
+
+ /**
+ Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established.
+
+ - returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence.
+ */
+ public func connect() -> Disposable {
+ rxAbstractMethod()
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Multicasts the source sequence notifications through an instantiated subject into all uses of the sequence within a selector function.
+
+ Each subscription to the resulting sequence causes a separate multicast invocation, exposing the sequence resulting from the selector function's invocation.
+
+ For specializations with fixed subject types, see `publish` and `replay`.
+
+ - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html)
+
+ - parameter subjectSelector: Factory function to create an intermediate subject through which the source sequence's elements will be multicast to the selector function.
+ - parameter selector: Selector function which can use the multicasted source sequence subject to the policies enforced by the created subject.
+ - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
+ */
+ public func multicast<Subject: SubjectType, Result>(_ subjectSelector: @escaping () throws -> Subject, selector: @escaping (Observable<Subject.Element>) throws -> Observable<Result>)
+ -> Observable<Result> where Subject.Observer.Element == Element {
+ return Multicast(
+ source: self.asObservable(),
+ subjectSelector: subjectSelector,
+ selector: selector
+ )
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Returns a connectable observable sequence that shares a single subscription to the underlying sequence.
+
+ This operator is a specialization of `multicast` using a `PublishSubject`.
+
+ - seealso: [publish operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html)
+
+ - returns: A connectable observable sequence that shares a single subscription to the underlying sequence.
+ */
+ public func publish() -> ConnectableObservable<Element> {
+ return self.multicast { PublishSubject() }
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying bufferSize elements.
+
+ This operator is a specialization of `multicast` using a `ReplaySubject`.
+
+ - seealso: [replay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html)
+
+ - parameter bufferSize: Maximum element count of the replay buffer.
+ - returns: A connectable observable sequence that shares a single subscription to the underlying sequence.
+ */
+ public func replay(_ bufferSize: Int)
+ -> ConnectableObservable<Element> {
+ return self.multicast { ReplaySubject.create(bufferSize: bufferSize) }
+ }
+
+ /**
+ Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying all elements.
+
+ This operator is a specialization of `multicast` using a `ReplaySubject`.
+
+ - seealso: [replay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html)
+
+ - returns: A connectable observable sequence that shares a single subscription to the underlying sequence.
+ */
+ public func replayAll()
+ -> ConnectableObservable<Element> {
+ return self.multicast { ReplaySubject.createUnbounded() }
+ }
+}
+
+extension ConnectableObservableType {
+
+ /**
+ Returns an observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence.
+
+ - seealso: [refCount operator on reactivex.io](http://reactivex.io/documentation/operators/refcount.html)
+
+ - returns: An observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence.
+ */
+ public func refCount() -> Observable<Element> {
+ return RefCount(source: self)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Multicasts the source sequence notifications through the specified subject to the resulting connectable observable.
+
+ Upon connection of the connectable observable, the subject is subscribed to the source exactly one, and messages are forwarded to the observers registered with the connectable observable.
+
+ For specializations with fixed subject types, see `publish` and `replay`.
+
+ - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html)
+
+ - parameter subject: Subject to push source elements into.
+ - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject.
+ */
+ public func multicast<Subject: SubjectType>(_ subject: Subject)
+ -> ConnectableObservable<Subject.Element> where Subject.Observer.Element == Element {
+ return ConnectableObservableAdapter(source: self.asObservable(), makeSubject: { subject })
+ }
+
+ /**
+ Multicasts the source sequence notifications through an instantiated subject to the resulting connectable observable.
+
+ Upon connection of the connectable observable, the subject is subscribed to the source exactly one, and messages are forwarded to the observers registered with the connectable observable.
+
+ Subject is cleared on connection disposal or in case source sequence produces terminal event.
+
+ - seealso: [multicast operator on reactivex.io](http://reactivex.io/documentation/operators/publish.html)
+
+ - parameter makeSubject: Factory function used to instantiate a subject for each connection.
+ - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject.
+ */
+ public func multicast<Subject: SubjectType>(makeSubject: @escaping () -> Subject)
+ -> ConnectableObservable<Subject.Element> where Subject.Observer.Element == Element {
+ return ConnectableObservableAdapter(source: self.asObservable(), makeSubject: makeSubject)
+ }
+}
+
+final private class Connection<Subject: SubjectType>: ObserverType, Disposable {
+ typealias Element = Subject.Observer.Element
+
+ private var _lock: RecursiveLock
+ // state
+ private var _parent: ConnectableObservableAdapter<Subject>?
+ private var _subscription : Disposable?
+ private var _subjectObserver: Subject.Observer
+
+ private let _disposed = AtomicInt(0)
+
+ init(parent: ConnectableObservableAdapter<Subject>, subjectObserver: Subject.Observer, lock: RecursiveLock, subscription: Disposable) {
+ self._parent = parent
+ self._subscription = subscription
+ self._lock = lock
+ self._subjectObserver = subjectObserver
+ }
+
+ func on(_ event: Event<Subject.Observer.Element>) {
+ if isFlagSet(self._disposed, 1) {
+ return
+ }
+ if event.isStopEvent {
+ self.dispose()
+ }
+ self._subjectObserver.on(event)
+ }
+
+ func dispose() {
+ _lock.lock(); defer { _lock.unlock() } // {
+ fetchOr(self._disposed, 1)
+ guard let parent = _parent else {
+ return
+ }
+
+ if parent._connection === self {
+ parent._connection = nil
+ parent._subject = nil
+ }
+ self._parent = nil
+
+ self._subscription?.dispose()
+ self._subscription = nil
+ // }
+ }
+}
+
+final private class ConnectableObservableAdapter<Subject: SubjectType>
+ : ConnectableObservable<Subject.Element> {
+ typealias ConnectionType = Connection<Subject>
+
+ private let _source: Observable<Subject.Observer.Element>
+ private let _makeSubject: () -> Subject
+
+ fileprivate let _lock = RecursiveLock()
+ fileprivate var _subject: Subject?
+
+ // state
+ fileprivate var _connection: ConnectionType?
+
+ init(source: Observable<Subject.Observer.Element>, makeSubject: @escaping () -> Subject) {
+ self._source = source
+ self._makeSubject = makeSubject
+ self._subject = nil
+ self._connection = nil
+ }
+
+ override func connect() -> Disposable {
+ return self._lock.calculateLocked {
+ if let connection = self._connection {
+ return connection
+ }
+
+ let singleAssignmentDisposable = SingleAssignmentDisposable()
+ let connection = Connection(parent: self, subjectObserver: self.lazySubject.asObserver(), lock: self._lock, subscription: singleAssignmentDisposable)
+ self._connection = connection
+ let subscription = self._source.subscribe(connection)
+ singleAssignmentDisposable.setDisposable(subscription)
+ return connection
+ }
+ }
+
+ private var lazySubject: Subject {
+ if let subject = self._subject {
+ return subject
+ }
+
+ let subject = self._makeSubject()
+ self._subject = subject
+ return subject
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Subject.Element {
+ return self.lazySubject.subscribe(observer)
+ }
+}
+
+final private class RefCountSink<ConnectableSource: ConnectableObservableType, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType where ConnectableSource.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias Parent = RefCount<ConnectableSource>
+
+ private let _parent: Parent
+
+ private var _connectionIdSnapshot: Int64 = -1
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription = self._parent._source.subscribe(self)
+ self._parent._lock.lock(); defer { self._parent._lock.unlock() } // {
+
+ self._connectionIdSnapshot = self._parent._connectionId
+
+ if self.disposed {
+ return Disposables.create()
+ }
+
+ if self._parent._count == 0 {
+ self._parent._count = 1
+ self._parent._connectableSubscription = self._parent._source.connect()
+ }
+ else {
+ self._parent._count += 1
+ }
+ // }
+
+ return Disposables.create {
+ subscription.dispose()
+ self._parent._lock.lock(); defer { self._parent._lock.unlock() } // {
+ if self._parent._connectionId != self._connectionIdSnapshot {
+ return
+ }
+ if self._parent._count == 1 {
+ self._parent._count = 0
+ guard let connectableSubscription = self._parent._connectableSubscription else {
+ return
+ }
+
+ connectableSubscription.dispose()
+ self._parent._connectableSubscription = nil
+ }
+ else if self._parent._count > 1 {
+ self._parent._count -= 1
+ }
+ else {
+ rxFatalError("Something went wrong with RefCount disposing mechanism")
+ }
+ // }
+ }
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .error, .completed:
+ self._parent._lock.lock() // {
+ if self._parent._connectionId == self._connectionIdSnapshot {
+ let connection = self._parent._connectableSubscription
+ defer { connection?.dispose() }
+ self._parent._count = 0
+ self._parent._connectionId = self._parent._connectionId &+ 1
+ self._parent._connectableSubscription = nil
+ }
+ // }
+ self._parent._lock.unlock()
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+}
+
+final private class RefCount<ConnectableSource: ConnectableObservableType>: Producer<ConnectableSource.Element> {
+ fileprivate let _lock = RecursiveLock()
+
+ // state
+ fileprivate var _count = 0
+ fileprivate var _connectionId: Int64 = 0
+ fileprivate var _connectableSubscription = nil as Disposable?
+
+ fileprivate let _source: ConnectableSource
+
+ init(source: ConnectableSource) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable)
+ where Observer.Element == ConnectableSource.Element {
+ let sink = RefCountSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class MulticastSink<Subject: SubjectType, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias ResultType = Element
+ typealias MutlicastType = Multicast<Subject, Observer.Element>
+
+ private let _parent: MutlicastType
+
+ init(parent: MutlicastType, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ do {
+ let subject = try self._parent._subjectSelector()
+ let connectable = ConnectableObservableAdapter(source: self._parent._source, makeSubject: { subject })
+
+ let observable = try self._parent._selector(connectable)
+
+ let subscription = observable.subscribe(self)
+ let connection = connectable.connect()
+
+ return Disposables.create(subscription, connection)
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return Disposables.create()
+ }
+ }
+
+ func on(_ event: Event<ResultType>) {
+ self.forwardOn(event)
+ switch event {
+ case .next: break
+ case .error, .completed:
+ self.dispose()
+ }
+ }
+}
+
+final private class Multicast<Subject: SubjectType, Result>: Producer<Result> {
+ typealias SubjectSelectorType = () throws -> Subject
+ typealias SelectorType = (Observable<Subject.Element>) throws -> Observable<Result>
+
+ fileprivate let _source: Observable<Subject.Observer.Element>
+ fileprivate let _subjectSelector: SubjectSelectorType
+ fileprivate let _selector: SelectorType
+
+ init(source: Observable<Subject.Observer.Element>, subjectSelector: @escaping SubjectSelectorType, selector: @escaping SelectorType) {
+ self._source = source
+ self._subjectSelector = subjectSelector
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = MulticastSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Never.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
+
+ - seealso: [never operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence whose observers will never get called.
+ */
+ public static func never() -> Observable<Element> {
+ return NeverProducer()
+ }
+}
+
+final private class NeverProducer<Element>: Producer<Element> {
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ return Disposables.create()
+ }
+}
--- /dev/null
+//
+// NopDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a disposable that does nothing on disposal.
+///
+/// Nop = No Operation
+private struct NopDisposable : Disposable {
+
+ fileprivate static let noOp: Disposable = NopDisposable()
+
+ private init() {
+
+ }
+
+ /// Does nothing.
+ public func dispose() {
+ }
+}
+
+extension Disposables {
+ /**
+ Creates a disposable that does nothing on disposal.
+ */
+ static public func create() -> Disposable {
+ return NopDisposable.noOp
+ }
+}
--- /dev/null
+//
+// Observable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// A type-erased `ObservableType`.
+///
+/// It represents a push style sequence.
+public class Observable<Element> : ObservableType {
+ init() {
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ }
+
+ public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ rxAbstractMethod()
+ }
+
+ public func asObservable() -> Observable<Element> {
+ return self
+ }
+
+ deinit {
+#if TRACE_RESOURCES
+ _ = Resources.decrementTotal()
+#endif
+ }
+}
+
--- /dev/null
+//
+// ObservableConvertibleType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/17/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Type that can be converted to observable sequence (`Observable<Element>`).
+public protocol ObservableConvertibleType {
+ /// Type of elements in sequence.
+ associatedtype Element
+
+ @available(*, deprecated, renamed: "Element")
+ typealias E = Element
+
+ /// Converts `self` to `Observable` sequence.
+ ///
+ /// - returns: Observable sequence that represents `self`.
+ func asObservable() -> Observable<Element>
+}
--- /dev/null
+//
+// ObservableType+Extensions.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+#if DEBUG
+ import Foundation
+#endif
+
+extension ObservableType {
+ /**
+ Subscribes an event handler to an observable sequence.
+
+ - parameter on: Action to invoke for each event in the observable sequence.
+ - returns: Subscription object used to unsubscribe from the observable sequence.
+ */
+ public func subscribe(_ on: @escaping (Event<Element>) -> Void)
+ -> Disposable {
+ let observer = AnonymousObserver { e in
+ on(e)
+ }
+ return self.asObservable().subscribe(observer)
+ }
+
+
+ /**
+ Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence.
+
+ - parameter onNext: Action to invoke for each element in the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
+ - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
+ gracefully completed, errored, or if the generation is canceled by disposing subscription).
+ - returns: Subscription object used to unsubscribe from the observable sequence.
+ */
+ public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
+ -> Disposable {
+ let disposable: Disposable
+
+ if let disposed = onDisposed {
+ disposable = Disposables.create(with: disposed)
+ }
+ else {
+ disposable = Disposables.create()
+ }
+
+ #if DEBUG
+ let synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
+
+ let observer = AnonymousObserver<Element> { event in
+
+ #if DEBUG
+ synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { synchronizationTracker.unregister() }
+ #endif
+
+ switch event {
+ case .next(let value):
+ onNext?(value)
+ case .error(let error):
+ if let onError = onError {
+ onError(error)
+ }
+ else {
+ Hooks.defaultErrorHandler(callStack, error)
+ }
+ disposable.dispose()
+ case .completed:
+ onCompleted?()
+ disposable.dispose()
+ }
+ }
+ return Disposables.create(
+ self.asObservable().subscribe(observer),
+ disposable
+ )
+ }
+}
+
+import class Foundation.NSRecursiveLock
+
+extension Hooks {
+ public typealias DefaultErrorHandler = (_ subscriptionCallStack: [String], _ error: Error) -> Void
+ public typealias CustomCaptureSubscriptionCallstack = () -> [String]
+
+ private static let _lock = RecursiveLock()
+ private static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in
+ #if DEBUG
+ let serializedCallStack = subscriptionCallStack.joined(separator: "\n")
+ print("Unhandled error happened: \(error)")
+ if !serializedCallStack.isEmpty {
+ print("subscription called from:\n\(serializedCallStack)")
+ }
+ #endif
+ }
+ private static var _customCaptureSubscriptionCallstack: CustomCaptureSubscriptionCallstack = {
+ #if DEBUG
+ return Thread.callStackSymbols
+ #else
+ return []
+ #endif
+ }
+
+ /// Error handler called in case onError handler wasn't provided.
+ public static var defaultErrorHandler: DefaultErrorHandler {
+ get {
+ _lock.lock(); defer { _lock.unlock() }
+ return _defaultErrorHandler
+ }
+ set {
+ _lock.lock(); defer { _lock.unlock() }
+ _defaultErrorHandler = newValue
+ }
+ }
+
+ /// Subscription callstack block to fetch custom callstack information.
+ public static var customCaptureSubscriptionCallstack: CustomCaptureSubscriptionCallstack {
+ get {
+ _lock.lock(); defer { _lock.unlock() }
+ return _customCaptureSubscriptionCallstack
+ }
+ set {
+ _lock.lock(); defer { _lock.unlock() }
+ _customCaptureSubscriptionCallstack = newValue
+ }
+ }
+}
+
--- /dev/null
+//
+// ObservableType+PrimitiveSequence.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/17/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ The `asSingle` operator throws a `RxError.noElements` or `RxError.moreThanOneElement`
+ if the source Observable does not emit exactly one element before successfully completing.
+
+ - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html)
+
+ - returns: An observable sequence that emits a single element when the source Observable has completed, or throws an exception if more (or none) of them are emitted.
+ */
+ public func asSingle() -> Single<Element> {
+ return PrimitiveSequence(raw: AsSingle(source: self.asObservable()))
+ }
+
+ /**
+ The `first` operator emits only the very first item emitted by this Observable,
+ or nil if this Observable completes without emitting anything.
+
+ - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html)
+
+ - returns: An observable sequence that emits a single element or nil if the source observable sequence completes without emitting any items.
+ */
+ public func first() -> Single<Element?> {
+ return PrimitiveSequence(raw: First(source: self.asObservable()))
+ }
+
+ /**
+ The `asMaybe` operator throws a `RxError.moreThanOneElement`
+ if the source Observable does not emit at most one element before successfully completing.
+
+ - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html)
+
+ - returns: An observable sequence that emits a single element, completes when the source Observable has completed, or throws an exception if more of them are emitted.
+ */
+ public func asMaybe() -> Maybe<Element> {
+ return PrimitiveSequence(raw: AsMaybe(source: self.asObservable()))
+ }
+}
+
+extension ObservableType where Element == Never {
+ /**
+ - returns: An observable sequence that completes.
+ */
+ public func asCompletable()
+ -> Completable {
+ return PrimitiveSequence(raw: self.asObservable())
+ }
+}
--- /dev/null
+//
+// ObservableType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a push style sequence.
+public protocol ObservableType: ObservableConvertibleType {
+ /**
+ Subscribes `observer` to receive events for this sequence.
+
+ ### Grammar
+
+ **Next\* (Error | Completed)?**
+
+ * sequences can produce zero or more elements so zero or more `Next` events can be sent to `observer`
+ * once an `Error` or `Completed` event is sent, the sequence terminates and can't produce any other elements
+
+ It is possible that events are sent from different threads, but no two events can be sent concurrently to
+ `observer`.
+
+ ### Resource Management
+
+ When sequence sends `Complete` or `Error` event all internal resources that compute sequence elements
+ will be freed.
+
+ To cancel production of sequence elements and free resources immediately, call `dispose` on returned
+ subscription.
+
+ - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources.
+ */
+ func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element
+}
+
+extension ObservableType {
+
+ /// Default implementation of converting `ObservableType` to `Observable`.
+ public func asObservable() -> Observable<Element> {
+ // temporary workaround
+ //return Observable.create(subscribe: self.subscribe)
+ return Observable.create { o in
+ return self.subscribe(o)
+ }
+ }
+}
--- /dev/null
+//
+// ObserveOn.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 7/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Wraps the source sequence in order to run its observer callbacks on the specified scheduler.
+
+ This only invokes observer callbacks on a `scheduler`. In case the subscription and/or unsubscription
+ actions have side-effects that require to be run on a scheduler, use `subscribeOn`.
+
+ - seealso: [observeOn operator on reactivex.io](http://reactivex.io/documentation/operators/observeon.html)
+
+ - parameter scheduler: Scheduler to notify observers on.
+ - returns: The source sequence whose observations happen on the specified scheduler.
+ */
+ public func observeOn(_ scheduler: ImmediateSchedulerType)
+ -> Observable<Element> {
+ if let scheduler = scheduler as? SerialDispatchQueueScheduler {
+ return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler)
+ }
+ else {
+ return ObserveOn(source: self.asObservable(), scheduler: scheduler)
+ }
+ }
+}
+
+final private class ObserveOn<Element>: Producer<Element> {
+ let scheduler: ImmediateSchedulerType
+ let source: Observable<Element>
+
+ init(source: Observable<Element>, scheduler: ImmediateSchedulerType) {
+ self.scheduler = scheduler
+ self.source = source
+
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ObserveOnSink(scheduler: self.scheduler, observer: observer, cancel: cancel)
+ let subscription = self.source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+
+#if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+#endif
+}
+
+enum ObserveOnState : Int32 {
+ // pump is not running
+ case stopped = 0
+ // pump is running
+ case running = 1
+}
+
+final private class ObserveOnSink<Observer: ObserverType>: ObserverBase<Observer.Element> {
+ typealias Element = Observer.Element
+
+ let _scheduler: ImmediateSchedulerType
+
+ var _lock = SpinLock()
+ let _observer: Observer
+
+ // state
+ var _state = ObserveOnState.stopped
+ var _queue = Queue<Event<Element>>(capacity: 10)
+
+ let _scheduleDisposable = SerialDisposable()
+ let _cancel: Cancelable
+
+ init(scheduler: ImmediateSchedulerType, observer: Observer, cancel: Cancelable) {
+ self._scheduler = scheduler
+ self._observer = observer
+ self._cancel = cancel
+ }
+
+ override func onCore(_ event: Event<Element>) {
+ let shouldStart = self._lock.calculateLocked { () -> Bool in
+ self._queue.enqueue(event)
+
+ switch self._state {
+ case .stopped:
+ self._state = .running
+ return true
+ case .running:
+ return false
+ }
+ }
+
+ if shouldStart {
+ self._scheduleDisposable.disposable = self._scheduler.scheduleRecursive((), action: self.run)
+ }
+ }
+
+ func run(_ state: (), _ recurse: (()) -> Void) {
+ let (nextEvent, observer) = self._lock.calculateLocked { () -> (Event<Element>?, Observer) in
+ if !self._queue.isEmpty {
+ return (self._queue.dequeue(), self._observer)
+ }
+ else {
+ self._state = .stopped
+ return (nil, self._observer)
+ }
+ }
+
+ if let nextEvent = nextEvent, !self._cancel.isDisposed {
+ observer.on(nextEvent)
+ if nextEvent.isStopEvent {
+ self.dispose()
+ }
+ }
+ else {
+ return
+ }
+
+ let shouldContinue = self._shouldContinue_synchronized()
+
+ if shouldContinue {
+ recurse(())
+ }
+ }
+
+ func _shouldContinue_synchronized() -> Bool {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if !self._queue.isEmpty {
+ return true
+ }
+ else {
+ self._state = .stopped
+ return false
+ }
+ // }
+ }
+
+ override func dispose() {
+ super.dispose()
+
+ self._cancel.dispose()
+ self._scheduleDisposable.dispose()
+ }
+}
+
+#if TRACE_RESOURCES
+ private let _numberOfSerialDispatchQueueObservables = AtomicInt(0)
+ extension Resources {
+ /**
+ Counts number of `SerialDispatchQueueObservables`.
+
+ Purposed for unit tests.
+ */
+ public static var numberOfSerialDispatchQueueObservables: Int32 {
+ return load(_numberOfSerialDispatchQueueObservables)
+ }
+ }
+#endif
+
+final private class ObserveOnSerialDispatchQueueSink<Observer: ObserverType>: ObserverBase<Observer.Element> {
+ let scheduler: SerialDispatchQueueScheduler
+ let observer: Observer
+
+ let cancel: Cancelable
+
+ var cachedScheduleLambda: (((sink: ObserveOnSerialDispatchQueueSink<Observer>, event: Event<Element>)) -> Disposable)!
+
+ init(scheduler: SerialDispatchQueueScheduler, observer: Observer, cancel: Cancelable) {
+ self.scheduler = scheduler
+ self.observer = observer
+ self.cancel = cancel
+ super.init()
+
+ self.cachedScheduleLambda = { pair in
+ guard !cancel.isDisposed else { return Disposables.create() }
+
+ pair.sink.observer.on(pair.event)
+
+ if pair.event.isStopEvent {
+ pair.sink.dispose()
+ }
+
+ return Disposables.create()
+ }
+ }
+
+ override func onCore(_ event: Event<Element>) {
+ _ = self.scheduler.schedule((self, event), action: self.cachedScheduleLambda!)
+ }
+
+ override func dispose() {
+ super.dispose()
+
+ self.cancel.dispose()
+ }
+}
+
+final private class ObserveOnSerialDispatchQueue<Element>: Producer<Element> {
+ let scheduler: SerialDispatchQueueScheduler
+ let source: Observable<Element>
+
+ init(source: Observable<Element>, scheduler: SerialDispatchQueueScheduler) {
+ self.scheduler = scheduler
+ self.source = source
+
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ _ = increment(_numberOfSerialDispatchQueueObservables)
+ #endif
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ObserveOnSerialDispatchQueueSink(scheduler: self.scheduler, observer: observer, cancel: cancel)
+ let subscription = self.source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ _ = decrement(_numberOfSerialDispatchQueueObservables)
+ }
+ #endif
+}
--- /dev/null
+//
+// ObserverBase.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+class ObserverBase<Element> : Disposable, ObserverType {
+ private let _isStopped = AtomicInt(0)
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ if load(self._isStopped) == 0 {
+ self.onCore(event)
+ }
+ case .error, .completed:
+ if fetchOr(self._isStopped, 1) == 0 {
+ self.onCore(event)
+ }
+ }
+ }
+
+ func onCore(_ event: Event<Element>) {
+ rxAbstractMethod()
+ }
+
+ func dispose() {
+ fetchOr(self._isStopped, 1)
+ }
+}
--- /dev/null
+//
+// ObserverType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Supports push-style iteration over an observable sequence.
+public protocol ObserverType {
+ /// The type of elements in sequence that observer can observe.
+ associatedtype Element
+
+ @available(*, deprecated, renamed: "Element")
+ typealias E = Element
+
+ /// Notify observer about sequence event.
+ ///
+ /// - parameter event: Event that occurred.
+ func on(_ event: Event<Element>)
+}
+
+/// Convenience API extensions to provide alternate next, error, completed events
+extension ObserverType {
+
+ /// Convenience method equivalent to `on(.next(element: Element))`
+ ///
+ /// - parameter element: Next element to send to observer(s)
+ public func onNext(_ element: Element) {
+ self.on(.next(element))
+ }
+
+ /// Convenience method equivalent to `on(.completed)`
+ public func onCompleted() {
+ self.on(.completed)
+ }
+
+ /// Convenience method equivalent to `on(.error(Swift.Error))`
+ /// - parameter error: Swift.Error to send to observer(s)
+ public func onError(_ error: Swift.Error) {
+ self.on(.error(error))
+ }
+}
--- /dev/null
+//
+// OperationQueueScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/4/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import class Foundation.Operation
+import class Foundation.OperationQueue
+import class Foundation.BlockOperation
+import Dispatch
+
+/// Abstracts the work that needs to be performed on a specific `NSOperationQueue`.
+///
+/// This scheduler is suitable for cases when there is some bigger chunk of work that needs to be performed in background and you want to fine tune concurrent processing using `maxConcurrentOperationCount`.
+public class OperationQueueScheduler: ImmediateSchedulerType {
+ public let operationQueue: OperationQueue
+ public let queuePriority: Operation.QueuePriority
+
+ /// Constructs new instance of `OperationQueueScheduler` that performs work on `operationQueue`.
+ ///
+ /// - parameter operationQueue: Operation queue targeted to perform work on.
+ /// - parameter queuePriority: Queue priority which will be assigned to new operations.
+ public init(operationQueue: OperationQueue, queuePriority: Operation.QueuePriority = .normal) {
+ self.operationQueue = operationQueue
+ self.queuePriority = queuePriority
+ }
+
+ /**
+ Schedules an action to be executed recursively.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let cancel = SingleAssignmentDisposable()
+
+ let operation = BlockOperation {
+ if cancel.isDisposed {
+ return
+ }
+
+
+ cancel.setDisposable(action(state))
+ }
+
+ operation.queuePriority = self.queuePriority
+
+ self.operationQueue.addOperation(operation)
+
+ return cancel
+ }
+
+}
--- /dev/null
+//
+// Optional.swift
+// RxSwift
+//
+// Created by tarunon on 2016/12/13.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Converts a optional to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - parameter optional: Optional element in the resulting observable sequence.
+ - returns: An observable sequence containing the wrapped value or not from given optional.
+ */
+ public static func from(optional: Element?) -> Observable<Element> {
+ return ObservableOptional(optional: optional)
+ }
+
+ /**
+ Converts a optional to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - parameter optional: Optional element in the resulting observable sequence.
+ - parameter scheduler: Scheduler to send the optional element on.
+ - returns: An observable sequence containing the wrapped value or not from given optional.
+ */
+ public static func from(optional: Element?, scheduler: ImmediateSchedulerType) -> Observable<Element> {
+ return ObservableOptionalScheduled(optional: optional, scheduler: scheduler)
+ }
+}
+
+final private class ObservableOptionalScheduledSink<Observer: ObserverType>: Sink<Observer> {
+ typealias Element = Observer.Element
+ typealias Parent = ObservableOptionalScheduled<Element>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.schedule(self._parent._optional) { (optional: Element?) -> Disposable in
+ if let next = optional {
+ self.forwardOn(.next(next))
+ return self._parent._scheduler.schedule(()) { _ in
+ self.forwardOn(.completed)
+ self.dispose()
+ return Disposables.create()
+ }
+ } else {
+ self.forwardOn(.completed)
+ self.dispose()
+ return Disposables.create()
+ }
+ }
+ }
+}
+
+final private class ObservableOptionalScheduled<Element>: Producer<Element> {
+ fileprivate let _optional: Element?
+ fileprivate let _scheduler: ImmediateSchedulerType
+
+ init(optional: Element?, scheduler: ImmediateSchedulerType) {
+ self._optional = optional
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ObservableOptionalScheduledSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class ObservableOptional<Element>: Producer<Element> {
+ private let _optional: Element?
+
+ init(optional: Element?) {
+ self._optional = optional
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if let element = self._optional {
+ observer.on(.next(element))
+ }
+ observer.on(.completed)
+ return Disposables.create()
+ }
+}
--- /dev/null
+//
+// Platform.Darwin.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 12/29/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
+
+ import Darwin
+ import class Foundation.Thread
+ import protocol Foundation.NSCopying
+
+ extension Thread {
+ static func setThreadLocalStorageValue<T: AnyObject>(_ value: T?, forKey key: NSCopying) {
+ let currentThread = Thread.current
+ let threadDictionary = currentThread.threadDictionary
+
+ if let newValue = value {
+ threadDictionary[key] = newValue
+ }
+ else {
+ threadDictionary[key] = nil
+ }
+ }
+
+ static func getThreadLocalStorageValueForKey<T>(_ key: NSCopying) -> T? {
+ let currentThread = Thread.current
+ let threadDictionary = currentThread.threadDictionary
+
+ return threadDictionary[key] as? T
+ }
+ }
+
+#endif
--- /dev/null
+//
+// Platform.Linux.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 12/29/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+#if os(Linux)
+
+ import class Foundation.Thread
+
+ extension Thread {
+
+ static func setThreadLocalStorageValue<T: AnyObject>(_ value: T?, forKey key: String) {
+ if let newValue = value {
+ Thread.current.threadDictionary[key] = newValue
+ }
+ else {
+ Thread.current.threadDictionary[key] = nil
+ }
+ }
+
+ static func getThreadLocalStorageValueForKey<T: AnyObject>(_ key: String) -> T? {
+ let currentThread = Thread.current
+ let threadDictionary = currentThread.threadDictionary
+
+ return threadDictionary[key] as? T
+ }
+ }
+
+#endif
--- /dev/null
+// This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project
+//
+// PrimitiveSequence+Zip+arity.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/23/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+
+
+// 2
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, resultSelector: @escaping (E1, E2) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>)
+ -> PrimitiveSequence<Trait, (E1, E2)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, resultSelector: @escaping (E1, E2) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>)
+ -> PrimitiveSequence<Trait, (E1, E2)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable())
+ )
+ }
+}
+
+
+
+
+// 3
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, resultSelector: @escaping (E1, E2, E3) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, resultSelector: @escaping (E1, E2, E3) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable())
+ )
+ }
+}
+
+
+
+
+// 4
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, resultSelector: @escaping (E1, E2, E3, E4) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, resultSelector: @escaping (E1, E2, E3, E4) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable())
+ )
+ }
+}
+
+
+
+
+// 5
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, resultSelector: @escaping (E1, E2, E3, E4, E5) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, resultSelector: @escaping (E1, E2, E3, E4, E5) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable())
+ )
+ }
+}
+
+
+
+
+// 6
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable())
+ )
+ }
+}
+
+
+
+
+// 7
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6, E7) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6, E7)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6, E7) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6, E7)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable())
+ )
+ }
+}
+
+
+
+
+// 8
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7, E8>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, _ source8: PrimitiveSequence<Trait, E8>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(), source8.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == SingleTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7, E8>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, _ source8: PrimitiveSequence<Trait, E8>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6, E7, E8)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(), source8.asObservable())
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7, E8>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, _ source8: PrimitiveSequence<Trait, E8>, resultSelector: @escaping (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(), source8.asObservable(),
+ resultSelector: resultSelector)
+ )
+ }
+}
+
+extension PrimitiveSequenceType where Element == Any, Trait == MaybeTrait {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<E1, E2, E3, E4, E5, E6, E7, E8>(_ source1: PrimitiveSequence<Trait, E1>, _ source2: PrimitiveSequence<Trait, E2>, _ source3: PrimitiveSequence<Trait, E3>, _ source4: PrimitiveSequence<Trait, E4>, _ source5: PrimitiveSequence<Trait, E5>, _ source6: PrimitiveSequence<Trait, E6>, _ source7: PrimitiveSequence<Trait, E7>, _ source8: PrimitiveSequence<Trait, E8>)
+ -> PrimitiveSequence<Trait, (E1, E2, E3, E4, E5, E6, E7, E8)> {
+ return PrimitiveSequence(raw: Observable.zip(
+ source1.asObservable(), source2.asObservable(), source3.asObservable(), source4.asObservable(), source5.asObservable(), source6.asObservable(), source7.asObservable(), source8.asObservable())
+ )
+ }
+}
+
+
+
--- /dev/null
+//
+// PrimitiveSequence.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/5/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+/// Observable sequences containing 0 or 1 element.
+public struct PrimitiveSequence<Trait, Element> {
+ let source: Observable<Element>
+
+ init(raw: Observable<Element>) {
+ self.source = raw
+ }
+}
+
+/// Observable sequences containing 0 or 1 element
+public protocol PrimitiveSequenceType {
+ /// Additional constraints
+ associatedtype Trait
+
+ /// Sequence element type
+ associatedtype Element
+
+ @available(*, deprecated, renamed: "Trait")
+ typealias TraitType = Trait
+
+ @available(*, deprecated, renamed: "Element")
+ typealias ElementType = Element
+
+ // Converts `self` to primitive sequence.
+ ///
+ /// - returns: Observable sequence that represents `self`.
+ var primitiveSequence: PrimitiveSequence<Trait, Element> { get }
+}
+
+extension PrimitiveSequence: PrimitiveSequenceType {
+ // Converts `self` to primitive sequence.
+ ///
+ /// - returns: Observable sequence that represents `self`.
+ public var primitiveSequence: PrimitiveSequence<Trait, Element> {
+ return self
+ }
+}
+
+extension PrimitiveSequence: ObservableConvertibleType {
+ /// Converts `self` to `Observable` sequence.
+ ///
+ /// - returns: Observable sequence that represents `self`.
+ public func asObservable() -> Observable<Element> {
+ return self.source
+ }
+}
+
+extension PrimitiveSequence {
+ /**
+ Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes.
+
+ - seealso: [defer operator on reactivex.io](http://reactivex.io/documentation/operators/defer.html)
+
+ - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence.
+ - returns: An observable sequence whose observers trigger an invocation of the given observable factory function.
+ */
+ public static func deferred(_ observableFactory: @escaping () throws -> PrimitiveSequence<Trait, Element>)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.deferred {
+ try observableFactory().asObservable()
+ })
+ }
+
+ /**
+ Returns an observable sequence by the source observable sequence shifted forward in time by a specified delay. Error events from the source observable sequence are not delayed.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the source by.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: the source Observable shifted in time by the specified delay.
+ */
+ public func delay(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.primitiveSequence.source.delay(dueTime, scheduler: scheduler))
+ }
+
+ /**
+ Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers.
+
+ - seealso: [delay operator on reactivex.io](http://reactivex.io/documentation/operators/delay.html)
+
+ - parameter dueTime: Relative time shift of the subscription.
+ - parameter scheduler: Scheduler to run the subscription delay timer on.
+ - returns: Time-shifted sequence.
+ */
+ public func delaySubscription(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.delaySubscription(dueTime, scheduler: scheduler))
+ }
+
+ /**
+ Wraps the source sequence in order to run its observer callbacks on the specified scheduler.
+
+ This only invokes observer callbacks on a `scheduler`. In case the subscription and/or unsubscription
+ actions have side-effects that require to be run on a scheduler, use `subscribeOn`.
+
+ - seealso: [observeOn operator on reactivex.io](http://reactivex.io/documentation/operators/observeon.html)
+
+ - parameter scheduler: Scheduler to notify observers on.
+ - returns: The source sequence whose observations happen on the specified scheduler.
+ */
+ public func observeOn(_ scheduler: ImmediateSchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.observeOn(scheduler))
+ }
+
+ /**
+ Wraps the source sequence in order to run its subscription and unsubscription logic on the specified
+ scheduler.
+
+ This operation is not commonly used.
+
+ This only performs the side-effects of subscription and unsubscription on the specified scheduler.
+
+ In order to invoke observer callbacks on a `scheduler`, use `observeOn`.
+
+ - seealso: [subscribeOn operator on reactivex.io](http://reactivex.io/documentation/operators/subscribeon.html)
+
+ - parameter scheduler: Scheduler to perform subscription and unsubscription actions on.
+ - returns: The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler.
+ */
+ public func subscribeOn(_ scheduler: ImmediateSchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.subscribeOn(scheduler))
+ }
+
+ /**
+ Continues an observable sequence that is terminated by an error with the observable sequence produced by the handler.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - parameter handler: Error handler function, producing another observable sequence.
+ - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred.
+ */
+ public func catchError(_ handler: @escaping (Swift.Error) throws -> PrimitiveSequence<Trait, Element>)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.catchError { try handler($0).asObservable() })
+ }
+
+ /**
+ If the initial subscription to the observable sequence emits an error event, try repeating it up to the specified number of attempts (inclusive of the initial attempt) or until is succeeds. For example, if you want to retry a sequence once upon failure, you should use retry(2) (once for the initial attempt, and once for the retry).
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter maxAttemptCount: Maximum number of times to attempt the sequence subscription.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully.
+ */
+ public func retry(_ maxAttemptCount: Int)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.retry(maxAttemptCount))
+ }
+
+ /**
+ Repeats the source observable sequence on error when the notifier emits a next value.
+ If the source observable errors and the notifier completes, it will complete the source sequence.
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete.
+ */
+ public func retryWhen<TriggerObservable: ObservableType, Error: Swift.Error>(_ notificationHandler: @escaping (Observable<Error>) -> TriggerObservable)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.retryWhen(notificationHandler))
+ }
+
+ /**
+ Repeats the source observable sequence on error when the notifier emits a next value.
+ If the source observable errors and the notifier completes, it will complete the source sequence.
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete.
+ */
+ public func retryWhen<TriggerObservable: ObservableType>(_ notificationHandler: @escaping (Observable<Swift.Error>) -> TriggerObservable)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.retryWhen(notificationHandler))
+ }
+
+ /**
+ Prints received events for all observers on standard output.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter identifier: Identifier that is printed together with event description to standard output.
+ - parameter trimOutput: Should output be trimmed to max 40 characters.
+ - returns: An observable sequence whose events are printed to standard output.
+ */
+ public func debug(_ identifier: String? = nil, trimOutput: Bool = false, file: String = #file, line: UInt = #line, function: String = #function)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.source.debug(identifier, trimOutput: trimOutput, file: file, line: line, function: function))
+ }
+
+ /**
+ Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime.
+
+ - seealso: [using operator on reactivex.io](http://reactivex.io/documentation/operators/using.html)
+
+ - parameter resourceFactory: Factory function to obtain a resource object.
+ - parameter primitiveSequenceFactory: Factory function to obtain an observable sequence that depends on the obtained resource.
+ - returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object.
+ */
+ public static func using<Resource: Disposable>(_ resourceFactory: @escaping () throws -> Resource, primitiveSequenceFactory: @escaping (Resource) throws -> PrimitiveSequence<Trait, Element>)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable.using(resourceFactory, observableFactory: { (resource: Resource) throws -> Observable<Element> in
+ return try primitiveSequenceFactory(resource).asObservable()
+ }))
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence. If the next element isn't received within the specified timeout duration starting from its predecessor, a TimeoutError is propagated to the observer.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: An observable sequence with a `RxError.timeout` in case of a timeout.
+ */
+ public func timeout(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence<Trait, Element>(raw: self.primitiveSequence.source.timeout(dueTime, scheduler: scheduler))
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence, using the specified scheduler to run timeout timers. If the next element isn't received within the specified timeout duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter other: Sequence to return in case of a timeout.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: The source sequence switching to the other sequence in case of a timeout.
+ */
+ public func timeout(_ dueTime: RxTimeInterval,
+ other: PrimitiveSequence<Trait, Element>,
+ scheduler: SchedulerType) -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence<Trait, Element>(raw: self.primitiveSequence.source.timeout(dueTime, other: other.source, scheduler: scheduler))
+ }
+}
+
+extension PrimitiveSequenceType where Element: RxAbstractInteger
+{
+ /**
+ Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers.
+
+ - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html)
+
+ - parameter dueTime: Relative time at which to produce the first value.
+ - parameter scheduler: Scheduler to run timers on.
+ - returns: An observable sequence that produces a value after due time has elapsed and then each period.
+ */
+ public static func timer(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: Observable<Element>.timer(dueTime, scheduler: scheduler))
+ }
+}
--- /dev/null
+//
+// PriorityQueue.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 12/27/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+struct PriorityQueue<Element> {
+ private let _hasHigherPriority: (Element, Element) -> Bool
+ private let _isEqual: (Element, Element) -> Bool
+
+ private var _elements = [Element]()
+
+ init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) {
+ _hasHigherPriority = hasHigherPriority
+ _isEqual = isEqual
+ }
+
+ mutating func enqueue(_ element: Element) {
+ _elements.append(element)
+ bubbleToHigherPriority(_elements.count - 1)
+ }
+
+ func peek() -> Element? {
+ return _elements.first
+ }
+
+ var isEmpty: Bool {
+ return _elements.count == 0
+ }
+
+ mutating func dequeue() -> Element? {
+ guard let front = peek() else {
+ return nil
+ }
+
+ removeAt(0)
+
+ return front
+ }
+
+ mutating func remove(_ element: Element) {
+ for i in 0 ..< _elements.count {
+ if _isEqual(_elements[i], element) {
+ removeAt(i)
+ return
+ }
+ }
+ }
+
+ private mutating func removeAt(_ index: Int) {
+ let removingLast = index == _elements.count - 1
+ if !removingLast {
+ _elements.swapAt(index, _elements.count - 1)
+ }
+
+ _ = _elements.popLast()
+
+ if !removingLast {
+ bubbleToHigherPriority(index)
+ bubbleToLowerPriority(index)
+ }
+ }
+
+ private mutating func bubbleToHigherPriority(_ initialUnbalancedIndex: Int) {
+ precondition(initialUnbalancedIndex >= 0)
+ precondition(initialUnbalancedIndex < _elements.count)
+
+ var unbalancedIndex = initialUnbalancedIndex
+
+ while unbalancedIndex > 0 {
+ let parentIndex = (unbalancedIndex - 1) / 2
+ guard _hasHigherPriority(_elements[unbalancedIndex], _elements[parentIndex]) else { break }
+ _elements.swapAt(unbalancedIndex, parentIndex)
+ unbalancedIndex = parentIndex
+ }
+ }
+
+ private mutating func bubbleToLowerPriority(_ initialUnbalancedIndex: Int) {
+ precondition(initialUnbalancedIndex >= 0)
+ precondition(initialUnbalancedIndex < _elements.count)
+
+ var unbalancedIndex = initialUnbalancedIndex
+ while true {
+ let leftChildIndex = unbalancedIndex * 2 + 1
+ let rightChildIndex = unbalancedIndex * 2 + 2
+
+ var highestPriorityIndex = unbalancedIndex
+
+ if leftChildIndex < _elements.count && _hasHigherPriority(_elements[leftChildIndex], _elements[highestPriorityIndex]) {
+ highestPriorityIndex = leftChildIndex
+ }
+
+ if rightChildIndex < _elements.count && _hasHigherPriority(_elements[rightChildIndex], _elements[highestPriorityIndex]) {
+ highestPriorityIndex = rightChildIndex
+ }
+
+ guard highestPriorityIndex != unbalancedIndex else { break }
+ _elements.swapAt(highestPriorityIndex, unbalancedIndex)
+
+ unbalancedIndex = highestPriorityIndex
+ }
+ }
+}
+
+extension PriorityQueue : CustomDebugStringConvertible {
+ var debugDescription: String {
+ return _elements.debugDescription
+ }
+}
--- /dev/null
+//
+// Producer.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/20/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+class Producer<Element> : Observable<Element> {
+ override init() {
+ super.init()
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if !CurrentThreadScheduler.isScheduleRequired {
+ // The returned disposable needs to release all references once it was disposed.
+ let disposer = SinkDisposer()
+ let sinkAndSubscription = self.run(observer, cancel: disposer)
+ disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
+
+ return disposer
+ }
+ else {
+ return CurrentThreadScheduler.instance.schedule(()) { _ in
+ let disposer = SinkDisposer()
+ let sinkAndSubscription = self.run(observer, cancel: disposer)
+ disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
+
+ return disposer
+ }
+ }
+ }
+
+ func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ rxAbstractMethod()
+ }
+}
+
+private final class SinkDisposer: Cancelable {
+ private enum DisposeState: Int32 {
+ case disposed = 1
+ case sinkAndSubscriptionSet = 2
+ }
+
+ private let _state = AtomicInt(0)
+ private var _sink: Disposable?
+ private var _subscription: Disposable?
+
+ var isDisposed: Bool {
+ return isFlagSet(self._state, DisposeState.disposed.rawValue)
+ }
+
+ func setSinkAndSubscription(sink: Disposable, subscription: Disposable) {
+ self._sink = sink
+ self._subscription = subscription
+
+ let previousState = fetchOr(self._state, DisposeState.sinkAndSubscriptionSet.rawValue)
+ if (previousState & DisposeState.sinkAndSubscriptionSet.rawValue) != 0 {
+ rxFatalError("Sink and subscription were already set")
+ }
+
+ if (previousState & DisposeState.disposed.rawValue) != 0 {
+ sink.dispose()
+ subscription.dispose()
+ self._sink = nil
+ self._subscription = nil
+ }
+ }
+
+ func dispose() {
+ let previousState = fetchOr(self._state, DisposeState.disposed.rawValue)
+
+ if (previousState & DisposeState.disposed.rawValue) != 0 {
+ return
+ }
+
+ if (previousState & DisposeState.sinkAndSubscriptionSet.rawValue) != 0 {
+ guard let sink = self._sink else {
+ rxFatalError("Sink not set")
+ }
+ guard let subscription = self._subscription else {
+ rxFatalError("Subscription not set")
+ }
+
+ sink.dispose()
+ subscription.dispose()
+
+ self._sink = nil
+ self._subscription = nil
+ }
+ }
+}
--- /dev/null
+//
+// PublishSubject.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/11/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an object that is both an observable sequence as well as an observer.
+///
+/// Each notification is broadcasted to all subscribed observers.
+public final class PublishSubject<Element>
+ : Observable<Element>
+ , SubjectType
+ , Cancelable
+ , ObserverType
+ , SynchronizedUnsubscribeType {
+ public typealias SubjectObserverType = PublishSubject<Element>
+
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ /// Indicates whether the subject has any observers
+ public var hasObservers: Bool {
+ self._lock.lock()
+ let count = self._observers.count > 0
+ self._lock.unlock()
+ return count
+ }
+
+ private let _lock = RecursiveLock()
+
+ // state
+ private var _isDisposed = false
+ private var _observers = Observers()
+ private var _stopped = false
+ private var _stoppedEvent = nil as Event<Element>?
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ /// Indicates whether the subject has been isDisposed.
+ public var isDisposed: Bool {
+ return self._isDisposed
+ }
+
+ /// Creates a subject.
+ public override init() {
+ super.init()
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ /// Notifies all subscribed observers about next event.
+ ///
+ /// - parameter event: Event to send to the observers.
+ public func on(_ event: Event<Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ dispatch(self._synchronized_on(event), event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) -> Observers {
+ self._lock.lock(); defer { self._lock.unlock() }
+ switch event {
+ case .next:
+ if self._isDisposed || self._stopped {
+ return Observers()
+ }
+
+ return self._observers
+ case .completed, .error:
+ if self._stoppedEvent == nil {
+ self._stoppedEvent = event
+ self._stopped = true
+ let observers = self._observers
+ self._observers.removeAll()
+ return observers
+ }
+
+ return Observers()
+ }
+ }
+
+ /**
+ Subscribes an observer to the subject.
+
+ - parameter observer: Observer to subscribe to the subject.
+ - returns: Disposable object that can be used to unsubscribe the observer from the subject.
+ */
+ public override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock()
+ let subscription = self._synchronized_subscribe(observer)
+ self._lock.unlock()
+ return subscription
+ }
+
+ func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if let stoppedEvent = self._stoppedEvent {
+ observer.on(stoppedEvent)
+ return Disposables.create()
+ }
+
+ if self._isDisposed {
+ observer.on(.error(RxError.disposed(object: self)))
+ return Disposables.create()
+ }
+
+ let key = self._observers.insert(observer.on)
+ return SubscriptionDisposable(owner: self, key: key)
+ }
+
+ func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock()
+ self._synchronized_unsubscribe(disposeKey)
+ self._lock.unlock()
+ }
+
+ func _synchronized_unsubscribe(_ disposeKey: DisposeKey) {
+ _ = self._observers.removeKey(disposeKey)
+ }
+
+ /// Returns observer interface for subject.
+ public func asObserver() -> PublishSubject<Element> {
+ return self
+ }
+
+ /// Unsubscribe all observers and release resources.
+ public func dispose() {
+ self._lock.lock()
+ self._synchronized_dispose()
+ self._lock.unlock()
+ }
+
+ final func _synchronized_dispose() {
+ self._isDisposed = true
+ self._observers.removeAll()
+ self._stoppedEvent = nil
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
--- /dev/null
+//
+// Queue.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 3/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+Data structure that represents queue.
+
+Complexity of `enqueue`, `dequeue` is O(1) when number of operations is
+averaged over N operations.
+
+Complexity of `peek` is O(1).
+*/
+struct Queue<T>: Sequence {
+ /// Type of generator.
+ typealias Generator = AnyIterator<T>
+
+ private let _resizeFactor = 2
+
+ private var _storage: ContiguousArray<T?>
+ private var _count = 0
+ private var _pushNextIndex = 0
+ private let _initialCapacity: Int
+
+ /**
+ Creates new queue.
+
+ - parameter capacity: Capacity of newly created queue.
+ */
+ init(capacity: Int) {
+ _initialCapacity = capacity
+
+ _storage = ContiguousArray<T?>(repeating: nil, count: capacity)
+ }
+
+ private var dequeueIndex: Int {
+ let index = _pushNextIndex - count
+ return index < 0 ? index + _storage.count : index
+ }
+
+ /// - returns: Is queue empty.
+ var isEmpty: Bool {
+ return count == 0
+ }
+
+ /// - returns: Number of elements inside queue.
+ var count: Int {
+ return _count
+ }
+
+ /// - returns: Element in front of a list of elements to `dequeue`.
+ func peek() -> T {
+ precondition(count > 0)
+
+ return _storage[dequeueIndex]!
+ }
+
+ mutating private func resizeTo(_ size: Int) {
+ var newStorage = ContiguousArray<T?>(repeating: nil, count: size)
+
+ let count = _count
+
+ let dequeueIndex = self.dequeueIndex
+ let spaceToEndOfQueue = _storage.count - dequeueIndex
+
+ // first batch is from dequeue index to end of array
+ let countElementsInFirstBatch = Swift.min(count, spaceToEndOfQueue)
+ // second batch is wrapped from start of array to end of queue
+ let numberOfElementsInSecondBatch = count - countElementsInFirstBatch
+
+ newStorage[0 ..< countElementsInFirstBatch] = _storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)]
+ newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = _storage[0 ..< numberOfElementsInSecondBatch]
+
+ _count = count
+ _pushNextIndex = count
+ _storage = newStorage
+ }
+
+ /// Enqueues `element`.
+ ///
+ /// - parameter element: Element to enqueue.
+ mutating func enqueue(_ element: T) {
+ if count == _storage.count {
+ resizeTo(Swift.max(_storage.count, 1) * _resizeFactor)
+ }
+
+ _storage[_pushNextIndex] = element
+ _pushNextIndex += 1
+ _count += 1
+
+ if _pushNextIndex >= _storage.count {
+ _pushNextIndex -= _storage.count
+ }
+ }
+
+ private mutating func dequeueElementOnly() -> T {
+ precondition(count > 0)
+
+ let index = dequeueIndex
+
+ defer {
+ _storage[index] = nil
+ _count -= 1
+ }
+
+ return _storage[index]!
+ }
+
+ /// Dequeues element or throws an exception in case queue is empty.
+ ///
+ /// - returns: Dequeued element.
+ mutating func dequeue() -> T? {
+ if self.count == 0 {
+ return nil
+ }
+
+ defer {
+ let downsizeLimit = _storage.count / (_resizeFactor * _resizeFactor)
+ if _count < downsizeLimit && downsizeLimit >= _initialCapacity {
+ resizeTo(_storage.count / _resizeFactor)
+ }
+ }
+
+ return dequeueElementOnly()
+ }
+
+ /// - returns: Generator of contained elements.
+ func makeIterator() -> AnyIterator<T> {
+ var i = dequeueIndex
+ var count = _count
+
+ return AnyIterator {
+ if count == 0 {
+ return nil
+ }
+
+ defer {
+ count -= 1
+ i += 1
+ }
+
+ if i >= self._storage.count {
+ i -= self._storage.count
+ }
+
+ return self._storage[i]
+ }
+ }
+}
--- /dev/null
+//
+// Range.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/13/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType where Element : RxAbstractInteger {
+ /**
+ Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to generate and send out observer messages.
+
+ - seealso: [range operator on reactivex.io](http://reactivex.io/documentation/operators/range.html)
+
+ - parameter start: The value of the first integer in the sequence.
+ - parameter count: The number of sequential integers to generate.
+ - parameter scheduler: Scheduler to run the generator loop on.
+ - returns: An observable sequence that contains a range of sequential integral numbers.
+ */
+ public static func range(start: Element, count: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<Element> {
+ return RangeProducer<Element>(start: start, count: count, scheduler: scheduler)
+ }
+}
+
+final private class RangeProducer<Element: RxAbstractInteger>: Producer<Element> {
+ fileprivate let _start: Element
+ fileprivate let _count: Element
+ fileprivate let _scheduler: ImmediateSchedulerType
+
+ init(start: Element, count: Element, scheduler: ImmediateSchedulerType) {
+ guard count >= 0 else {
+ rxFatalError("count can't be negative")
+ }
+
+ guard start &+ (count - 1) >= start || count == 0 else {
+ rxFatalError("overflow of count")
+ }
+
+ self._start = start
+ self._count = count
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = RangeSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class RangeSink<Observer: ObserverType>: Sink<Observer> where Observer.Element: RxAbstractInteger {
+ typealias Parent = RangeProducer<Observer.Element>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.scheduleRecursive(0 as Observer.Element) { i, recurse in
+ if i < self._parent._count {
+ self.forwardOn(.next(self._parent._start + i))
+ recurse(i + 1)
+ }
+ else {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ }
+}
--- /dev/null
+//
+// Reactive.swift
+// RxSwift
+//
+// Created by Yury Korolev on 5/2/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+ Use `Reactive` proxy as customization point for constrained protocol extensions.
+
+ General pattern would be:
+
+ // 1. Extend Reactive protocol with constrain on Base
+ // Read as: Reactive Extension where Base is a SomeType
+ extension Reactive where Base: SomeType {
+ // 2. Put any specific reactive extension for SomeType here
+ }
+
+ With this approach we can have more specialized methods and properties using
+ `Base` and not just specialized on common base type.
+
+ */
+
+public struct Reactive<Base> {
+ /// Base object to extend.
+ public let base: Base
+
+ /// Creates extensions with base object.
+ ///
+ /// - parameter base: Base object.
+ public init(_ base: Base) {
+ self.base = base
+ }
+}
+
+/// A type that has reactive extensions.
+public protocol ReactiveCompatible {
+ /// Extended type
+ associatedtype ReactiveBase
+
+ @available(*, deprecated, renamed: "ReactiveBase")
+ typealias CompatibleType = ReactiveBase
+
+ /// Reactive extensions.
+ static var rx: Reactive<ReactiveBase>.Type { get set }
+
+ /// Reactive extensions.
+ var rx: Reactive<ReactiveBase> { get set }
+}
+
+extension ReactiveCompatible {
+ /// Reactive extensions.
+ public static var rx: Reactive<Self>.Type {
+ get {
+ return Reactive<Self>.self
+ }
+ // swiftlint:disable:next unused_setter_value
+ set {
+ // this enables using Reactive to "mutate" base type
+ }
+ }
+
+ /// Reactive extensions.
+ public var rx: Reactive<Self> {
+ get {
+ return Reactive(self)
+ }
+ // swiftlint:disable:next unused_setter_value
+ set {
+ // this enables using Reactive to "mutate" base object
+ }
+ }
+}
+
+import class Foundation.NSObject
+
+/// Extend NSObject with `rx` proxy.
+extension NSObject: ReactiveCompatible { }
--- /dev/null
+//
+// RecursiveLock.swift
+// Platform
+//
+// Created by Krunoslav Zaher on 12/18/16.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+import class Foundation.NSRecursiveLock
+
+#if TRACE_RESOURCES
+ class RecursiveLock: NSRecursiveLock {
+ override init() {
+ _ = Resources.incrementTotal()
+ super.init()
+ }
+
+ override func lock() {
+ super.lock()
+ _ = Resources.incrementTotal()
+ }
+
+ override func unlock() {
+ super.unlock()
+ _ = Resources.decrementTotal()
+ }
+
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ }
+#else
+ typealias RecursiveLock = NSRecursiveLock
+#endif
--- /dev/null
+//
+// RecursiveScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+private enum ScheduleState {
+ case initial
+ case added(CompositeDisposable.DisposeKey)
+ case done
+}
+
+/// Type erased recursive scheduler.
+final class AnyRecursiveScheduler<State> {
+
+ typealias Action = (State, AnyRecursiveScheduler<State>) -> Void
+
+ private let _lock = RecursiveLock()
+
+ // state
+ private let _group = CompositeDisposable()
+
+ private var _scheduler: SchedulerType
+ private var _action: Action?
+
+ init(scheduler: SchedulerType, action: @escaping Action) {
+ self._action = action
+ self._scheduler = scheduler
+ }
+
+ /**
+ Schedules an action to be executed recursively.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the recursive action.
+ */
+ func schedule(_ state: State, dueTime: RxTimeInterval) {
+ var scheduleState: ScheduleState = .initial
+
+ let d = self._scheduler.scheduleRelative(state, dueTime: dueTime) { state -> Disposable in
+ // best effort
+ if self._group.isDisposed {
+ return Disposables.create()
+ }
+
+ let action = self._lock.calculateLocked { () -> Action? in
+ switch scheduleState {
+ case let .added(removeKey):
+ self._group.remove(for: removeKey)
+ case .initial:
+ break
+ case .done:
+ break
+ }
+
+ scheduleState = .done
+
+ return self._action
+ }
+
+ if let action = action {
+ action(state, self)
+ }
+
+ return Disposables.create()
+ }
+
+ self._lock.performLocked {
+ switch scheduleState {
+ case .added:
+ rxFatalError("Invalid state")
+ case .initial:
+ if let removeKey = self._group.insert(d) {
+ scheduleState = .added(removeKey)
+ }
+ else {
+ scheduleState = .done
+ }
+ case .done:
+ break
+ }
+ }
+ }
+
+ /// Schedules an action to be executed recursively.
+ ///
+ /// - parameter state: State passed to the action to be executed.
+ func schedule(_ state: State) {
+ var scheduleState: ScheduleState = .initial
+
+ let d = self._scheduler.schedule(state) { state -> Disposable in
+ // best effort
+ if self._group.isDisposed {
+ return Disposables.create()
+ }
+
+ let action = self._lock.calculateLocked { () -> Action? in
+ switch scheduleState {
+ case let .added(removeKey):
+ self._group.remove(for: removeKey)
+ case .initial:
+ break
+ case .done:
+ break
+ }
+
+ scheduleState = .done
+
+ return self._action
+ }
+
+ if let action = action {
+ action(state, self)
+ }
+
+ return Disposables.create()
+ }
+
+ self._lock.performLocked {
+ switch scheduleState {
+ case .added:
+ rxFatalError("Invalid state")
+ case .initial:
+ if let removeKey = self._group.insert(d) {
+ scheduleState = .added(removeKey)
+ }
+ else {
+ scheduleState = .done
+ }
+ case .done:
+ break
+ }
+ }
+ }
+
+ func dispose() {
+ self._lock.performLocked {
+ self._action = nil
+ }
+ self._group.dispose()
+ }
+}
+
+/// Type erased recursive scheduler.
+final class RecursiveImmediateScheduler<State> {
+ typealias Action = (_ state: State, _ recurse: (State) -> Void) -> Void
+
+ private var _lock = SpinLock()
+ private let _group = CompositeDisposable()
+
+ private var _action: Action?
+ private let _scheduler: ImmediateSchedulerType
+
+ init(action: @escaping Action, scheduler: ImmediateSchedulerType) {
+ self._action = action
+ self._scheduler = scheduler
+ }
+
+ // immediate scheduling
+
+ /// Schedules an action to be executed recursively.
+ ///
+ /// - parameter state: State passed to the action to be executed.
+ func schedule(_ state: State) {
+ var scheduleState: ScheduleState = .initial
+
+ let d = self._scheduler.schedule(state) { state -> Disposable in
+ // best effort
+ if self._group.isDisposed {
+ return Disposables.create()
+ }
+
+ let action = self._lock.calculateLocked { () -> Action? in
+ switch scheduleState {
+ case let .added(removeKey):
+ self._group.remove(for: removeKey)
+ case .initial:
+ break
+ case .done:
+ break
+ }
+
+ scheduleState = .done
+
+ return self._action
+ }
+
+ if let action = action {
+ action(state, self.schedule)
+ }
+
+ return Disposables.create()
+ }
+
+ self._lock.performLocked {
+ switch scheduleState {
+ case .added:
+ rxFatalError("Invalid state")
+ case .initial:
+ if let removeKey = self._group.insert(d) {
+ scheduleState = .added(removeKey)
+ }
+ else {
+ scheduleState = .done
+ }
+ case .done:
+ break
+ }
+ }
+ }
+
+ func dispose() {
+ self._lock.performLocked {
+ self._action = nil
+ }
+ self._group.dispose()
+ }
+}
--- /dev/null
+//
+// Reduce.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/1/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+
+extension ObservableType {
+ /**
+ Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value.
+
+ For aggregation behavior with incremental intermediate results, see `scan`.
+
+ - seealso: [reduce operator on reactivex.io](http://reactivex.io/documentation/operators/reduce.html)
+
+ - parameter seed: The initial accumulator value.
+ - parameter accumulator: A accumulator function to be invoked on each element.
+ - parameter mapResult: A function to transform the final accumulator value into the result value.
+ - returns: An observable sequence containing a single element with the final accumulator value.
+ */
+ public func reduce<A, Result>(_ seed: A, accumulator: @escaping (A, Element) throws -> A, mapResult: @escaping (A) throws -> Result)
+ -> Observable<Result> {
+ return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: mapResult)
+ }
+
+ /**
+ Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value.
+
+ For aggregation behavior with incremental intermediate results, see `scan`.
+
+ - seealso: [reduce operator on reactivex.io](http://reactivex.io/documentation/operators/reduce.html)
+
+ - parameter seed: The initial accumulator value.
+ - parameter accumulator: A accumulator function to be invoked on each element.
+ - returns: An observable sequence containing a single element with the final accumulator value.
+ */
+ public func reduce<A>(_ seed: A, accumulator: @escaping (A, Element) throws -> A)
+ -> Observable<A> {
+ return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: { $0 })
+ }
+}
+
+final private class ReduceSink<SourceType, AccumulateType, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias ResultType = Observer.Element
+ typealias Parent = Reduce<SourceType, AccumulateType, ResultType>
+
+ private let _parent: Parent
+ private var _accumulation: AccumulateType
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._accumulation = parent._seed
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case .next(let value):
+ do {
+ self._accumulation = try self._parent._accumulator(self._accumulation, value)
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .error(let e):
+ self.forwardOn(.error(e))
+ self.dispose()
+ case .completed:
+ do {
+ let result = try self._parent._mapResult(self._accumulation)
+ self.forwardOn(.next(result))
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ }
+ }
+}
+
+final private class Reduce<SourceType, AccumulateType, ResultType>: Producer<ResultType> {
+ typealias AccumulatorType = (AccumulateType, SourceType) throws -> AccumulateType
+ typealias ResultSelectorType = (AccumulateType) throws -> ResultType
+
+ private let _source: Observable<SourceType>
+ fileprivate let _seed: AccumulateType
+ fileprivate let _accumulator: AccumulatorType
+ fileprivate let _mapResult: ResultSelectorType
+
+ init(source: Observable<SourceType>, seed: AccumulateType, accumulator: @escaping AccumulatorType, mapResult: @escaping ResultSelectorType) {
+ self._source = source
+ self._seed = seed
+ self._accumulator = accumulator
+ self._mapResult = mapResult
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == ResultType {
+ let sink = ReduceSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
--- /dev/null
+//
+// RefCountDisposable.swift
+// RxSwift
+//
+// Created by Junior B. on 10/29/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed.
+public final class RefCountDisposable : DisposeBase, Cancelable {
+ private var _lock = SpinLock()
+ private var _disposable = nil as Disposable?
+ private var _primaryDisposed = false
+ private var _count = 0
+
+ /// - returns: Was resource disposed.
+ public var isDisposed: Bool {
+ self._lock.lock(); defer { self._lock.unlock() }
+ return self._disposable == nil
+ }
+
+ /// Initializes a new instance of the `RefCountDisposable`.
+ public init(disposable: Disposable) {
+ self._disposable = disposable
+ super.init()
+ }
+
+ /**
+ Holds a dependent disposable that when disposed decreases the refcount on the underlying disposable.
+
+ When getter is called, a dependent disposable contributing to the reference count that manages the underlying disposable's lifetime is returned.
+ */
+ public func retain() -> Disposable {
+ return self._lock.calculateLocked {
+ if self._disposable != nil {
+ do {
+ _ = try incrementChecked(&self._count)
+ } catch {
+ rxFatalError("RefCountDisposable increment failed")
+ }
+
+ return RefCountInnerDisposable(self)
+ } else {
+ return Disposables.create()
+ }
+ }
+ }
+
+ /// Disposes the underlying disposable only when all dependent disposables have been disposed.
+ public func dispose() {
+ let oldDisposable: Disposable? = self._lock.calculateLocked {
+ if let oldDisposable = self._disposable, !self._primaryDisposed {
+ self._primaryDisposed = true
+
+ if self._count == 0 {
+ self._disposable = nil
+ return oldDisposable
+ }
+ }
+
+ return nil
+ }
+
+ if let disposable = oldDisposable {
+ disposable.dispose()
+ }
+ }
+
+ fileprivate func release() {
+ let oldDisposable: Disposable? = self._lock.calculateLocked {
+ if let oldDisposable = self._disposable {
+ do {
+ _ = try decrementChecked(&self._count)
+ } catch {
+ rxFatalError("RefCountDisposable decrement on release failed")
+ }
+
+ guard self._count >= 0 else {
+ rxFatalError("RefCountDisposable counter is lower than 0")
+ }
+
+ if self._primaryDisposed && self._count == 0 {
+ self._disposable = nil
+ return oldDisposable
+ }
+ }
+
+ return nil
+ }
+
+ if let disposable = oldDisposable {
+ disposable.dispose()
+ }
+ }
+}
+
+internal final class RefCountInnerDisposable: DisposeBase, Disposable
+{
+ private let _parent: RefCountDisposable
+ private let _isDisposed = AtomicInt(0)
+
+ init(_ parent: RefCountDisposable) {
+ self._parent = parent
+ super.init()
+ }
+
+ internal func dispose()
+ {
+ if fetchOr(self._isDisposed, 1) == 0 {
+ self._parent.release()
+ }
+ }
+}
--- /dev/null
+//
+// Repeat.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/13/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Generates an observable sequence that repeats the given element infinitely, using the specified scheduler to send out observer messages.
+
+ - seealso: [repeat operator on reactivex.io](http://reactivex.io/documentation/operators/repeat.html)
+
+ - parameter element: Element to repeat.
+ - parameter scheduler: Scheduler to run the producer loop on.
+ - returns: An observable sequence that repeats the given element infinitely.
+ */
+ public static func repeatElement(_ element: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<Element> {
+ return RepeatElement(element: element, scheduler: scheduler)
+ }
+}
+
+final private class RepeatElement<Element>: Producer<Element> {
+ fileprivate let _element: Element
+ fileprivate let _scheduler: ImmediateSchedulerType
+
+ init(element: Element, scheduler: ImmediateSchedulerType) {
+ self._element = element
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = RepeatElementSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class RepeatElementSink<Observer: ObserverType>: Sink<Observer> {
+ typealias Parent = RepeatElement<Observer.Element>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.scheduleRecursive(self._parent._element) { e, recurse in
+ self.forwardOn(.next(e))
+ recurse(e)
+ }
+ }
+}
--- /dev/null
+//
+// ReplaySubject.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an object that is both an observable sequence as well as an observer.
+///
+/// Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
+public class ReplaySubject<Element>
+ : Observable<Element>
+ , SubjectType
+ , ObserverType
+ , Disposable {
+ public typealias SubjectObserverType = ReplaySubject<Element>
+
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ /// Indicates whether the subject has any observers
+ public var hasObservers: Bool {
+ self._lock.lock()
+ let value = self._observers.count > 0
+ self._lock.unlock()
+ return value
+ }
+
+ fileprivate let _lock = RecursiveLock()
+
+ // state
+ fileprivate var _isDisposed = false
+ fileprivate var _isStopped = false
+ fileprivate var _stoppedEvent = nil as Event<Element>? {
+ didSet {
+ self._isStopped = self._stoppedEvent != nil
+ }
+ }
+ fileprivate var _observers = Observers()
+
+ #if DEBUG
+ fileprivate let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ func unsubscribe(_ key: DisposeKey) {
+ rxAbstractMethod()
+ }
+
+ final var isStopped: Bool {
+ return self._isStopped
+ }
+
+ /// Notifies all subscribed observers about next event.
+ ///
+ /// - parameter event: Event to send to the observers.
+ public func on(_ event: Event<Element>) {
+ rxAbstractMethod()
+ }
+
+ /// Returns observer interface for subject.
+ public func asObserver() -> SubjectObserverType {
+ return self
+ }
+
+ /// Unsubscribe all observers and release resources.
+ public func dispose() {
+ }
+
+ /// Creates new instance of `ReplaySubject` that replays at most `bufferSize` last elements of sequence.
+ ///
+ /// - parameter bufferSize: Maximal number of elements to replay to observer after subscription.
+ /// - returns: New instance of replay subject.
+ public static func create(bufferSize: Int) -> ReplaySubject<Element> {
+ if bufferSize == 1 {
+ return ReplayOne()
+ }
+ else {
+ return ReplayMany(bufferSize: bufferSize)
+ }
+ }
+
+ /// Creates a new instance of `ReplaySubject` that buffers all the elements of a sequence.
+ /// To avoid filling up memory, developer needs to make sure that the use case will only ever store a 'reasonable'
+ /// number of elements.
+ public static func createUnbounded() -> ReplaySubject<Element> {
+ return ReplayAll()
+ }
+
+ #if TRACE_RESOURCES
+ override init() {
+ _ = Resources.incrementTotal()
+ }
+
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
+
+private class ReplayBufferBase<Element>
+ : ReplaySubject<Element>
+ , SynchronizedUnsubscribeType {
+
+ func trim() {
+ rxAbstractMethod()
+ }
+
+ func addValueToBuffer(_ value: Element) {
+ rxAbstractMethod()
+ }
+
+ func replayBuffer<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
+ rxAbstractMethod()
+ }
+
+ override func on(_ event: Event<Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ dispatch(self._synchronized_on(event), event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) -> Observers {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._isDisposed {
+ return Observers()
+ }
+
+ if self._isStopped {
+ return Observers()
+ }
+
+ switch event {
+ case .next(let element):
+ self.addValueToBuffer(element)
+ self.trim()
+ return self._observers
+ case .error, .completed:
+ self._stoppedEvent = event
+ self.trim()
+ let observers = self._observers
+ self._observers.removeAll()
+ return observers
+ }
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock()
+ let subscription = self._synchronized_subscribe(observer)
+ self._lock.unlock()
+ return subscription
+ }
+
+ func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ if self._isDisposed {
+ observer.on(.error(RxError.disposed(object: self)))
+ return Disposables.create()
+ }
+
+ let anyObserver = observer.asObserver()
+
+ self.replayBuffer(anyObserver)
+ if let stoppedEvent = self._stoppedEvent {
+ observer.on(stoppedEvent)
+ return Disposables.create()
+ }
+ else {
+ let key = self._observers.insert(observer.on)
+ return SubscriptionDisposable(owner: self, key: key)
+ }
+ }
+
+ func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock()
+ self._synchronized_unsubscribe(disposeKey)
+ self._lock.unlock()
+ }
+
+ func _synchronized_unsubscribe(_ disposeKey: DisposeKey) {
+ if self._isDisposed {
+ return
+ }
+
+ _ = self._observers.removeKey(disposeKey)
+ }
+
+ override func dispose() {
+ super.dispose()
+
+ self.synchronizedDispose()
+ }
+
+ func synchronizedDispose() {
+ self._lock.lock()
+ self._synchronized_dispose()
+ self._lock.unlock()
+ }
+
+ func _synchronized_dispose() {
+ self._isDisposed = true
+ self._observers.removeAll()
+ }
+}
+
+private final class ReplayOne<Element> : ReplayBufferBase<Element> {
+ private var _value: Element?
+
+ override init() {
+ super.init()
+ }
+
+ override func trim() {
+
+ }
+
+ override func addValueToBuffer(_ value: Element) {
+ self._value = value
+ }
+
+ override func replayBuffer<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
+ if let value = self._value {
+ observer.on(.next(value))
+ }
+ }
+
+ override func _synchronized_dispose() {
+ super._synchronized_dispose()
+ self._value = nil
+ }
+}
+
+private class ReplayManyBase<Element>: ReplayBufferBase<Element> {
+ fileprivate var _queue: Queue<Element>
+
+ init(queueSize: Int) {
+ self._queue = Queue(capacity: queueSize + 1)
+ }
+
+ override func addValueToBuffer(_ value: Element) {
+ self._queue.enqueue(value)
+ }
+
+ override func replayBuffer<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
+ for item in self._queue {
+ observer.on(.next(item))
+ }
+ }
+
+ override func _synchronized_dispose() {
+ super._synchronized_dispose()
+ self._queue = Queue(capacity: 0)
+ }
+}
+
+private final class ReplayMany<Element> : ReplayManyBase<Element> {
+ private let _bufferSize: Int
+
+ init(bufferSize: Int) {
+ self._bufferSize = bufferSize
+
+ super.init(queueSize: bufferSize)
+ }
+
+ override func trim() {
+ while self._queue.count > self._bufferSize {
+ _ = self._queue.dequeue()
+ }
+ }
+}
+
+private final class ReplayAll<Element> : ReplayManyBase<Element> {
+ init() {
+ super.init(queueSize: 0)
+ }
+
+ override func trim() {
+
+ }
+}
--- /dev/null
+//
+// RetryWhen.swift
+// RxSwift
+//
+// Created by Junior B. on 06/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Repeats the source observable sequence on error when the notifier emits a next value.
+ If the source observable errors and the notifier completes, it will complete the source sequence.
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete.
+ */
+ public func retryWhen<TriggerObservable: ObservableType, Error: Swift.Error>(_ notificationHandler: @escaping (Observable<Error>) -> TriggerObservable)
+ -> Observable<Element> {
+ return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler)
+ }
+
+ /**
+ Repeats the source observable sequence on error when the notifier emits a next value.
+ If the source observable errors and the notifier completes, it will complete the source sequence.
+
+ - seealso: [retry operator on reactivex.io](http://reactivex.io/documentation/operators/retry.html)
+
+ - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable.
+ - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete.
+ */
+ public func retryWhen<TriggerObservable: ObservableType>(_ notificationHandler: @escaping (Observable<Swift.Error>) -> TriggerObservable)
+ -> Observable<Element> {
+ return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler)
+ }
+}
+
+final private class RetryTriggerSink<Sequence: Swift.Sequence, Observer: ObserverType, TriggerObservable: ObservableType, Error>
+ : ObserverType where Sequence.Element: ObservableType, Sequence.Element.Element == Observer.Element {
+ typealias Element = TriggerObservable.Element
+
+ typealias Parent = RetryWhenSequenceSinkIter<Sequence, Observer, TriggerObservable, Error>
+
+ private let _parent: Parent
+
+ init(parent: Parent) {
+ self._parent = parent
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent._parent._lastError = nil
+ self._parent._parent.schedule(.moveNext)
+ case .error(let e):
+ self._parent._parent.forwardOn(.error(e))
+ self._parent._parent.dispose()
+ case .completed:
+ self._parent._parent.forwardOn(.completed)
+ self._parent._parent.dispose()
+ }
+ }
+}
+
+final private class RetryWhenSequenceSinkIter<Sequence: Swift.Sequence, Observer: ObserverType, TriggerObservable: ObservableType, Error>
+ : ObserverType
+ , Disposable where Sequence.Element: ObservableType, Sequence.Element.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias Parent = RetryWhenSequenceSink<Sequence, Observer, TriggerObservable, Error>
+
+ fileprivate let _parent: Parent
+ private let _errorHandlerSubscription = SingleAssignmentDisposable()
+ private let _subscription: Disposable
+
+ init(parent: Parent, subscription: Disposable) {
+ self._parent = parent
+ self._subscription = subscription
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent.forwardOn(event)
+ case .error(let error):
+ self._parent._lastError = error
+
+ if let failedWith = error as? Error {
+ // dispose current subscription
+ self._subscription.dispose()
+
+ let errorHandlerSubscription = self._parent._notifier.subscribe(RetryTriggerSink(parent: self))
+ self._errorHandlerSubscription.setDisposable(errorHandlerSubscription)
+ self._parent._errorSubject.on(.next(failedWith))
+ }
+ else {
+ self._parent.forwardOn(.error(error))
+ self._parent.dispose()
+ }
+ case .completed:
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ }
+ }
+
+ final func dispose() {
+ self._subscription.dispose()
+ self._errorHandlerSubscription.dispose()
+ }
+}
+
+final private class RetryWhenSequenceSink<Sequence: Swift.Sequence, Observer: ObserverType, TriggerObservable: ObservableType, Error>
+ : TailRecursiveSink<Sequence, Observer> where Sequence.Element: ObservableType, Sequence.Element.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias Parent = RetryWhenSequence<Sequence, TriggerObservable, Error>
+
+ let _lock = RecursiveLock()
+
+ private let _parent: Parent
+
+ fileprivate var _lastError: Swift.Error?
+ fileprivate let _errorSubject = PublishSubject<Error>()
+ private let _handler: Observable<TriggerObservable.Element>
+ fileprivate let _notifier = PublishSubject<TriggerObservable.Element>()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._handler = parent._notificationHandler(self._errorSubject).asObservable()
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ override func done() {
+ if let lastError = self._lastError {
+ self.forwardOn(.error(lastError))
+ self._lastError = nil
+ }
+ else {
+ self.forwardOn(.completed)
+ }
+
+ self.dispose()
+ }
+
+ override func extract(_ observable: Observable<Element>) -> SequenceGenerator? {
+ // It is important to always return `nil` here because there are sideffects in the `run` method
+ // that are dependant on particular `retryWhen` operator so single operator stack can't be reused in this
+ // case.
+ return nil
+ }
+
+ override func subscribeToNext(_ source: Observable<Element>) -> Disposable {
+ let subscription = SingleAssignmentDisposable()
+ let iter = RetryWhenSequenceSinkIter(parent: self, subscription: subscription)
+ subscription.setDisposable(source.subscribe(iter))
+ return iter
+ }
+
+ override func run(_ sources: SequenceGenerator) -> Disposable {
+ let triggerSubscription = self._handler.subscribe(self._notifier.asObserver())
+ let superSubscription = super.run(sources)
+ return Disposables.create(superSubscription, triggerSubscription)
+ }
+}
+
+final private class RetryWhenSequence<Sequence: Swift.Sequence, TriggerObservable: ObservableType, Error>: Producer<Sequence.Element.Element> where Sequence.Element: ObservableType {
+ typealias Element = Sequence.Element.Element
+
+ private let _sources: Sequence
+ fileprivate let _notificationHandler: (Observable<Error>) -> TriggerObservable
+
+ init(sources: Sequence, notificationHandler: @escaping (Observable<Error>) -> TriggerObservable) {
+ self._sources = sources
+ self._notificationHandler = notificationHandler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = RetryWhenSequenceSink<Sequence, Observer, TriggerObservable, Error>(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run((self._sources.makeIterator(), nil))
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Rx.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+#if TRACE_RESOURCES
+ private let resourceCount = AtomicInt(0)
+
+ /// Resource utilization information
+ public struct Resources {
+ /// Counts internal Rx resource allocations (Observables, Observers, Disposables, etc.). This provides a simple way to detect leaks during development.
+ public static var total: Int32 {
+ return load(resourceCount)
+ }
+
+ /// Increments `Resources.total` resource count.
+ ///
+ /// - returns: New resource count
+ public static func incrementTotal() -> Int32 {
+ return increment(resourceCount)
+ }
+
+ /// Decrements `Resources.total` resource count
+ ///
+ /// - returns: New resource count
+ public static func decrementTotal() -> Int32 {
+ return decrement(resourceCount)
+ }
+ }
+#endif
+
+/// Swift does not implement abstract methods. This method is used as a runtime check to ensure that methods which intended to be abstract (i.e., they should be implemented in subclasses) are not called directly on the superclass.
+func rxAbstractMethod(file: StaticString = #file, line: UInt = #line) -> Swift.Never {
+ rxFatalError("Abstract method", file: file, line: line)
+}
+
+func rxFatalError(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> Swift.Never {
+ fatalError(lastMessage(), file: file, line: line)
+}
+
+func rxFatalErrorInDebug(_ lastMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) {
+ #if DEBUG
+ fatalError(lastMessage(), file: file, line: line)
+ #else
+ print("\(file):\(line): \(lastMessage())")
+ #endif
+}
+
+func incrementChecked(_ i: inout Int) throws -> Int {
+ if i == Int.max {
+ throw RxError.overflow
+ }
+ defer { i += 1 }
+ return i
+}
+
+func decrementChecked(_ i: inout Int) throws -> Int {
+ if i == Int.min {
+ throw RxError.overflow
+ }
+ defer { i -= 1 }
+ return i
+}
+
+#if DEBUG
+ import class Foundation.Thread
+ final class SynchronizationTracker {
+ private let _lock = RecursiveLock()
+
+ public enum SynchronizationErrorMessages: String {
+ case variable = "Two different threads are trying to assign the same `Variable.value` unsynchronized.\n This is undefined behavior because the end result (variable value) is nondeterministic and depends on the \n operating system thread scheduler. This will cause random behavior of your program.\n"
+ case `default` = "Two different unsynchronized threads are trying to send some event simultaneously.\n This is undefined behavior because the ordering of the effects caused by these events is nondeterministic and depends on the \n operating system thread scheduler. This will result in a random behavior of your program.\n"
+ }
+
+ private var _threads = [UnsafeMutableRawPointer: Int]()
+
+ private func synchronizationError(_ message: String) {
+ #if FATAL_SYNCHRONIZATION
+ rxFatalError(message)
+ #else
+ print(message)
+ #endif
+ }
+
+ func register(synchronizationErrorMessage: SynchronizationErrorMessages) {
+ self._lock.lock(); defer { self._lock.unlock() }
+ let pointer = Unmanaged.passUnretained(Thread.current).toOpaque()
+ let count = (self._threads[pointer] ?? 0) + 1
+
+ if count > 1 {
+ self.synchronizationError(
+ "⚠️ Reentrancy anomaly was detected.\n" +
+ " > Debugging: To debug this issue you can set a breakpoint in \(#file):\(#line) and observe the call stack.\n" +
+ " > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?`\n" +
+ " This behavior breaks the grammar because there is overlapping between sequence events.\n" +
+ " Observable sequence is trying to send an event before sending of previous event has finished.\n" +
+ " > Interpretation: This could mean that there is some kind of unexpected cyclic dependency in your code,\n" +
+ " or that the system is not behaving in the expected way.\n" +
+ " > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)`\n" +
+ " or by enqueuing sequence events in some other way.\n"
+ )
+ }
+
+ self._threads[pointer] = count
+
+ if self._threads.count > 1 {
+ self.synchronizationError(
+ "⚠️ Synchronization anomaly was detected.\n" +
+ " > Debugging: To debug this issue you can set a breakpoint in \(#file):\(#line) and observe the call stack.\n" +
+ " > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?`\n" +
+ " This behavior breaks the grammar because there is overlapping between sequence events.\n" +
+ " Observable sequence is trying to send an event before sending of previous event has finished.\n" +
+ " > Interpretation: " + synchronizationErrorMessage.rawValue +
+ " > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)`\n" +
+ " or by synchronizing sequence events in some other way.\n"
+ )
+ }
+ }
+
+ func unregister() {
+ self._lock.lock(); defer { self._lock.unlock() }
+ let pointer = Unmanaged.passUnretained(Thread.current).toOpaque()
+ self._threads[pointer] = (self._threads[pointer] ?? 1) - 1
+ if self._threads[pointer] == 0 {
+ self._threads[pointer] = nil
+ }
+ }
+ }
+
+#endif
+
+/// RxSwift global hooks
+public enum Hooks {
+
+ // Should capture call stack
+ public static var recordCallStackOnError: Bool = false
+
+}
--- /dev/null
+//
+// RxMutableBox.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/22/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+#if os(Linux)
+/// As Swift 5 was released, A patch to `Thread` for Linux
+/// changed `threadDictionary` to a `NSMutableDictionary` instead of
+/// a `Dictionary<String, Any>`: https://github.com/apple/swift-corelibs-foundation/pull/1762/files
+///
+/// This means that on Linux specifically, `RxMutableBox` must be a `NSObject`
+/// or it won't be possible to store it in `Thread.threadDictionary`.
+///
+/// For more information, read the discussion at:
+/// https://github.com/ReactiveX/RxSwift/issues/1911#issuecomment-479723298
+import class Foundation.NSObject
+
+/// Creates mutable reference wrapper for any type.
+final class RxMutableBox<T>: NSObject {
+ /// Wrapped value
+ var value: T
+
+ /// Creates reference wrapper for `value`.
+ ///
+ /// - parameter value: Value to wrap.
+ init (_ value: T) {
+ self.value = value
+ }
+}
+#else
+/// Creates mutable reference wrapper for any type.
+final class RxMutableBox<T>: CustomDebugStringConvertible {
+ /// Wrapped value
+ var value: T
+
+ /// Creates reference wrapper for `value`.
+ ///
+ /// - parameter value: Value to wrap.
+ init (_ value: T) {
+ self.value = value
+ }
+}
+
+extension RxMutableBox {
+ /// - returns: Box description.
+ var debugDescription: String {
+ return "MutatingBox(\(self.value))"
+ }
+}
+#endif
--- /dev/null
+//
+// Sample.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/1/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Samples the source observable sequence using a sampler observable sequence producing sampling ticks.
+
+ Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence.
+
+ **In case there were no new elements between sampler ticks, no element is sent to the resulting sequence.**
+
+ - seealso: [sample operator on reactivex.io](http://reactivex.io/documentation/operators/sample.html)
+
+ - parameter sampler: Sampling tick sequence.
+ - returns: Sampled observable sequence.
+ */
+ public func sample<Source: ObservableType>(_ sampler: Source)
+ -> Observable<Element> {
+ return Sample(source: self.asObservable(), sampler: sampler.asObservable())
+ }
+}
+
+final private class SamplerSink<Observer: ObserverType, SampleType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Element = SampleType
+
+ typealias Parent = SampleSequenceSink<Observer, SampleType>
+
+ private let _parent: Parent
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ init(parent: Parent) {
+ self._parent = parent
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next, .completed:
+ if let element = _parent._element {
+ self._parent._element = nil
+ self._parent.forwardOn(.next(element))
+ }
+
+ if self._parent._atEnd {
+ self._parent.forwardOn(.completed)
+ self._parent.dispose()
+ }
+ case .error(let e):
+ self._parent.forwardOn(.error(e))
+ self._parent.dispose()
+ }
+ }
+}
+
+final private class SampleSequenceSink<Observer: ObserverType, SampleType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Element = Observer.Element
+ typealias Parent = Sample<Element, SampleType>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ // state
+ fileprivate var _element = nil as Element?
+ fileprivate var _atEnd = false
+
+ private let _sourceSubscription = SingleAssignmentDisposable()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ self._sourceSubscription.setDisposable(self._parent._source.subscribe(self))
+ let samplerSubscription = self._parent._sampler.subscribe(SamplerSink(parent: self))
+
+ return Disposables.create(_sourceSubscription, samplerSubscription)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ self._element = element
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self._atEnd = true
+ self._sourceSubscription.dispose()
+ }
+ }
+
+}
+
+final private class Sample<Element, SampleType>: Producer<Element> {
+ fileprivate let _source: Observable<Element>
+ fileprivate let _sampler: Observable<SampleType>
+
+ init(source: Observable<Element>, sampler: Observable<SampleType>) {
+ self._source = source
+ self._sampler = sampler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SampleSequenceSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Scan.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value.
+
+ For aggregation behavior with no intermediate results, see `reduce`.
+
+ - seealso: [scan operator on reactivex.io](http://reactivex.io/documentation/operators/scan.html)
+
+ - parameter seed: The initial accumulator value.
+ - parameter accumulator: An accumulator function to be invoked on each element.
+ - returns: An observable sequence containing the accumulated values.
+ */
+ public func scan<A>(into seed: A, accumulator: @escaping (inout A, Element) throws -> Void)
+ -> Observable<A> {
+ return Scan(source: self.asObservable(), seed: seed, accumulator: accumulator)
+ }
+
+ /**
+ Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value.
+
+ For aggregation behavior with no intermediate results, see `reduce`.
+
+ - seealso: [scan operator on reactivex.io](http://reactivex.io/documentation/operators/scan.html)
+
+ - parameter seed: The initial accumulator value.
+ - parameter accumulator: An accumulator function to be invoked on each element.
+ - returns: An observable sequence containing the accumulated values.
+ */
+ public func scan<A>(_ seed: A, accumulator: @escaping (A, Element) throws -> A)
+ -> Observable<A> {
+ return Scan(source: self.asObservable(), seed: seed) { acc, element in
+ let currentAcc = acc
+ acc = try accumulator(currentAcc, element)
+ }
+ }
+}
+
+final private class ScanSink<Element, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Accumulate = Observer.Element
+ typealias Parent = Scan<Element, Accumulate>
+
+ private let _parent: Parent
+ private var _accumulate: Accumulate
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._accumulate = parent._seed
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ do {
+ try self._parent._accumulator(&self._accumulate, element)
+ self.forwardOn(.next(self._accumulate))
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+
+}
+
+final private class Scan<Element, Accumulate>: Producer<Accumulate> {
+ typealias Accumulator = (inout Accumulate, Element) throws -> Void
+
+ private let _source: Observable<Element>
+ fileprivate let _seed: Accumulate
+ fileprivate let _accumulator: Accumulator
+
+ init(source: Observable<Element>, seed: Accumulate, accumulator: @escaping Accumulator) {
+ self._source = source
+ self._seed = seed
+ self._accumulator = accumulator
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Accumulate {
+ let sink = ScanSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// ScheduledDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/13/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+private let disposeScheduledDisposable: (ScheduledDisposable) -> Disposable = { sd in
+ sd.disposeInner()
+ return Disposables.create()
+}
+
+/// Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler.
+public final class ScheduledDisposable : Cancelable {
+ public let scheduler: ImmediateSchedulerType
+
+ private let _isDisposed = AtomicInt(0)
+
+ // state
+ private var _disposable: Disposable?
+
+ /// - returns: Was resource disposed.
+ public var isDisposed: Bool {
+ return isFlagSet(self._isDisposed, 1)
+ }
+
+ /**
+ Initializes a new instance of the `ScheduledDisposable` that uses a `scheduler` on which to dispose the `disposable`.
+
+ - parameter scheduler: Scheduler where the disposable resource will be disposed on.
+ - parameter disposable: Disposable resource to dispose on the given scheduler.
+ */
+ public init(scheduler: ImmediateSchedulerType, disposable: Disposable) {
+ self.scheduler = scheduler
+ self._disposable = disposable
+ }
+
+ /// Disposes the wrapped disposable on the provided scheduler.
+ public func dispose() {
+ _ = self.scheduler.schedule(self, action: disposeScheduledDisposable)
+ }
+
+ func disposeInner() {
+ if fetchOr(self._isDisposed, 1) == 0 {
+ self._disposable!.dispose()
+ self._disposable = nil
+ }
+ }
+}
--- /dev/null
+//
+// ScheduledItem.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 9/2/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+struct ScheduledItem<T>
+ : ScheduledItemType
+ , InvocableType {
+ typealias Action = (T) -> Disposable
+
+ private let _action: Action
+ private let _state: T
+
+ private let _disposable = SingleAssignmentDisposable()
+
+ var isDisposed: Bool {
+ return self._disposable.isDisposed
+ }
+
+ init(action: @escaping Action, state: T) {
+ self._action = action
+ self._state = state
+ }
+
+ func invoke() {
+ self._disposable.setDisposable(self._action(self._state))
+ }
+
+ func dispose() {
+ self._disposable.dispose()
+ }
+}
--- /dev/null
+//
+// ScheduledItemType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 11/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol ScheduledItemType
+ : Cancelable
+ , InvocableType {
+ func invoke()
+}
--- /dev/null
+//
+// SchedulerServices+Emulation.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/6/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+enum SchedulePeriodicRecursiveCommand {
+ case tick
+ case dispatchStart
+}
+
+final class SchedulePeriodicRecursive<State> {
+ typealias RecursiveAction = (State) -> State
+ typealias RecursiveScheduler = AnyRecursiveScheduler<SchedulePeriodicRecursiveCommand>
+
+ private let _scheduler: SchedulerType
+ private let _startAfter: RxTimeInterval
+ private let _period: RxTimeInterval
+ private let _action: RecursiveAction
+
+ private var _state: State
+ private let _pendingTickCount = AtomicInt(0)
+
+ init(scheduler: SchedulerType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping RecursiveAction, state: State) {
+ self._scheduler = scheduler
+ self._startAfter = startAfter
+ self._period = period
+ self._action = action
+ self._state = state
+ }
+
+ func start() -> Disposable {
+ return self._scheduler.scheduleRecursive(SchedulePeriodicRecursiveCommand.tick, dueTime: self._startAfter, action: self.tick)
+ }
+
+ func tick(_ command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) {
+ // Tries to emulate periodic scheduling as best as possible.
+ // The problem that could arise is if handling periodic ticks take too long, or
+ // tick interval is short.
+ switch command {
+ case .tick:
+ scheduler.schedule(.tick, dueTime: self._period)
+
+ // The idea is that if on tick there wasn't any item enqueued, schedule to perform work immediately.
+ // Else work will be scheduled after previous enqueued work completes.
+ if increment(self._pendingTickCount) == 0 {
+ self.tick(.dispatchStart, scheduler: scheduler)
+ }
+
+ case .dispatchStart:
+ self._state = self._action(self._state)
+ // Start work and schedule check is this last batch of work
+ if decrement(self._pendingTickCount) > 1 {
+ // This gives priority to scheduler emulation, it's not perfect, but helps
+ scheduler.schedule(SchedulePeriodicRecursiveCommand.dispatchStart)
+ }
+ }
+ }
+}
--- /dev/null
+//
+// SchedulerType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import enum Dispatch.DispatchTimeInterval
+import struct Foundation.Date
+
+// Type that represents time interval in the context of RxSwift.
+public typealias RxTimeInterval = DispatchTimeInterval
+
+/// Type that represents absolute time in the context of RxSwift.
+public typealias RxTime = Date
+
+/// Represents an object that schedules units of work.
+public protocol SchedulerType: ImmediateSchedulerType {
+
+ /// - returns: Current time.
+ var now : RxTime {
+ get
+ }
+
+ /**
+ Schedules an action to be executed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable
+
+ /**
+ Schedules a periodic piece of work.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter startAfter: Period after which initial work should be run.
+ - parameter period: Period for running the work periodically.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable
+}
+
+extension SchedulerType {
+
+ /**
+ Periodic task will be emulated using recursive scheduling.
+
+ - parameter state: Initial state passed to the action upon the first iteration.
+ - parameter startAfter: Period after which initial work should be run.
+ - parameter period: Period for running the work periodically.
+ - returns: The disposable object used to cancel the scheduled recurring action (best effort).
+ */
+ public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
+ let schedule = SchedulePeriodicRecursive(scheduler: self, startAfter: startAfter, period: period, action: action, state: state)
+
+ return schedule.start()
+ }
+
+ func scheduleRecursive<State>(_ state: State, dueTime: RxTimeInterval, action: @escaping (State, AnyRecursiveScheduler<State>) -> Void) -> Disposable {
+ let scheduler = AnyRecursiveScheduler(scheduler: self, action: action)
+
+ scheduler.schedule(state, dueTime: dueTime)
+
+ return Disposables.create(with: scheduler.dispose)
+ }
+}
--- /dev/null
+//
+// Sequence.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 11/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ // MARK: of
+
+ /**
+ This method creates a new Observable instance with a variable number of elements.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - parameter elements: Elements to generate.
+ - parameter scheduler: Scheduler to send elements on. If `nil`, elements are sent immediately on subscription.
+ - returns: The observable sequence whose elements are pulled from the given arguments.
+ */
+ public static func of(_ elements: Element ..., scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<Element> {
+ return ObservableSequence(elements: elements, scheduler: scheduler)
+ }
+}
+
+extension ObservableType {
+ /**
+ Converts an array to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - returns: The observable sequence whose elements are pulled from the given enumerable sequence.
+ */
+ public static func from(_ array: [Element], scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<Element> {
+ return ObservableSequence(elements: array, scheduler: scheduler)
+ }
+
+ /**
+ Converts a sequence to an observable sequence.
+
+ - seealso: [from operator on reactivex.io](http://reactivex.io/documentation/operators/from.html)
+
+ - returns: The observable sequence whose elements are pulled from the given enumerable sequence.
+ */
+ public static func from<Sequence: Swift.Sequence>(_ sequence: Sequence, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<Element> where Sequence.Element == Element {
+ return ObservableSequence(elements: sequence, scheduler: scheduler)
+ }
+}
+
+final private class ObservableSequenceSink<Sequence: Swift.Sequence, Observer: ObserverType>: Sink<Observer> where Sequence.Element == Observer.Element {
+ typealias Parent = ObservableSequence<Sequence>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.scheduleRecursive(self._parent._elements.makeIterator()) { iterator, recurse in
+ var mutableIterator = iterator
+ if let next = mutableIterator.next() {
+ self.forwardOn(.next(next))
+ recurse(mutableIterator)
+ }
+ else {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ }
+}
+
+final private class ObservableSequence<Sequence: Swift.Sequence>: Producer<Sequence.Element> {
+ fileprivate let _elements: Sequence
+ fileprivate let _scheduler: ImmediateSchedulerType
+
+ init(elements: Sequence, scheduler: ImmediateSchedulerType) {
+ self._elements = elements
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ObservableSequenceSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// SerialDispatchQueueScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/8/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.TimeInterval
+import struct Foundation.Date
+import Dispatch
+
+/**
+Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. It will make sure
+that even if concurrent dispatch queue is passed, it's transformed into a serial one.
+
+It is extremely important that this scheduler is serial, because
+certain operator perform optimizations that rely on that property.
+
+Because there is no way of detecting is passed dispatch queue serial or
+concurrent, for every queue that is being passed, worst case (concurrent)
+will be assumed, and internal serial proxy dispatch queue will be created.
+
+This scheduler can also be used with internal serial queue alone.
+
+In case some customization need to be made on it before usage,
+internal serial queue can be customized using `serialQueueConfiguration`
+callback.
+*/
+public class SerialDispatchQueueScheduler : SchedulerType {
+ public typealias TimeInterval = Foundation.TimeInterval
+ public typealias Time = Date
+
+ /// - returns: Current time.
+ public var now : Date {
+ return Date()
+ }
+
+ let configuration: DispatchQueueConfiguration
+
+ /**
+ Constructs new `SerialDispatchQueueScheduler` that wraps `serialQueue`.
+
+ - parameter serialQueue: Target dispatch queue.
+ - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ */
+ init(serialQueue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ self.configuration = DispatchQueueConfiguration(queue: serialQueue, leeway: leeway)
+ }
+
+ /**
+ Constructs new `SerialDispatchQueueScheduler` with internal serial queue named `internalSerialQueueName`.
+
+ Additional dispatch queue properties can be set after dispatch queue is created using `serialQueueConfiguration`.
+
+ - parameter internalSerialQueueName: Name of internal serial dispatch queue.
+ - parameter serialQueueConfiguration: Additional configuration of internal serial dispatch queue.
+ - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ */
+ public convenience init(internalSerialQueueName: String, serialQueueConfiguration: ((DispatchQueue) -> Void)? = nil, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ let queue = DispatchQueue(label: internalSerialQueueName, attributes: [])
+ serialQueueConfiguration?(queue)
+ self.init(serialQueue: queue, leeway: leeway)
+ }
+
+ /**
+ Constructs new `SerialDispatchQueueScheduler` named `internalSerialQueueName` that wraps `queue`.
+
+ - parameter queue: Possibly concurrent dispatch queue used to perform work.
+ - parameter internalSerialQueueName: Name of internal serial dispatch queue proxy.
+ - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ */
+ public convenience init(queue: DispatchQueue, internalSerialQueueName: String, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ // Swift 3.0 IUO
+ let serialQueue = DispatchQueue(label: internalSerialQueueName,
+ attributes: [],
+ target: queue)
+ self.init(serialQueue: serialQueue, leeway: leeway)
+ }
+
+ /**
+ Constructs new `SerialDispatchQueueScheduler` that wraps one of the global concurrent dispatch queues.
+
+ - parameter qos: Identifier for global dispatch queue with specified quality of service class.
+ - parameter internalSerialQueueName: Custom name for internal serial dispatch queue proxy.
+ - parameter leeway: The amount of time, in nanoseconds, that the system will defer the timer.
+ */
+ @available(iOS 8, OSX 10.10, *)
+ public convenience init(qos: DispatchQoS, internalSerialQueueName: String = "rx.global_dispatch_queue.serial", leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
+ self.init(queue: DispatchQueue.global(qos: qos.qosClass), internalSerialQueueName: internalSerialQueueName, leeway: leeway)
+ }
+
+ /**
+ Schedules an action to be executed immediately.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public final func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.scheduleInternal(state, action: action)
+ }
+
+ func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.configuration.schedule(state, action: action)
+ }
+
+ /**
+ Schedules an action to be executed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public final func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action)
+ }
+
+ /**
+ Schedules a periodic piece of work.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter startAfter: Period after which initial work should be run.
+ - parameter period: Period for running the work periodically.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
+ return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
+ }
+}
--- /dev/null
+//
+// SerialDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
+public final class SerialDisposable : DisposeBase, Cancelable {
+ private var _lock = SpinLock()
+
+ // state
+ private var _current = nil as Disposable?
+ private var _isDisposed = false
+
+ /// - returns: Was resource disposed.
+ public var isDisposed: Bool {
+ return self._isDisposed
+ }
+
+ /// Initializes a new instance of the `SerialDisposable`.
+ override public init() {
+ super.init()
+ }
+
+ /**
+ Gets or sets the underlying disposable.
+
+ Assigning this property disposes the previous disposable object.
+
+ If the `SerialDisposable` has already been disposed, assignment to this property causes immediate disposal of the given disposable object.
+ */
+ public var disposable: Disposable {
+ get {
+ return self._lock.calculateLocked {
+ return self._current ?? Disposables.create()
+ }
+ }
+ set (newDisposable) {
+ let disposable: Disposable? = self._lock.calculateLocked {
+ if self._isDisposed {
+ return newDisposable
+ }
+ else {
+ let toDispose = self._current
+ self._current = newDisposable
+ return toDispose
+ }
+ }
+
+ if let disposable = disposable {
+ disposable.dispose()
+ }
+ }
+ }
+
+ /// Disposes the underlying disposable as well as all future replacements.
+ public func dispose() {
+ self._dispose()?.dispose()
+ }
+
+ private func _dispose() -> Disposable? {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if self._isDisposed {
+ return nil
+ }
+ else {
+ self._isDisposed = true
+ let current = self._current
+ self._current = nil
+ return current
+ }
+ }
+}
--- /dev/null
+//
+// ShareReplayScope.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/28/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+/// Subject lifetime scope
+public enum SubjectLifetimeScope {
+ /**
+ **Each connection will have it's own subject instance to store replay events.**
+ **Connections will be isolated from each another.**
+
+ Configures the underlying implementation to behave equivalent to.
+
+ ```
+ source.multicast(makeSubject: { MySubject() }).refCount()
+ ```
+
+ **This is the recommended default.**
+
+ This has the following consequences:
+ * `retry` or `concat` operators will function as expected because terminating the sequence will clear internal state.
+ * Each connection to source observable sequence will use it's own subject.
+ * When the number of subscribers drops from 1 to 0 and connection to source sequence is disposed, subject will be cleared.
+
+
+ ```
+ let xs = Observable.deferred { () -> Observable<TimeInterval> in
+ print("Performing work ...")
+ return Observable.just(Date().timeIntervalSince1970)
+ }
+ .share(replay: 1, scope: .whileConnected)
+
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+
+ ```
+
+ Notice how time interval is different and `Performing work ...` is printed each time)
+
+ ```
+ Performing work ...
+ next 1495998900.82141
+ completed
+
+ Performing work ...
+ next 1495998900.82359
+ completed
+
+ Performing work ...
+ next 1495998900.82444
+ completed
+
+
+ ```
+
+ */
+ case whileConnected
+
+ /**
+ **One subject will store replay events for all connections to source.**
+ **Connections won't be isolated from each another.**
+
+ Configures the underlying implementation behave equivalent to.
+
+ ```
+ source.multicast(MySubject()).refCount()
+ ```
+
+ This has the following consequences:
+ * Using `retry` or `concat` operators after this operator usually isn't advised.
+ * Each connection to source observable sequence will share the same subject.
+ * After number of subscribers drops from 1 to 0 and connection to source observable sequence is dispose, this operator will
+ continue holding a reference to the same subject.
+ If at some later moment a new observer initiates a new connection to source it can potentially receive
+ some of the stale events received during previous connection.
+ * After source sequence terminates any new observer will always immediately receive replayed elements and terminal event.
+ No new subscriptions to source observable sequence will be attempted.
+
+ ```
+ let xs = Observable.deferred { () -> Observable<TimeInterval> in
+ print("Performing work ...")
+ return Observable.just(Date().timeIntervalSince1970)
+ }
+ .share(replay: 1, scope: .forever)
+
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+ _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
+ ```
+
+ Notice how time interval is the same, replayed, and `Performing work ...` is printed only once
+
+ ```
+ Performing work ...
+ next 1495999013.76356
+ completed
+
+ next 1495999013.76356
+ completed
+
+ next 1495999013.76356
+ completed
+ ```
+
+ */
+ case forever
+}
+
+extension ObservableType {
+
+ /**
+ Returns an observable sequence that **shares a single subscription to the underlying sequence**, and immediately upon subscription replays elements in buffer.
+
+ This operator is equivalent to:
+ * `.whileConnected`
+ ```
+ // Each connection will have it's own subject instance to store replay events.
+ // Connections will be isolated from each another.
+ source.multicast(makeSubject: { Replay.create(bufferSize: replay) }).refCount()
+ ```
+ * `.forever`
+ ```
+ // One subject will store replay events for all connections to source.
+ // Connections won't be isolated from each another.
+ source.multicast(Replay.create(bufferSize: replay)).refCount()
+ ```
+
+ It uses optimized versions of the operators for most common operations.
+
+ - parameter replay: Maximum element count of the replay buffer.
+ - parameter scope: Lifetime scope of sharing subject. For more information see `SubjectLifetimeScope` enum.
+
+ - seealso: [shareReplay operator on reactivex.io](http://reactivex.io/documentation/operators/replay.html)
+
+ - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence.
+ */
+ public func share(replay: Int = 0, scope: SubjectLifetimeScope = .whileConnected)
+ -> Observable<Element> {
+ switch scope {
+ case .forever:
+ switch replay {
+ case 0: return self.multicast(PublishSubject()).refCount()
+ default: return self.multicast(ReplaySubject.create(bufferSize: replay)).refCount()
+ }
+ case .whileConnected:
+ switch replay {
+ case 0: return ShareWhileConnected(source: self.asObservable())
+ case 1: return ShareReplay1WhileConnected(source: self.asObservable())
+ default: return self.multicast(makeSubject: { ReplaySubject.create(bufferSize: replay) }).refCount()
+ }
+ }
+ }
+}
+
+private final class ShareReplay1WhileConnectedConnection<Element>
+ : ObserverType
+ , SynchronizedUnsubscribeType {
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ typealias Parent = ShareReplay1WhileConnected<Element>
+ private let _parent: Parent
+ private let _subscription = SingleAssignmentDisposable()
+
+ private let _lock: RecursiveLock
+ private var _disposed: Bool = false
+ fileprivate var _observers = Observers()
+ private var _element: Element?
+
+ init(parent: Parent, lock: RecursiveLock) {
+ self._parent = parent
+ self._lock = lock
+
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ final func on(_ event: Event<Element>) {
+ self._lock.lock()
+ let observers = self._synchronized_on(event)
+ self._lock.unlock()
+ dispatch(observers, event)
+ }
+
+ final private func _synchronized_on(_ event: Event<Element>) -> Observers {
+ if self._disposed {
+ return Observers()
+ }
+
+ switch event {
+ case .next(let element):
+ self._element = element
+ return self._observers
+ case .error, .completed:
+ let observers = self._observers
+ self._synchronized_dispose()
+ return observers
+ }
+ }
+
+ final func connect() {
+ self._subscription.setDisposable(self._parent._source.subscribe(self))
+ }
+
+ final func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock(); defer { self._lock.unlock() }
+ if let element = self._element {
+ observer.on(.next(element))
+ }
+
+ let disposeKey = self._observers.insert(observer.on)
+
+ return SubscriptionDisposable(owner: self, key: disposeKey)
+ }
+
+ final private func _synchronized_dispose() {
+ self._disposed = true
+ if self._parent._connection === self {
+ self._parent._connection = nil
+ }
+ self._observers = Observers()
+ }
+
+ final func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock()
+ let shouldDisconnect = self._synchronized_unsubscribe(disposeKey)
+ self._lock.unlock()
+ if shouldDisconnect {
+ self._subscription.dispose()
+ }
+ }
+
+ @inline(__always)
+ final private func _synchronized_unsubscribe(_ disposeKey: DisposeKey) -> Bool {
+ // if already unsubscribed, just return
+ if self._observers.removeKey(disposeKey) == nil {
+ return false
+ }
+
+ if self._observers.count == 0 {
+ self._synchronized_dispose()
+ return true
+ }
+
+ return false
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
+
+// optimized version of share replay for most common case
+final private class ShareReplay1WhileConnected<Element>
+ : Observable<Element> {
+
+ fileprivate typealias Connection = ShareReplay1WhileConnectedConnection<Element>
+
+ fileprivate let _source: Observable<Element>
+
+ private let _lock = RecursiveLock()
+
+ fileprivate var _connection: Connection?
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock()
+
+ let connection = self._synchronized_subscribe(observer)
+ let count = connection._observers.count
+
+ let disposable = connection._synchronized_subscribe(observer)
+
+ self._lock.unlock()
+
+ if count == 0 {
+ connection.connect()
+ }
+
+ return disposable
+ }
+
+ @inline(__always)
+ private func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Connection where Observer.Element == Element {
+ let connection: Connection
+
+ if let existingConnection = self._connection {
+ connection = existingConnection
+ }
+ else {
+ connection = ShareReplay1WhileConnectedConnection<Element>(
+ parent: self,
+ lock: self._lock)
+ self._connection = connection
+ }
+
+ return connection
+ }
+}
+
+private final class ShareWhileConnectedConnection<Element>
+ : ObserverType
+ , SynchronizedUnsubscribeType {
+ typealias Observers = AnyObserver<Element>.s
+ typealias DisposeKey = Observers.KeyType
+
+ typealias Parent = ShareWhileConnected<Element>
+ private let _parent: Parent
+ private let _subscription = SingleAssignmentDisposable()
+
+ private let _lock: RecursiveLock
+ private var _disposed: Bool = false
+ fileprivate var _observers = Observers()
+
+ init(parent: Parent, lock: RecursiveLock) {
+ self._parent = parent
+ self._lock = lock
+
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ final func on(_ event: Event<Element>) {
+ self._lock.lock()
+ let observers = self._synchronized_on(event)
+ self._lock.unlock()
+ dispatch(observers, event)
+ }
+
+ final private func _synchronized_on(_ event: Event<Element>) -> Observers {
+ if self._disposed {
+ return Observers()
+ }
+
+ switch event {
+ case .next:
+ return self._observers
+ case .error, .completed:
+ let observers = self._observers
+ self._synchronized_dispose()
+ return observers
+ }
+ }
+
+ final func connect() {
+ self._subscription.setDisposable(self._parent._source.subscribe(self))
+ }
+
+ final func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ let disposeKey = self._observers.insert(observer.on)
+
+ return SubscriptionDisposable(owner: self, key: disposeKey)
+ }
+
+ final private func _synchronized_dispose() {
+ self._disposed = true
+ if self._parent._connection === self {
+ self._parent._connection = nil
+ }
+ self._observers = Observers()
+ }
+
+ final func synchronizedUnsubscribe(_ disposeKey: DisposeKey) {
+ self._lock.lock()
+ let shouldDisconnect = self._synchronized_unsubscribe(disposeKey)
+ self._lock.unlock()
+ if shouldDisconnect {
+ self._subscription.dispose()
+ }
+ }
+
+ @inline(__always)
+ final private func _synchronized_unsubscribe(_ disposeKey: DisposeKey) -> Bool {
+ // if already unsubscribed, just return
+ if self._observers.removeKey(disposeKey) == nil {
+ return false
+ }
+
+ if self._observers.count == 0 {
+ self._synchronized_dispose()
+ return true
+ }
+
+ return false
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
+
+// optimized version of share replay for most common case
+final private class ShareWhileConnected<Element>
+ : Observable<Element> {
+
+ fileprivate typealias Connection = ShareWhileConnectedConnection<Element>
+
+ fileprivate let _source: Observable<Element>
+
+ private let _lock = RecursiveLock()
+
+ fileprivate var _connection: Connection?
+
+ init(source: Observable<Element>) {
+ self._source = source
+ }
+
+ override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
+ self._lock.lock()
+
+ let connection = self._synchronized_subscribe(observer)
+ let count = connection._observers.count
+
+ let disposable = connection._synchronized_subscribe(observer)
+
+ self._lock.unlock()
+
+ if count == 0 {
+ connection.connect()
+ }
+
+ return disposable
+ }
+
+ @inline(__always)
+ private func _synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Connection where Observer.Element == Element {
+ let connection: Connection
+
+ if let existingConnection = self._connection {
+ connection = existingConnection
+ }
+ else {
+ connection = ShareWhileConnectedConnection<Element>(
+ parent: self,
+ lock: self._lock)
+ self._connection = connection
+ }
+
+ return connection
+ }
+}
--- /dev/null
+//
+// Single.swift
+// RxSwift
+//
+// Created by sergdort on 19/08/2017.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+#if DEBUG
+import Foundation
+#endif
+
+/// Sequence containing exactly 1 element
+public enum SingleTrait { }
+/// Represents a push style sequence containing 1 element.
+public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
+
+public enum SingleEvent<Element> {
+ /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
+ case success(Element)
+
+ /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
+ case error(Swift.Error)
+}
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ public typealias SingleObserver = (SingleEvent<Element>) -> Void
+
+ /**
+ Creates an observable sequence from a specified subscribe method implementation.
+
+ - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
+
+ - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
+ - returns: The observable sequence with the specified implementation for the `subscribe` method.
+ */
+ public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<Element> {
+ let source = Observable<Element>.create { observer in
+ return subscribe { event in
+ switch event {
+ case .success(let element):
+ observer.on(.next(element))
+ observer.on(.completed)
+ case .error(let error):
+ observer.on(.error(error))
+ }
+ }
+ }
+
+ return PrimitiveSequence(raw: source)
+ }
+
+
+ /**
+ Subscribes `observer` to receive events for this sequence.
+
+ - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources.
+ */
+ public func subscribe(_ observer: @escaping (SingleEvent<Element>) -> Void) -> Disposable {
+ var stopped = false
+ return self.primitiveSequence.asObservable().subscribe { event in
+ if stopped { return }
+ stopped = true
+
+ switch event {
+ case .next(let element):
+ observer(.success(element))
+ case .error(let error):
+ observer(.error(error))
+ case .completed:
+ rxFatalErrorInDebug("Singles can't emit a completion event")
+ }
+ }
+ }
+
+ /**
+ Subscribes a success handler, and an error handler for this sequence.
+
+ - parameter onSuccess: Action to invoke for each element in the observable sequence.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - returns: Subscription object used to unsubscribe from the observable sequence.
+ */
+ public func subscribe(onSuccess: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil) -> Disposable {
+ #if DEBUG
+ let callStack = Hooks.recordCallStackOnError ? Thread.callStackSymbols : []
+ #else
+ let callStack = [String]()
+ #endif
+
+ return self.primitiveSequence.subscribe { event in
+ switch event {
+ case .success(let element):
+ onSuccess?(element)
+ case .error(let error):
+ if let onError = onError {
+ onError(error)
+ } else {
+ Hooks.defaultErrorHandler(callStack, error)
+ }
+ }
+ }
+ }
+}
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element) -> Single<Element> {
+ return Single(raw: Observable.just(element))
+ }
+
+ /**
+ Returns an observable sequence that contains a single element.
+
+ - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
+
+ - parameter element: Single element in the resulting observable sequence.
+ - parameter scheduler: Scheduler to send the single element on.
+ - returns: An observable sequence containing the single specified element.
+ */
+ public static func just(_ element: Element, scheduler: ImmediateSchedulerType) -> Single<Element> {
+ return Single(raw: Observable.just(element, scheduler: scheduler))
+ }
+
+ /**
+ Returns an observable sequence that terminates with an `error`.
+
+ - seealso: [throw operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: The observable sequence that terminates with specified error.
+ */
+ public static func error(_ error: Swift.Error) -> Single<Element> {
+ return PrimitiveSequence(raw: Observable.error(error))
+ }
+
+ /**
+ Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
+
+ - seealso: [never operator on reactivex.io](http://reactivex.io/documentation/operators/empty-never-throw.html)
+
+ - returns: An observable sequence whose observers will never get called.
+ */
+ public static func never() -> Single<Element> {
+ return PrimitiveSequence(raw: Observable.never())
+ }
+}
+
+extension PrimitiveSequenceType where Trait == SingleTrait {
+
+ /**
+ Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence.
+
+ - seealso: [do operator on reactivex.io](http://reactivex.io/documentation/operators/do.html)
+
+ - parameter onSuccess: Action to invoke for each element in the observable sequence.
+ - parameter afterSuccess: Action to invoke for each element after the observable has passed an onNext event along to its downstream.
+ - parameter onError: Action to invoke upon errored termination of the observable sequence.
+ - parameter afterError: Action to invoke after errored termination of the observable sequence.
+ - parameter onSubscribe: Action to invoke before subscribing to source observable sequence.
+ - parameter onSubscribed: Action to invoke after subscribing to source observable sequence.
+ - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed.
+ - returns: The source sequence with the side-effecting behavior applied.
+ */
+ public func `do`(onSuccess: ((Element) throws -> Void)? = nil,
+ afterSuccess: ((Element) throws -> Void)? = nil,
+ onError: ((Swift.Error) throws -> Void)? = nil,
+ afterError: ((Swift.Error) throws -> Void)? = nil,
+ onSubscribe: (() -> Void)? = nil,
+ onSubscribed: (() -> Void)? = nil,
+ onDispose: (() -> Void)? = nil)
+ -> Single<Element> {
+ return Single(raw: self.primitiveSequence.source.do(
+ onNext: onSuccess,
+ afterNext: afterSuccess,
+ onError: onError,
+ afterError: afterError,
+ onSubscribe: onSubscribe,
+ onSubscribed: onSubscribed,
+ onDispose: onDispose)
+ )
+ }
+
+ /**
+ Filters the elements of an observable sequence based on a predicate.
+
+ - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html)
+
+ - parameter predicate: A function to test each source element for a condition.
+ - returns: An observable sequence that contains elements from the input sequence that satisfy the condition.
+ */
+ public func filter(_ predicate: @escaping (Element) throws -> Bool)
+ -> Maybe<Element> {
+ return Maybe(raw: self.primitiveSequence.source.filter(predicate))
+ }
+
+ /**
+ Projects each element of an observable sequence into a new form.
+
+ - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)
+
+ - parameter transform: A transform function to apply to each source element.
+ - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.
+
+ */
+ public func map<Result>(_ transform: @escaping (Element) throws -> Result)
+ -> Single<Result> {
+ return Single(raw: self.primitiveSequence.source.map(transform))
+ }
+
+ /**
+ Projects each element of an observable sequence into an optional form and filters all optional results.
+
+ - parameter transform: A transform function to apply to each source element.
+ - returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source.
+
+ */
+ public func compactMap<Result>(_ transform: @escaping (Element) throws -> Result?)
+ -> Maybe<Result> {
+ return Maybe(raw: self.primitiveSequence.source.compactMap(transform))
+ }
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ public func flatMap<Result>(_ selector: @escaping (Element) throws -> Single<Result>)
+ -> Single<Result> {
+ return Single<Result>(raw: self.primitiveSequence.source.flatMap(selector))
+ }
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ public func flatMapMaybe<Result>(_ selector: @escaping (Element) throws -> Maybe<Result>)
+ -> Maybe<Result> {
+ return Maybe<Result>(raw: self.primitiveSequence.source.flatMap(selector))
+ }
+
+ /**
+ Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
+
+ - seealso: [flatMap operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
+ */
+ public func flatMapCompletable(_ selector: @escaping (Element) throws -> Completable)
+ -> Completable {
+ return Completable(raw: self.primitiveSequence.source.flatMap(selector))
+ }
+
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<Collection: Swift.Collection, Result>(_ collection: Collection, resultSelector: @escaping ([Element]) throws -> Result) -> PrimitiveSequence<Trait, Result> where Collection.Element == PrimitiveSequence<Trait, Element> {
+
+ if collection.isEmpty {
+ return PrimitiveSequence<Trait, Result>.deferred {
+ return PrimitiveSequence<Trait, Result>(raw: .just(try resultSelector([])))
+ }
+ }
+
+ let raw = Observable.zip(collection.map { $0.asObservable() }, resultSelector: resultSelector)
+ return PrimitiveSequence<Trait, Result>(raw: raw)
+ }
+
+ /**
+ Merges the specified observable sequences into one observable sequence all of the observable sequences have produced an element at a corresponding index.
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<Collection: Swift.Collection>(_ collection: Collection) -> PrimitiveSequence<Trait, [Element]> where Collection.Element == PrimitiveSequence<Trait, Element> {
+
+ if collection.isEmpty {
+ return PrimitiveSequence<Trait, [Element]>(raw: .just([]))
+ }
+
+ let raw = Observable.zip(collection.map { $0.asObservable() })
+ return PrimitiveSequence(raw: raw)
+ }
+
+ /**
+ Continues an observable sequence that is terminated by an error with a single element.
+
+ - seealso: [catch operator on reactivex.io](http://reactivex.io/documentation/operators/catch.html)
+
+ - parameter element: Last element in an observable sequence in case error occurs.
+ - returns: An observable sequence containing the source sequence's elements, followed by the `element` in case an error occurred.
+ */
+ public func catchErrorJustReturn(_ element: Element)
+ -> PrimitiveSequence<Trait, Element> {
+ return PrimitiveSequence(raw: self.primitiveSequence.source.catchErrorJustReturn(element))
+ }
+
+ /// Converts `self` to `Maybe` trait.
+ ///
+ /// - returns: Maybe trait that represents `self`.
+ public func asMaybe() -> Maybe<Element> {
+ return Maybe(raw: self.primitiveSequence.source)
+ }
+
+ /// Converts `self` to `Completable` trait.
+ ///
+ /// - returns: Completable trait that represents `self`.
+ public func asCompletable() -> Completable {
+ return self.primitiveSequence.source.ignoreElements()
+ }
+}
--- /dev/null
+//
+// SingleAssignmentDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/**
+Represents a disposable resource which only allows a single assignment of its underlying disposable resource.
+
+If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception.
+*/
+public final class SingleAssignmentDisposable : DisposeBase, Cancelable {
+
+ private enum DisposeState: Int32 {
+ case disposed = 1
+ case disposableSet = 2
+ }
+
+ // state
+ private let _state = AtomicInt(0)
+ private var _disposable = nil as Disposable?
+
+ /// - returns: A value that indicates whether the object is disposed.
+ public var isDisposed: Bool {
+ return isFlagSet(self._state, DisposeState.disposed.rawValue)
+ }
+
+ /// Initializes a new instance of the `SingleAssignmentDisposable`.
+ public override init() {
+ super.init()
+ }
+
+ /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
+ ///
+ /// **Throws exception if the `SingleAssignmentDisposable` has already been assigned to.**
+ public func setDisposable(_ disposable: Disposable) {
+ self._disposable = disposable
+
+ let previousState = fetchOr(self._state, DisposeState.disposableSet.rawValue)
+
+ if (previousState & DisposeState.disposableSet.rawValue) != 0 {
+ rxFatalError("oldState.disposable != nil")
+ }
+
+ if (previousState & DisposeState.disposed.rawValue) != 0 {
+ disposable.dispose()
+ self._disposable = nil
+ }
+ }
+
+ /// Disposes the underlying disposable.
+ public func dispose() {
+ let previousState = fetchOr(self._state, DisposeState.disposed.rawValue)
+
+ if (previousState & DisposeState.disposed.rawValue) != 0 {
+ return
+ }
+
+ if (previousState & DisposeState.disposableSet.rawValue) != 0 {
+ guard let disposable = self._disposable else {
+ rxFatalError("Disposable not set")
+ }
+ disposable.dispose()
+ self._disposable = nil
+ }
+ }
+
+}
--- /dev/null
+//
+// SingleAsync.swift
+// RxSwift
+//
+// Created by Junior B. on 09/11/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ The single operator is similar to first, but throws a `RxError.noElements` or `RxError.moreThanOneElement`
+ if the source Observable does not emit exactly one element before successfully completing.
+
+ - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html)
+
+ - returns: An observable sequence that emits a single element or throws an exception if more (or none) of them are emitted.
+ */
+ public func single()
+ -> Observable<Element> {
+ return SingleAsync(source: self.asObservable())
+ }
+
+ /**
+ The single operator is similar to first, but throws a `RxError.NoElements` or `RxError.MoreThanOneElement`
+ if the source Observable does not emit exactly one element before successfully completing.
+
+ - seealso: [single operator on reactivex.io](http://reactivex.io/documentation/operators/first.html)
+
+ - parameter predicate: A function to test each source element for a condition.
+ - returns: An observable sequence that emits a single element or throws an exception if more (or none) of them are emitted.
+ */
+ public func single(_ predicate: @escaping (Element) throws -> Bool)
+ -> Observable<Element> {
+ return SingleAsync(source: self.asObservable(), predicate: predicate)
+ }
+}
+
+private final class SingleAsyncSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = SingleAsync<Element>
+
+ private let _parent: Parent
+ private var _seenValue: Bool = false
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ do {
+ let forward = try self._parent._predicate?(value) ?? true
+ if !forward {
+ return
+ }
+ }
+ catch let error {
+ self.forwardOn(.error(error as Swift.Error))
+ self.dispose()
+ return
+ }
+
+ if self._seenValue {
+ self.forwardOn(.error(RxError.moreThanOneElement))
+ self.dispose()
+ return
+ }
+
+ self._seenValue = true
+ self.forwardOn(.next(value))
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if self._seenValue {
+ self.forwardOn(.completed)
+ } else {
+ self.forwardOn(.error(RxError.noElements))
+ }
+ self.dispose()
+ }
+ }
+}
+
+final class SingleAsync<Element>: Producer<Element> {
+ typealias Predicate = (Element) throws -> Bool
+
+ private let _source: Observable<Element>
+ fileprivate let _predicate: Predicate?
+
+ init(source: Observable<Element>, predicate: Predicate? = nil) {
+ self._source = source
+ self._predicate = predicate
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SingleAsyncSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Sink.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/19/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+class Sink<Observer: ObserverType> : Disposable {
+ fileprivate let _observer: Observer
+ fileprivate let _cancel: Cancelable
+ private let _disposed = AtomicInt(0)
+
+ #if DEBUG
+ private let _synchronizationTracker = SynchronizationTracker()
+ #endif
+
+ init(observer: Observer, cancel: Cancelable) {
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ self._observer = observer
+ self._cancel = cancel
+ }
+
+ final func forwardOn(_ event: Event<Observer.Element>) {
+ #if DEBUG
+ self._synchronizationTracker.register(synchronizationErrorMessage: .default)
+ defer { self._synchronizationTracker.unregister() }
+ #endif
+ if isFlagSet(self._disposed, 1) {
+ return
+ }
+ self._observer.on(event)
+ }
+
+ final func forwarder() -> SinkForward<Observer> {
+ return SinkForward(forward: self)
+ }
+
+ final var disposed: Bool {
+ return isFlagSet(self._disposed, 1)
+ }
+
+ func dispose() {
+ fetchOr(self._disposed, 1)
+ self._cancel.dispose()
+ }
+
+ deinit {
+#if TRACE_RESOURCES
+ _ = Resources.decrementTotal()
+#endif
+ }
+}
+
+final class SinkForward<Observer: ObserverType>: ObserverType {
+ typealias Element = Observer.Element
+
+ private let _forward: Sink<Observer>
+
+ init(forward: Sink<Observer>) {
+ self._forward = forward
+ }
+
+ final func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._forward._observer.on(event)
+ case .error, .completed:
+ self._forward._observer.on(event)
+ self._forward._cancel.dispose()
+ }
+ }
+}
--- /dev/null
+//
+// Skip.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Bypasses a specified number of elements in an observable sequence and then returns the remaining elements.
+
+ - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html)
+
+ - parameter count: The number of elements to skip before returning the remaining elements.
+ - returns: An observable sequence that contains the elements that occur after the specified index in the input sequence.
+ */
+ public func skip(_ count: Int)
+ -> Observable<Element> {
+ return SkipCount(source: self.asObservable(), count: count)
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers.
+
+ - seealso: [skip operator on reactivex.io](http://reactivex.io/documentation/operators/skip.html)
+
+ - parameter duration: Duration for skipping elements from the start of the sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence with the elements skipped during the specified duration from the start of the source sequence.
+ */
+ public func skip(_ duration: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return SkipTime(source: self.asObservable(), duration: duration, scheduler: scheduler)
+ }
+}
+
+// count version
+
+final private class SkipCountSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = SkipCount<Element>
+
+ let parent: Parent
+
+ var remaining: Int
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self.parent = parent
+ self.remaining = parent.count
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+
+ if self.remaining <= 0 {
+ self.forwardOn(.next(value))
+ }
+ else {
+ self.remaining -= 1
+ }
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+}
+
+final private class SkipCount<Element>: Producer<Element> {
+ let source: Observable<Element>
+ let count: Int
+
+ init(source: Observable<Element>, count: Int) {
+ self.source = source
+ self.count = count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SkipCountSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self.source.subscribe(sink)
+
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+// time version
+
+final private class SkipTimeSink<Element, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == Element {
+ typealias Parent = SkipTime<Element>
+
+ let parent: Parent
+
+ // state
+ var open = false
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self.parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ if self.open {
+ self.forwardOn(.next(value))
+ }
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+ func tick() {
+ self.open = true
+ }
+
+ func run() -> Disposable {
+ let disposeTimer = self.parent.scheduler.scheduleRelative((), dueTime: self.parent.duration) { _ in
+ self.tick()
+ return Disposables.create()
+ }
+
+ let disposeSubscription = self.parent.source.subscribe(self)
+
+ return Disposables.create(disposeTimer, disposeSubscription)
+ }
+}
+
+final private class SkipTime<Element>: Producer<Element> {
+ let source: Observable<Element>
+ let duration: RxTimeInterval
+ let scheduler: SchedulerType
+
+ init(source: Observable<Element>, duration: RxTimeInterval, scheduler: SchedulerType) {
+ self.source = source
+ self.scheduler = scheduler
+ self.duration = duration
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SkipTimeSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// SkipUntil.swift
+// RxSwift
+//
+// Created by Yury Korolev on 10/3/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns the elements from the source observable sequence that are emitted after the other observable sequence produces an element.
+
+ - seealso: [skipUntil operator on reactivex.io](http://reactivex.io/documentation/operators/skipuntil.html)
+
+ - parameter other: Observable sequence that starts propagation of elements of the source sequence.
+ - returns: An observable sequence containing the elements of the source sequence that are emitted after the other sequence emits an item.
+ */
+ public func skipUntil<Source: ObservableType>(_ other: Source)
+ -> Observable<Element> {
+ return SkipUntil(source: self.asObservable(), other: other.asObservable())
+ }
+}
+
+final private class SkipUntilSinkOther<Other, Observer: ObserverType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Parent = SkipUntilSink<Other, Observer>
+ typealias Element = Other
+
+ private let _parent: Parent
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ let _subscription = SingleAssignmentDisposable()
+
+ init(parent: Parent) {
+ self._parent = parent
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent._forwardElements = true
+ self._subscription.dispose()
+ case .error(let e):
+ self._parent.forwardOn(.error(e))
+ self._parent.dispose()
+ case .completed:
+ self._subscription.dispose()
+ }
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+
+}
+
+
+final private class SkipUntilSink<Other, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Element = Observer.Element
+ typealias Parent = SkipUntil<Element, Other>
+
+ let _lock = RecursiveLock()
+ private let _parent: Parent
+ fileprivate var _forwardElements = false
+
+ private let _sourceSubscription = SingleAssignmentDisposable()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ if self._forwardElements {
+ self.forwardOn(event)
+ }
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if self._forwardElements {
+ self.forwardOn(event)
+ }
+ self.dispose()
+ }
+ }
+
+ func run() -> Disposable {
+ let sourceSubscription = self._parent._source.subscribe(self)
+ let otherObserver = SkipUntilSinkOther(parent: self)
+ let otherSubscription = self._parent._other.subscribe(otherObserver)
+ self._sourceSubscription.setDisposable(sourceSubscription)
+ otherObserver._subscription.setDisposable(otherSubscription)
+
+ return Disposables.create(_sourceSubscription, otherObserver._subscription)
+ }
+}
+
+final private class SkipUntil<Element, Other>: Producer<Element> {
+
+ fileprivate let _source: Observable<Element>
+ fileprivate let _other: Observable<Other>
+
+ init(source: Observable<Element>, other: Observable<Other>) {
+ self._source = source
+ self._other = other
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SkipUntilSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// SkipWhile.swift
+// RxSwift
+//
+// Created by Yury Korolev on 10/9/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements.
+
+ - seealso: [skipWhile operator on reactivex.io](http://reactivex.io/documentation/operators/skipwhile.html)
+
+ - parameter predicate: A function to test each element for a condition.
+ - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate.
+ */
+ public func skipWhile(_ predicate: @escaping (Element) throws -> Bool) -> Observable<Element> {
+ return SkipWhile(source: self.asObservable(), predicate: predicate)
+ }
+}
+
+final private class SkipWhileSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = SkipWhile<Element>
+
+ private let _parent: Parent
+ private var _running = false
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ if !self._running {
+ do {
+ self._running = try !self._parent._predicate(value)
+ } catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return
+ }
+ }
+
+ if self._running {
+ self.forwardOn(.next(value))
+ }
+ case .error, .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+}
+
+final private class SkipWhile<Element>: Producer<Element> {
+ typealias Predicate = (Element) throws -> Bool
+
+ private let _source: Observable<Element>
+ fileprivate let _predicate: Predicate
+
+ init(source: Observable<Element>, predicate: @escaping Predicate) {
+ self._source = source
+ self._predicate = predicate
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SkipWhileSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// StartWith.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 4/6/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Prepends a sequence of values to an observable sequence.
+
+ - seealso: [startWith operator on reactivex.io](http://reactivex.io/documentation/operators/startwith.html)
+
+ - parameter elements: Elements to prepend to the specified sequence.
+ - returns: The source sequence prepended with the specified values.
+ */
+ public func startWith(_ elements: Element ...)
+ -> Observable<Element> {
+ return StartWith(source: self.asObservable(), elements: elements)
+ }
+}
+
+final private class StartWith<Element>: Producer<Element> {
+ let elements: [Element]
+ let source: Observable<Element>
+
+ init(source: Observable<Element>, elements: [Element]) {
+ self.source = source
+ self.elements = elements
+ super.init()
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ for e in self.elements {
+ observer.on(.next(e))
+ }
+
+ return (sink: Disposables.create(), subscription: self.source.subscribe(observer))
+ }
+}
--- /dev/null
+//
+// SubjectType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/1/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Represents an object that is both an observable sequence as well as an observer.
+public protocol SubjectType : ObservableType {
+ /// The type of the observer that represents this subject.
+ ///
+ /// Usually this type is type of subject itself, but it doesn't have to be.
+ associatedtype Observer: ObserverType
+
+ @available(*, deprecated, renamed: "Observer")
+ typealias SubjectObserverType = Observer
+
+ /// Returns observer interface for subject.
+ ///
+ /// - returns: Observer interface for subject.
+ func asObserver() -> Observer
+
+}
--- /dev/null
+//
+// SubscribeOn.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Wraps the source sequence in order to run its subscription and unsubscription logic on the specified
+ scheduler.
+
+ This operation is not commonly used.
+
+ This only performs the side-effects of subscription and unsubscription on the specified scheduler.
+
+ In order to invoke observer callbacks on a `scheduler`, use `observeOn`.
+
+ - seealso: [subscribeOn operator on reactivex.io](http://reactivex.io/documentation/operators/subscribeon.html)
+
+ - parameter scheduler: Scheduler to perform subscription and unsubscription actions on.
+ - returns: The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler.
+ */
+ public func subscribeOn(_ scheduler: ImmediateSchedulerType)
+ -> Observable<Element> {
+ return SubscribeOn(source: self, scheduler: scheduler)
+ }
+}
+
+final private class SubscribeOnSink<Ob: ObservableType, Observer: ObserverType>: Sink<Observer>, ObserverType where Ob.Element == Observer.Element {
+ typealias Element = Observer.Element
+ typealias Parent = SubscribeOn<Ob>
+
+ let parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self.parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.forwardOn(event)
+
+ if event.isStopEvent {
+ self.dispose()
+ }
+ }
+
+ func run() -> Disposable {
+ let disposeEverything = SerialDisposable()
+ let cancelSchedule = SingleAssignmentDisposable()
+
+ disposeEverything.disposable = cancelSchedule
+
+ let disposeSchedule = self.parent.scheduler.schedule(()) { _ -> Disposable in
+ let subscription = self.parent.source.subscribe(self)
+ disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription)
+ return Disposables.create()
+ }
+
+ cancelSchedule.setDisposable(disposeSchedule)
+
+ return disposeEverything
+ }
+}
+
+final private class SubscribeOn<Ob: ObservableType>: Producer<Ob.Element> {
+ let source: Ob
+ let scheduler: ImmediateSchedulerType
+
+ init(source: Ob, scheduler: ImmediateSchedulerType) {
+ self.source = source
+ self.scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Ob.Element {
+ let sink = SubscribeOnSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// SubscriptionDisposable.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+struct SubscriptionDisposable<T: SynchronizedUnsubscribeType> : Disposable {
+ private let _key: T.DisposeKey
+ private weak var _owner: T?
+
+ init(owner: T, key: T.DisposeKey) {
+ self._owner = owner
+ self._key = key
+ }
+
+ func dispose() {
+ self._owner?.synchronizedUnsubscribe(self._key)
+ }
+}
--- /dev/null
+//
+// SwiftSupport.swift
+// RxSwift
+//
+// Created by Volodymyr Gorbenko on 3/6/17.
+// Copyright © 2017 Krunoslav Zaher. All rights reserved.
+//
+
+import Foundation
+
+typealias IntMax = Int64
+public typealias RxAbstractInteger = FixedWidthInteger
+
+extension SignedInteger {
+ func toIntMax() -> IntMax {
+ return IntMax(self)
+ }
+}
--- /dev/null
+//
+// Switch.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Projects each element of an observable sequence into a new sequence of observable sequences and then
+ transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.
+
+ It is a combination of `map` + `switchLatest` operator
+
+ - seealso: [flatMapLatest operator on reactivex.io](http://reactivex.io/documentation/operators/flatmap.html)
+
+ - parameter selector: A transform function to apply to each element.
+ - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an
+ Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received.
+ */
+ public func flatMapLatest<Source: ObservableConvertibleType>(_ selector: @escaping (Element) throws -> Source)
+ -> Observable<Source.Element> {
+ return FlatMapLatest(source: self.asObservable(), selector: selector)
+ }
+}
+
+extension ObservableType where Element : ObservableConvertibleType {
+
+ /**
+ Transforms an observable sequence of observable sequences into an observable sequence
+ producing values only from the most recent observable sequence.
+
+ Each time a new inner observable sequence is received, unsubscribe from the
+ previous inner observable sequence.
+
+ - seealso: [switch operator on reactivex.io](http://reactivex.io/documentation/operators/switch.html)
+
+ - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received.
+ */
+ public func switchLatest() -> Observable<Element.Element> {
+ return Switch(source: self.asObservable())
+ }
+}
+
+private class SwitchSink<SourceType, Source: ObservableConvertibleType, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType where Source.Element == Observer.Element {
+ typealias Element = SourceType
+
+ private let _subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable()
+ private let _innerSubscription: SerialDisposable = SerialDisposable()
+
+ let _lock = RecursiveLock()
+
+ // state
+ fileprivate var _stopped = false
+ fileprivate var _latest = 0
+ fileprivate var _hasLatest = false
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run(_ source: Observable<SourceType>) -> Disposable {
+ let subscription = source.subscribe(self)
+ self._subscriptions.setDisposable(subscription)
+ return Disposables.create(_subscriptions, _innerSubscription)
+ }
+
+ func performMap(_ element: SourceType) throws -> Source {
+ rxAbstractMethod()
+ }
+
+ @inline(__always)
+ final private func nextElementArrived(element: Element) -> (Int, Observable<Source.Element>)? {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ do {
+ let observable = try self.performMap(element).asObservable()
+ self._hasLatest = true
+ self._latest = self._latest &+ 1
+ return (self._latest, observable)
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+
+ return nil
+ // }
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ if let (latest, observable) = self.nextElementArrived(element: element) {
+ let d = SingleAssignmentDisposable()
+ self._innerSubscription.disposable = d
+
+ let observer = SwitchSinkIter(parent: self, id: latest, _self: d)
+ let disposable = observable.subscribe(observer)
+ d.setDisposable(disposable)
+ }
+ case .error(let error):
+ self._lock.lock(); defer { self._lock.unlock() }
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self._lock.lock(); defer { self._lock.unlock() }
+ self._stopped = true
+
+ self._subscriptions.dispose()
+
+ if !self._hasLatest {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ }
+}
+
+final private class SwitchSinkIter<SourceType, Source: ObservableConvertibleType, Observer: ObserverType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType where Source.Element == Observer.Element {
+ typealias Element = Source.Element
+ typealias Parent = SwitchSink<SourceType, Source, Observer>
+
+ private let _parent: Parent
+ private let _id: Int
+ private let _self: Disposable
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ init(parent: Parent, id: Int, _self: Disposable) {
+ self._parent = parent
+ self._id = id
+ self._self = _self
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next: break
+ case .error, .completed:
+ self._self.dispose()
+ }
+
+ if self._parent._latest != self._id {
+ return
+ }
+
+ switch event {
+ case .next:
+ self._parent.forwardOn(event)
+ case .error:
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ case .completed:
+ self._parent._hasLatest = false
+ if self._parent._stopped {
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ }
+ }
+ }
+}
+
+// MARK: Specializations
+
+final private class SwitchIdentitySink<Source: ObservableConvertibleType, Observer: ObserverType>: SwitchSink<Source, Source, Observer>
+ where Observer.Element == Source.Element {
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ override func performMap(_ element: Source) throws -> Source {
+ return element
+ }
+}
+
+final private class MapSwitchSink<SourceType, Source: ObservableConvertibleType, Observer: ObserverType>: SwitchSink<SourceType, Source, Observer> where Observer.Element == Source.Element {
+ typealias Selector = (SourceType) throws -> Source
+
+ private let _selector: Selector
+
+ init(selector: @escaping Selector, observer: Observer, cancel: Cancelable) {
+ self._selector = selector
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ override func performMap(_ element: SourceType) throws -> Source {
+ return try self._selector(element)
+ }
+}
+
+// MARK: Producers
+
+final private class Switch<Source: ObservableConvertibleType>: Producer<Source.Element> {
+ private let _source: Observable<Source>
+
+ init(source: Observable<Source>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Source.Element {
+ let sink = SwitchIdentitySink<Source, Observer>(observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class FlatMapLatest<SourceType, Source: ObservableConvertibleType>: Producer<Source.Element> {
+ typealias Selector = (SourceType) throws -> Source
+
+ private let _source: Observable<SourceType>
+ private let _selector: Selector
+
+ init(source: Observable<SourceType>, selector: @escaping Selector) {
+ self._source = source
+ self._selector = selector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Source.Element {
+ let sink = MapSwitchSink<SourceType, Source, Observer>(selector: self._selector, observer: observer, cancel: cancel)
+ let subscription = sink.run(self._source)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// SwitchIfEmpty.swift
+// RxSwift
+//
+// Created by sergdort on 23/12/2016.
+// Copyright © 2016 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Returns the elements of the specified sequence or `switchTo` sequence if the sequence is empty.
+
+ - seealso: [DefaultIfEmpty operator on reactivex.io](http://reactivex.io/documentation/operators/defaultifempty.html)
+
+ - parameter switchTo: Observable sequence being returned when source sequence is empty.
+ - returns: Observable sequence that contains elements from switchTo sequence if source is empty, otherwise returns source sequence elements.
+ */
+ public func ifEmpty(switchTo other: Observable<Element>) -> Observable<Element> {
+ return SwitchIfEmpty(source: self.asObservable(), ifEmpty: other)
+ }
+}
+
+final private class SwitchIfEmpty<Element>: Producer<Element> {
+
+ private let _source: Observable<Element>
+ private let _ifEmpty: Observable<Element>
+
+ init(source: Observable<Element>, ifEmpty: Observable<Element>) {
+ self._source = source
+ self._ifEmpty = ifEmpty
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = SwitchIfEmptySink(ifEmpty: self._ifEmpty,
+ observer: observer,
+ cancel: cancel)
+ let subscription = sink.run(self._source.asObservable())
+
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+final private class SwitchIfEmptySink<Observer: ObserverType>: Sink<Observer>
+ , ObserverType {
+ typealias Element = Observer.Element
+
+ private let _ifEmpty: Observable<Element>
+ private var _isEmpty = true
+ private let _ifEmptySubscription = SingleAssignmentDisposable()
+
+ init(ifEmpty: Observable<Element>, observer: Observer, cancel: Cancelable) {
+ self._ifEmpty = ifEmpty
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run(_ source: Observable<Observer.Element>) -> Disposable {
+ let subscription = source.subscribe(self)
+ return Disposables.create(subscription, _ifEmptySubscription)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._isEmpty = false
+ self.forwardOn(event)
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ guard self._isEmpty else {
+ self.forwardOn(.completed)
+ self.dispose()
+ return
+ }
+ let ifEmptySink = SwitchIfEmptySinkIter(parent: self)
+ self._ifEmptySubscription.setDisposable(self._ifEmpty.subscribe(ifEmptySink))
+ }
+ }
+}
+
+final private class SwitchIfEmptySinkIter<Observer: ObserverType>
+ : ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = SwitchIfEmptySink<Observer>
+
+ private let _parent: Parent
+
+ init(parent: Parent) {
+ self._parent = parent
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent.forwardOn(event)
+ case .error:
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ case .completed:
+ self._parent.forwardOn(event)
+ self._parent.dispose()
+ }
+ }
+}
--- /dev/null
+//
+// SynchronizedDisposeType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol SynchronizedDisposeType : class, Disposable, Lock {
+ func _synchronized_dispose()
+}
+
+extension SynchronizedDisposeType {
+ func synchronizedDispose() {
+ self.lock(); defer { self.unlock() }
+ self._synchronized_dispose()
+ }
+}
--- /dev/null
+//
+// SynchronizedOnType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol SynchronizedOnType : class, ObserverType, Lock {
+ func _synchronized_on(_ event: Event<Element>)
+}
+
+extension SynchronizedOnType {
+ func synchronizedOn(_ event: Event<Element>) {
+ self.lock(); defer { self.unlock() }
+ self._synchronized_on(event)
+ }
+}
--- /dev/null
+//
+// SynchronizedUnsubscribeType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 10/25/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol SynchronizedUnsubscribeType : class {
+ associatedtype DisposeKey
+
+ func synchronizedUnsubscribe(_ disposeKey: DisposeKey)
+}
--- /dev/null
+//
+// TailRecursiveSink.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/21/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+enum TailRecursiveSinkCommand {
+ case moveNext
+ case dispose
+}
+
+#if DEBUG || TRACE_RESOURCES
+ public var maxTailRecursiveSinkStackSize = 0
+#endif
+
+/// This class is usually used with `Generator` version of the operators.
+class TailRecursiveSink<Sequence: Swift.Sequence, Observer: ObserverType>
+ : Sink<Observer>
+ , InvocableWithValueType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element {
+ typealias Value = TailRecursiveSinkCommand
+ typealias Element = Observer.Element
+ typealias SequenceGenerator = (generator: Sequence.Iterator, remaining: IntMax?)
+
+ var _generators: [SequenceGenerator] = []
+ var _isDisposed = false
+ var _subscription = SerialDisposable()
+
+ // this is thread safe object
+ var _gate = AsyncLock<InvocableScheduledItem<TailRecursiveSink<Sequence, Observer>>>()
+
+ override init(observer: Observer, cancel: Cancelable) {
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run(_ sources: SequenceGenerator) -> Disposable {
+ self._generators.append(sources)
+
+ self.schedule(.moveNext)
+
+ return self._subscription
+ }
+
+ func invoke(_ command: TailRecursiveSinkCommand) {
+ switch command {
+ case .dispose:
+ self.disposeCommand()
+ case .moveNext:
+ self.moveNextCommand()
+ }
+ }
+
+ // simple implementation for now
+ func schedule(_ command: TailRecursiveSinkCommand) {
+ self._gate.invoke(InvocableScheduledItem(invocable: self, state: command))
+ }
+
+ func done() {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+
+ func extract(_ observable: Observable<Element>) -> SequenceGenerator? {
+ rxAbstractMethod()
+ }
+
+ // should be done on gate locked
+
+ private func moveNextCommand() {
+ var next: Observable<Element>?
+
+ repeat {
+ guard let (g, left) = self._generators.last else {
+ break
+ }
+
+ if self._isDisposed {
+ return
+ }
+
+ self._generators.removeLast()
+
+ var e = g
+
+ guard let nextCandidate = e.next()?.asObservable() else {
+ continue
+ }
+
+ // `left` is a hint of how many elements are left in generator.
+ // In case this is the last element, then there is no need to push
+ // that generator on stack.
+ //
+ // This is an optimization used to make sure in tail recursive case
+ // there is no memory leak in case this operator is used to generate non terminating
+ // sequence.
+
+ if let knownOriginalLeft = left {
+ // `- 1` because generator.next() has just been called
+ if knownOriginalLeft - 1 >= 1 {
+ self._generators.append((e, knownOriginalLeft - 1))
+ }
+ }
+ else {
+ self._generators.append((e, nil))
+ }
+
+ let nextGenerator = self.extract(nextCandidate)
+
+ if let nextGenerator = nextGenerator {
+ self._generators.append(nextGenerator)
+ #if DEBUG || TRACE_RESOURCES
+ if maxTailRecursiveSinkStackSize < self._generators.count {
+ maxTailRecursiveSinkStackSize = self._generators.count
+ }
+ #endif
+ }
+ else {
+ next = nextCandidate
+ }
+ } while next == nil
+
+ guard let existingNext = next else {
+ self.done()
+ return
+ }
+
+ let disposable = SingleAssignmentDisposable()
+ self._subscription.disposable = disposable
+ disposable.setDisposable(self.subscribeToNext(existingNext))
+ }
+
+ func subscribeToNext(_ source: Observable<Element>) -> Disposable {
+ rxAbstractMethod()
+ }
+
+ func disposeCommand() {
+ self._isDisposed = true
+ self._generators.removeAll(keepingCapacity: false)
+ }
+
+ override func dispose() {
+ super.dispose()
+
+ self._subscription.dispose()
+ self._gate.dispose()
+
+ self.schedule(.dispose)
+ }
+}
+
--- /dev/null
+//
+// Take.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/12/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns a specified number of contiguous elements from the start of an observable sequence.
+
+ - seealso: [take operator on reactivex.io](http://reactivex.io/documentation/operators/take.html)
+
+ - parameter count: The number of elements to return.
+ - returns: An observable sequence that contains the specified number of elements from the start of the input sequence.
+ */
+ public func take(_ count: Int)
+ -> Observable<Element> {
+ if count == 0 {
+ return Observable.empty()
+ }
+ else {
+ return TakeCount(source: self.asObservable(), count: count)
+ }
+ }
+}
+
+extension ObservableType {
+
+ /**
+ Takes elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers.
+
+ - seealso: [take operator on reactivex.io](http://reactivex.io/documentation/operators/take.html)
+
+ - parameter duration: Duration for taking elements from the start of the sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence with the elements taken during the specified duration from the start of the source sequence.
+ */
+ public func take(_ duration: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return TakeTime(source: self.asObservable(), duration: duration, scheduler: scheduler)
+ }
+}
+
+// count version
+
+final private class TakeCountSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = TakeCount<Element>
+
+ private let _parent: Parent
+
+ private var _remaining: Int
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._remaining = parent._count
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+
+ if self._remaining > 0 {
+ self._remaining -= 1
+
+ self.forwardOn(.next(value))
+
+ if self._remaining == 0 {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+}
+
+final private class TakeCount<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+ fileprivate let _count: Int
+
+ init(source: Observable<Element>, count: Int) {
+ if count < 0 {
+ rxFatalError("count can't be negative")
+ }
+ self._source = source
+ self._count = count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeCountSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+// time version
+
+final private class TakeTimeSink<Element, Observer: ObserverType>
+ : Sink<Observer>
+ , LockOwnerType
+ , ObserverType
+ , SynchronizedOnType where Observer.Element == Element {
+ typealias Parent = TakeTime<Element>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ self.forwardOn(.next(value))
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+ func tick() {
+ self._lock.lock(); defer { self._lock.unlock() }
+
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+
+ func run() -> Disposable {
+ let disposeTimer = self._parent._scheduler.scheduleRelative((), dueTime: self._parent._duration) { _ in
+ self.tick()
+ return Disposables.create()
+ }
+
+ let disposeSubscription = self._parent._source.subscribe(self)
+
+ return Disposables.create(disposeTimer, disposeSubscription)
+ }
+}
+
+final private class TakeTime<Element>: Producer<Element> {
+ typealias TimeInterval = RxTimeInterval
+
+ fileprivate let _source: Observable<Element>
+ fileprivate let _duration: TimeInterval
+ fileprivate let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, duration: TimeInterval, scheduler: SchedulerType) {
+ self._source = source
+ self._scheduler = scheduler
+ self._duration = duration
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeTimeSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// TakeLast.swift
+// RxSwift
+//
+// Created by Tomi Koskinen on 25/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns a specified number of contiguous elements from the end of an observable sequence.
+
+ This operator accumulates a buffer with a length enough to store elements count elements. Upon completion of the source sequence, this buffer is drained on the result sequence. This causes the elements to be delayed.
+
+ - seealso: [takeLast operator on reactivex.io](http://reactivex.io/documentation/operators/takelast.html)
+
+ - parameter count: Number of elements to take from the end of the source sequence.
+ - returns: An observable sequence containing the specified number of elements from the end of the source sequence.
+ */
+ public func takeLast(_ count: Int)
+ -> Observable<Element> {
+ return TakeLast(source: self.asObservable(), count: count)
+ }
+}
+
+final private class TakeLastSink<Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = TakeLast<Element>
+
+ private let _parent: Parent
+
+ private var _elements: Queue<Element>
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._elements = Queue<Element>(capacity: parent._count + 1)
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ self._elements.enqueue(value)
+ if self._elements.count > self._parent._count {
+ _ = self._elements.dequeue()
+ }
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ for e in self._elements {
+ self.forwardOn(.next(e))
+ }
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class TakeLast<Element>: Producer<Element> {
+ private let _source: Observable<Element>
+ fileprivate let _count: Int
+
+ init(source: Observable<Element>, count: Int) {
+ if count < 0 {
+ rxFatalError("count can't be negative")
+ }
+ self._source = source
+ self._count = count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeLastSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// TakeUntil.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns the elements from the source observable sequence until the other observable sequence produces an element.
+
+ - seealso: [takeUntil operator on reactivex.io](http://reactivex.io/documentation/operators/takeuntil.html)
+
+ - parameter other: Observable sequence that terminates propagation of elements of the source sequence.
+ - returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation.
+ */
+ public func takeUntil<Source: ObservableType>(_ other: Source)
+ -> Observable<Element> {
+ return TakeUntil(source: self.asObservable(), other: other.asObservable())
+ }
+
+ /**
+ Returns elements from an observable sequence until the specified condition is true.
+
+ - seealso: [takeUntil operator on reactivex.io](http://reactivex.io/documentation/operators/takeuntil.html)
+
+ - parameter behavior: Whether or not to include the last element matching the predicate.
+ - parameter predicate: A function to test each element for a condition.
+ - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test passes.
+ */
+ public func takeUntil(_ behavior: TakeUntilBehavior,
+ predicate: @escaping (Element) throws -> Bool)
+ -> Observable<Element> {
+ return TakeUntilPredicate(source: self.asObservable(),
+ behavior: behavior,
+ predicate: predicate)
+ }
+}
+
+/// Behaviors for the `takeUntil(_ behavior:predicate:)` operator.
+public enum TakeUntilBehavior {
+ /// Include the last element matching the predicate.
+ case inclusive
+
+ /// Exclude the last element matching the predicate.
+ case exclusive
+}
+
+// MARK: - TakeUntil Observable
+final private class TakeUntilSinkOther<Other, Observer: ObserverType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Parent = TakeUntilSink<Other, Observer>
+ typealias Element = Other
+
+ private let _parent: Parent
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ fileprivate let _subscription = SingleAssignmentDisposable()
+
+ init(parent: Parent) {
+ self._parent = parent
+#if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+#endif
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self._parent.forwardOn(.completed)
+ self._parent.dispose()
+ case .error(let e):
+ self._parent.forwardOn(.error(e))
+ self._parent.dispose()
+ case .completed:
+ self._subscription.dispose()
+ }
+ }
+
+#if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+#endif
+}
+
+final private class TakeUntilSink<Other, Observer: ObserverType>
+ : Sink<Observer>
+ , LockOwnerType
+ , ObserverType
+ , SynchronizedOnType {
+ typealias Element = Observer.Element
+ typealias Parent = TakeUntil<Element, Other>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ self.forwardOn(event)
+ case .error:
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+ func run() -> Disposable {
+ let otherObserver = TakeUntilSinkOther(parent: self)
+ let otherSubscription = self._parent._other.subscribe(otherObserver)
+ otherObserver._subscription.setDisposable(otherSubscription)
+ let sourceSubscription = self._parent._source.subscribe(self)
+
+ return Disposables.create(sourceSubscription, otherObserver._subscription)
+ }
+}
+
+final private class TakeUntil<Element, Other>: Producer<Element> {
+
+ fileprivate let _source: Observable<Element>
+ fileprivate let _other: Observable<Other>
+
+ init(source: Observable<Element>, other: Observable<Other>) {
+ self._source = source
+ self._other = other
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeUntilSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+// MARK: - TakeUntil Predicate
+final private class TakeUntilPredicateSink<Observer: ObserverType>
+ : Sink<Observer>, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = TakeUntilPredicate<Element>
+
+ private let _parent: Parent
+ private var _running = true
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ if !self._running {
+ return
+ }
+
+ do {
+ self._running = try !self._parent._predicate(value)
+ } catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return
+ }
+
+ if self._running {
+ self.forwardOn(.next(value))
+ } else {
+ if self._parent._behavior == .inclusive {
+ self.forwardOn(.next(value))
+ }
+
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ case .error, .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+}
+
+final private class TakeUntilPredicate<Element>: Producer<Element> {
+ typealias Predicate = (Element) throws -> Bool
+
+ private let _source: Observable<Element>
+ fileprivate let _predicate: Predicate
+ fileprivate let _behavior: TakeUntilBehavior
+
+ init(source: Observable<Element>,
+ behavior: TakeUntilBehavior,
+ predicate: @escaping Predicate) {
+ self._source = source
+ self._behavior = behavior
+ self._predicate = predicate
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeUntilPredicateSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// TakeWhile.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Returns elements from an observable sequence as long as a specified condition is true.
+
+ - seealso: [takeWhile operator on reactivex.io](http://reactivex.io/documentation/operators/takewhile.html)
+
+ - parameter predicate: A function to test each element for a condition.
+ - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.
+ */
+ public func takeWhile(_ predicate: @escaping (Element) throws -> Bool)
+ -> Observable<Element> {
+ return TakeWhile(source: self.asObservable(), predicate: predicate)
+ }
+}
+
+final private class TakeWhileSink<Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = TakeWhile<Element>
+
+ private let _parent: Parent
+
+ private var _running = true
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next(let value):
+ if !self._running {
+ return
+ }
+
+ do {
+ self._running = try self._parent._predicate(value)
+ } catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ return
+ }
+
+ if self._running {
+ self.forwardOn(.next(value))
+ } else {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ case .error, .completed:
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+
+}
+
+final private class TakeWhile<Element>: Producer<Element> {
+ typealias Predicate = (Element) throws -> Bool
+
+ private let _source: Observable<Element>
+ fileprivate let _predicate: Predicate
+
+ init(source: Observable<Element>, predicate: @escaping Predicate) {
+ self._source = source
+ self._predicate = predicate
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TakeWhileSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Throttle.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 3/22/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import struct Foundation.Date
+
+extension ObservableType {
+
+ /**
+ Returns an Observable that emits the first and the latest item emitted by the source Observable during sequential time windows of a specified duration.
+
+ This operator makes sure that no two elements are emitted in less then dueTime.
+
+ - seealso: [debounce operator on reactivex.io](http://reactivex.io/documentation/operators/debounce.html)
+
+ - parameter dueTime: Throttling duration for each element.
+ - parameter latest: Should latest element received in a dueTime wide time window since last element emission be emitted.
+ - parameter scheduler: Scheduler to run the throttle timers on.
+ - returns: The throttled sequence.
+ */
+ public func throttle(_ dueTime: RxTimeInterval, latest: Bool = true, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Throttle(source: self.asObservable(), dueTime: dueTime, latest: latest, scheduler: scheduler)
+ }
+}
+
+final private class ThrottleSink<Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias Element = Observer.Element
+ typealias ParentType = Throttle<Element>
+
+ private let _parent: ParentType
+
+ let _lock = RecursiveLock()
+
+ // state
+ private var _lastUnsentElement: Element?
+ private var _lastSentTime: Date?
+ private var _completed: Bool = false
+
+ let cancellable = SerialDisposable()
+
+ init(parent: ParentType, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let subscription = self._parent._source.subscribe(self)
+
+ return Disposables.create(subscription, cancellable)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case .next(let element):
+ let now = self._parent._scheduler.now
+
+ let reducedScheduledTime: RxTimeInterval
+
+ if let lastSendingTime = self._lastSentTime {
+ reducedScheduledTime = self._parent._dueTime.reduceWithSpanBetween(earlierDate: lastSendingTime, laterDate: now)
+ }
+ else {
+ reducedScheduledTime = .nanoseconds(0)
+ }
+
+ if reducedScheduledTime.isNow {
+ self.sendNow(element: element)
+ return
+ }
+
+ if !self._parent._latest {
+ return
+ }
+
+ let isThereAlreadyInFlightRequest = self._lastUnsentElement != nil
+
+ self._lastUnsentElement = element
+
+ if isThereAlreadyInFlightRequest {
+ return
+ }
+
+ let scheduler = self._parent._scheduler
+
+ let d = SingleAssignmentDisposable()
+ self.cancellable.disposable = d
+
+ d.setDisposable(scheduler.scheduleRelative(0, dueTime: reducedScheduledTime, action: self.propagate))
+ case .error:
+ self._lastUnsentElement = nil
+ self.forwardOn(event)
+ self.dispose()
+ case .completed:
+ if self._lastUnsentElement != nil {
+ self._completed = true
+ }
+ else {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+ }
+
+ private func sendNow(element: Element) {
+ self._lastUnsentElement = nil
+ self.forwardOn(.next(element))
+ // in case element processing takes a while, this should give some more room
+ self._lastSentTime = self._parent._scheduler.now
+ }
+
+ func propagate(_: Int) -> Disposable {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ if let lastUnsentElement = self._lastUnsentElement {
+ self.sendNow(element: lastUnsentElement)
+ }
+
+ if self._completed {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ // }
+ return Disposables.create()
+ }
+}
+
+final private class Throttle<Element>: Producer<Element> {
+ fileprivate let _source: Observable<Element>
+ fileprivate let _dueTime: RxTimeInterval
+ fileprivate let _latest: Bool
+ fileprivate let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, dueTime: RxTimeInterval, latest: Bool, scheduler: SchedulerType) {
+ self._source = source
+ self._dueTime = dueTime
+ self._latest = latest
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = ThrottleSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+
+}
--- /dev/null
+//
+// Timeout.swift
+// RxSwift
+//
+// Created by Tomi Koskinen on 13/11/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Applies a timeout policy for each element in the observable sequence. If the next element isn't received within the specified timeout duration starting from its predecessor, a TimeoutError is propagated to the observer.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: An observable sequence with a `RxError.timeout` in case of a timeout.
+ */
+ public func timeout(_ dueTime: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Timeout(source: self.asObservable(), dueTime: dueTime, other: Observable.error(RxError.timeout), scheduler: scheduler)
+ }
+
+ /**
+ Applies a timeout policy for each element in the observable sequence, using the specified scheduler to run timeout timers. If the next element isn't received within the specified timeout duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on.
+
+ - seealso: [timeout operator on reactivex.io](http://reactivex.io/documentation/operators/timeout.html)
+
+ - parameter dueTime: Maximum duration between values before a timeout occurs.
+ - parameter other: Sequence to return in case of a timeout.
+ - parameter scheduler: Scheduler to run the timeout timer on.
+ - returns: The source sequence switching to the other sequence in case of a timeout.
+ */
+ public func timeout<Source: ObservableConvertibleType>(_ dueTime: RxTimeInterval, other: Source, scheduler: SchedulerType)
+ -> Observable<Element> where Element == Source.Element {
+ return Timeout(source: self.asObservable(), dueTime: dueTime, other: other.asObservable(), scheduler: scheduler)
+ }
+}
+
+final private class TimeoutSink<Observer: ObserverType>: Sink<Observer>, LockOwnerType, ObserverType {
+ typealias Element = Observer.Element
+ typealias Parent = Timeout<Element>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ private let _timerD = SerialDisposable()
+ private let _subscription = SerialDisposable()
+
+ private var _id = 0
+ private var _switched = false
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let original = SingleAssignmentDisposable()
+ self._subscription.disposable = original
+
+ self._createTimeoutTimer()
+
+ original.setDisposable(self._parent._source.subscribe(self))
+
+ return Disposables.create(_subscription, _timerD)
+ }
+
+ func on(_ event: Event<Element>) {
+ switch event {
+ case .next:
+ var onNextWins = false
+
+ self._lock.performLocked {
+ onNextWins = !self._switched
+ if onNextWins {
+ self._id = self._id &+ 1
+ }
+ }
+
+ if onNextWins {
+ self.forwardOn(event)
+ self._createTimeoutTimer()
+ }
+ case .error, .completed:
+ var onEventWins = false
+
+ self._lock.performLocked {
+ onEventWins = !self._switched
+ if onEventWins {
+ self._id = self._id &+ 1
+ }
+ }
+
+ if onEventWins {
+ self.forwardOn(event)
+ self.dispose()
+ }
+ }
+ }
+
+ private func _createTimeoutTimer() {
+ if self._timerD.isDisposed {
+ return
+ }
+
+ let nextTimer = SingleAssignmentDisposable()
+ self._timerD.disposable = nextTimer
+
+ let disposeSchedule = self._parent._scheduler.scheduleRelative(self._id, dueTime: self._parent._dueTime) { state in
+
+ var timerWins = false
+
+ self._lock.performLocked {
+ self._switched = (state == self._id)
+ timerWins = self._switched
+ }
+
+ if timerWins {
+ self._subscription.disposable = self._parent._other.subscribe(self.forwarder())
+ }
+
+ return Disposables.create()
+ }
+
+ nextTimer.setDisposable(disposeSchedule)
+ }
+}
+
+
+final private class Timeout<Element>: Producer<Element> {
+ fileprivate let _source: Observable<Element>
+ fileprivate let _dueTime: RxTimeInterval
+ fileprivate let _other: Observable<Element>
+ fileprivate let _scheduler: SchedulerType
+
+ init(source: Observable<Element>, dueTime: RxTimeInterval, other: Observable<Element>, scheduler: SchedulerType) {
+ self._source = source
+ self._dueTime = dueTime
+ self._other = other
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = TimeoutSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Timer.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 6/7/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType where Element : RxAbstractInteger {
+ /**
+ Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages.
+
+ - seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html)
+
+ - parameter period: Period for producing the values in the resulting sequence.
+ - parameter scheduler: Scheduler to run the timer on.
+ - returns: An observable sequence that produces a value after each period.
+ */
+ public static func interval(_ period: RxTimeInterval, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Timer(
+ dueTime: period,
+ period: period,
+ scheduler: scheduler
+ )
+ }
+}
+
+extension ObservableType where Element: RxAbstractInteger {
+ /**
+ Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers.
+
+ - seealso: [timer operator on reactivex.io](http://reactivex.io/documentation/operators/timer.html)
+
+ - parameter dueTime: Relative time at which to produce the first value.
+ - parameter period: Period to produce subsequent values.
+ - parameter scheduler: Scheduler to run timers on.
+ - returns: An observable sequence that produces a value after due time has elapsed and then each period.
+ */
+ public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
+ -> Observable<Element> {
+ return Timer(
+ dueTime: dueTime,
+ period: period,
+ scheduler: scheduler
+ )
+ }
+}
+
+import Foundation
+
+final private class TimerSink<Observer: ObserverType> : Sink<Observer> where Observer.Element : RxAbstractInteger {
+ typealias Parent = Timer<Observer.Element>
+
+ private let _parent: Parent
+ private let _lock = RecursiveLock()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.schedulePeriodic(0 as Observer.Element, startAfter: self._parent._dueTime, period: self._parent._period!) { state in
+ self._lock.lock(); defer { self._lock.unlock() }
+ self.forwardOn(.next(state))
+ return state &+ 1
+ }
+ }
+}
+
+final private class TimerOneOffSink<Observer: ObserverType>: Sink<Observer> where Observer.Element: RxAbstractInteger {
+ typealias Parent = Timer<Observer.Element>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ return self._parent._scheduler.scheduleRelative(self, dueTime: self._parent._dueTime) { [unowned self] _ -> Disposable in
+ self.forwardOn(.next(0))
+ self.forwardOn(.completed)
+ self.dispose()
+
+ return Disposables.create()
+ }
+ }
+}
+
+final private class Timer<Element: RxAbstractInteger>: Producer<Element> {
+ fileprivate let _scheduler: SchedulerType
+ fileprivate let _dueTime: RxTimeInterval
+ fileprivate let _period: RxTimeInterval?
+
+ init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) {
+ self._scheduler = scheduler
+ self._dueTime = dueTime
+ self._period = period
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ if self._period != nil {
+ let sink = TimerSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+ else {
+ let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+ }
+}
--- /dev/null
+//
+// ToArray.swift
+// RxSwift
+//
+// Created by Junior B. on 20/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+
+extension ObservableType {
+
+ /**
+ Converts an Observable into a Single that emits the whole sequence as a single array and then terminates.
+
+ For aggregation behavior see `reduce`.
+
+ - seealso: [toArray operator on reactivex.io](http://reactivex.io/documentation/operators/to.html)
+
+ - returns: A Single sequence containing all the emitted elements as array.
+ */
+ public func toArray()
+ -> Single<[Element]> {
+ return PrimitiveSequence(raw: ToArray(source: self.asObservable()))
+ }
+}
+
+final private class ToArraySink<SourceType, Observer: ObserverType>: Sink<Observer>, ObserverType where Observer.Element == [SourceType] {
+ typealias Parent = ToArray<SourceType>
+
+ let _parent: Parent
+ var _list = [SourceType]()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case .next(let value):
+ self._list.append(value)
+ case .error(let e):
+ self.forwardOn(.error(e))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.next(self._list))
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class ToArray<SourceType>: Producer<[SourceType]> {
+ let _source: Observable<SourceType>
+
+ init(source: Observable<SourceType>) {
+ self._source = source
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == [SourceType] {
+ let sink = ToArraySink(parent: self, observer: observer, cancel: cancel)
+ let subscription = self._source.subscribe(sink)
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Using.swift
+// RxSwift
+//
+// Created by Yury Korolev on 10/15/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime.
+
+ - seealso: [using operator on reactivex.io](http://reactivex.io/documentation/operators/using.html)
+
+ - parameter resourceFactory: Factory function to obtain a resource object.
+ - parameter observableFactory: Factory function to obtain an observable sequence that depends on the obtained resource.
+ - returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object.
+ */
+ public static func using<Resource: Disposable>(_ resourceFactory: @escaping () throws -> Resource, observableFactory: @escaping (Resource) throws -> Observable<Element>) -> Observable<Element> {
+ return Using(resourceFactory: resourceFactory, observableFactory: observableFactory)
+ }
+}
+
+final private class UsingSink<ResourceType: Disposable, Observer: ObserverType>: Sink<Observer>, ObserverType {
+ typealias SourceType = Observer.Element
+ typealias Parent = Using<SourceType, ResourceType>
+
+ private let _parent: Parent
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ var disposable = Disposables.create()
+
+ do {
+ let resource = try self._parent._resourceFactory()
+ disposable = resource
+ let source = try self._parent._observableFactory(resource)
+
+ return Disposables.create(
+ source.subscribe(self),
+ disposable
+ )
+ } catch let error {
+ return Disposables.create(
+ Observable.error(error).subscribe(self),
+ disposable
+ )
+ }
+ }
+
+ func on(_ event: Event<SourceType>) {
+ switch event {
+ case let .next(value):
+ self.forwardOn(.next(value))
+ case let .error(error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final private class Using<SourceType, ResourceType: Disposable>: Producer<SourceType> {
+
+ typealias Element = SourceType
+
+ typealias ResourceFactory = () throws -> ResourceType
+ typealias ObservableFactory = (ResourceType) throws -> Observable<SourceType>
+
+ fileprivate let _resourceFactory: ResourceFactory
+ fileprivate let _observableFactory: ObservableFactory
+
+
+ init(resourceFactory: @escaping ResourceFactory, observableFactory: @escaping ObservableFactory) {
+ self._resourceFactory = resourceFactory
+ self._observableFactory = observableFactory
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
+ let sink = UsingSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// VirtualTimeConverterType.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 12/23/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+import Foundation
+
+/// Parametrization for virtual time used by `VirtualTimeScheduler`s.
+public protocol VirtualTimeConverterType {
+ /// Virtual time unit used that represents ticks of virtual clock.
+ associatedtype VirtualTimeUnit
+
+ /// Virtual time unit used to represent differences of virtual times.
+ associatedtype VirtualTimeIntervalUnit
+
+ /**
+ Converts virtual time to real time.
+
+ - parameter virtualTime: Virtual time to convert to `Date`.
+ - returns: `Date` corresponding to virtual time.
+ */
+ func convertFromVirtualTime(_ virtualTime: VirtualTimeUnit) -> RxTime
+
+ /**
+ Converts real time to virtual time.
+
+ - parameter time: `Date` to convert to virtual time.
+ - returns: Virtual time corresponding to `Date`.
+ */
+ func convertToVirtualTime(_ time: RxTime) -> VirtualTimeUnit
+
+ /**
+ Converts from virtual time interval to `NSTimeInterval`.
+
+ - parameter virtualTimeInterval: Virtual time interval to convert to `NSTimeInterval`.
+ - returns: `NSTimeInterval` corresponding to virtual time interval.
+ */
+ func convertFromVirtualTimeInterval(_ virtualTimeInterval: VirtualTimeIntervalUnit) -> TimeInterval
+
+ /**
+ Converts from `NSTimeInterval` to virtual time interval.
+
+ - parameter timeInterval: `NSTimeInterval` to convert to virtual time interval.
+ - returns: Virtual time interval corresponding to time interval.
+ */
+ func convertToVirtualTimeInterval(_ timeInterval: TimeInterval) -> VirtualTimeIntervalUnit
+
+ /**
+ Offsets virtual time by virtual time interval.
+
+ - parameter time: Virtual time.
+ - parameter offset: Virtual time interval.
+ - returns: Time corresponding to time offsetted by virtual time interval.
+ */
+ func offsetVirtualTime(_ time: VirtualTimeUnit, offset: VirtualTimeIntervalUnit) -> VirtualTimeUnit
+
+ /**
+ This is additional abstraction because `Date` is unfortunately not comparable.
+ Extending `Date` with `Comparable` would be too risky because of possible collisions with other libraries.
+ */
+ func compareVirtualTime(_ lhs: VirtualTimeUnit, _ rhs: VirtualTimeUnit) -> VirtualTimeComparison
+}
+
+/**
+ Virtual time comparison result.
+
+ This is additional abstraction because `Date` is unfortunately not comparable.
+ Extending `Date` with `Comparable` would be too risky because of possible collisions with other libraries.
+*/
+public enum VirtualTimeComparison {
+ /// lhs < rhs.
+ case lessThan
+ /// lhs == rhs.
+ case equal
+ /// lhs > rhs.
+ case greaterThan
+}
+
+extension VirtualTimeComparison {
+ /// lhs < rhs.
+ var lessThen: Bool {
+ return self == .lessThan
+ }
+
+ /// lhs > rhs
+ var greaterThan: Bool {
+ return self == .greaterThan
+ }
+
+ /// lhs == rhs
+ var equal: Bool {
+ return self == .equal
+ }
+}
--- /dev/null
+//
+// VirtualTimeScheduler.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 2/14/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+/// Base class for virtual time schedulers using a priority queue for scheduled items.
+open class VirtualTimeScheduler<Converter: VirtualTimeConverterType>
+ : SchedulerType {
+
+ public typealias VirtualTime = Converter.VirtualTimeUnit
+ public typealias VirtualTimeInterval = Converter.VirtualTimeIntervalUnit
+
+ private var _running : Bool
+
+ private var _clock: VirtualTime
+
+ private var _schedulerQueue : PriorityQueue<VirtualSchedulerItem<VirtualTime>>
+ private var _converter: Converter
+
+ private var _nextId = 0
+
+ /// - returns: Current time.
+ public var now: RxTime {
+ return self._converter.convertFromVirtualTime(self.clock)
+ }
+
+ /// - returns: Scheduler's absolute time clock value.
+ public var clock: VirtualTime {
+ return self._clock
+ }
+
+ /// Creates a new virtual time scheduler.
+ ///
+ /// - parameter initialClock: Initial value for the clock.
+ public init(initialClock: VirtualTime, converter: Converter) {
+ self._clock = initialClock
+ self._running = false
+ self._converter = converter
+ self._schedulerQueue = PriorityQueue(hasHigherPriority: {
+ switch converter.compareVirtualTime($0.time, $1.time) {
+ case .lessThan:
+ return true
+ case .equal:
+ return $0.id < $1.id
+ case .greaterThan:
+ return false
+ }
+ }, isEqual: { $0 === $1 })
+ #if TRACE_RESOURCES
+ _ = Resources.incrementTotal()
+ #endif
+ }
+
+ /**
+ Schedules an action to be executed immediately.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
+ return self.scheduleRelative(state, dueTime: .microseconds(0)) { a in
+ return action(a)
+ }
+ }
+
+ /**
+ Schedules an action to be executed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter dueTime: Relative time after which to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let time = self.now.addingDispatchInterval(dueTime)
+ let absoluteTime = self._converter.convertToVirtualTime(time)
+ let adjustedTime = self.adjustScheduledTime(absoluteTime)
+ return self.scheduleAbsoluteVirtual(state, time: adjustedTime, action: action)
+ }
+
+ /**
+ Schedules an action to be executed after relative time has passed.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter time: Absolute time when to execute the action. If this is less or equal then `now`, `now + 1` will be used.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func scheduleRelativeVirtual<StateType>(_ state: StateType, dueTime: VirtualTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
+ let time = self._converter.offsetVirtualTime(self.clock, offset: dueTime)
+ return self.scheduleAbsoluteVirtual(state, time: time, action: action)
+ }
+
+ /**
+ Schedules an action to be executed at absolute virtual time.
+
+ - parameter state: State passed to the action to be executed.
+ - parameter time: Absolute time when to execute the action.
+ - parameter action: Action to be executed.
+ - returns: The disposable object used to cancel the scheduled action (best effort).
+ */
+ public func scheduleAbsoluteVirtual<StateType>(_ state: StateType, time: VirtualTime, action: @escaping (StateType) -> Disposable) -> Disposable {
+ MainScheduler.ensureExecutingOnScheduler()
+
+ let compositeDisposable = CompositeDisposable()
+
+ let item = VirtualSchedulerItem(action: {
+ return action(state)
+ }, time: time, id: self._nextId)
+
+ self._nextId += 1
+
+ self._schedulerQueue.enqueue(item)
+
+ _ = compositeDisposable.insert(item)
+
+ return compositeDisposable
+ }
+
+ /// Adjusts time of scheduling before adding item to schedule queue.
+ open func adjustScheduledTime(_ time: VirtualTime) -> VirtualTime {
+ return time
+ }
+
+ /// Starts the virtual time scheduler.
+ public func start() {
+ MainScheduler.ensureExecutingOnScheduler()
+
+ if self._running {
+ return
+ }
+
+ self._running = true
+ repeat {
+ guard let next = self.findNext() else {
+ break
+ }
+
+ if self._converter.compareVirtualTime(next.time, self.clock).greaterThan {
+ self._clock = next.time
+ }
+
+ next.invoke()
+ self._schedulerQueue.remove(next)
+ } while self._running
+
+ self._running = false
+ }
+
+ func findNext() -> VirtualSchedulerItem<VirtualTime>? {
+ while let front = self._schedulerQueue.peek() {
+ if front.isDisposed {
+ self._schedulerQueue.remove(front)
+ continue
+ }
+
+ return front
+ }
+
+ return nil
+ }
+
+ /// Advances the scheduler's clock to the specified time, running all work till that point.
+ ///
+ /// - parameter virtualTime: Absolute time to advance the scheduler's clock to.
+ public func advanceTo(_ virtualTime: VirtualTime) {
+ MainScheduler.ensureExecutingOnScheduler()
+
+ if self._running {
+ fatalError("Scheduler is already running")
+ }
+
+ self._running = true
+ repeat {
+ guard let next = self.findNext() else {
+ break
+ }
+
+ if self._converter.compareVirtualTime(next.time, virtualTime).greaterThan {
+ break
+ }
+
+ if self._converter.compareVirtualTime(next.time, self.clock).greaterThan {
+ self._clock = next.time
+ }
+ next.invoke()
+ self._schedulerQueue.remove(next)
+ } while self._running
+
+ self._clock = virtualTime
+ self._running = false
+ }
+
+ /// Advances the scheduler's clock by the specified relative time.
+ public func sleep(_ virtualInterval: VirtualTimeInterval) {
+ MainScheduler.ensureExecutingOnScheduler()
+
+ let sleepTo = self._converter.offsetVirtualTime(self.clock, offset: virtualInterval)
+ if self._converter.compareVirtualTime(sleepTo, self.clock).lessThen {
+ fatalError("Can't sleep to past.")
+ }
+
+ self._clock = sleepTo
+ }
+
+ /// Stops the virtual time scheduler.
+ public func stop() {
+ MainScheduler.ensureExecutingOnScheduler()
+
+ self._running = false
+ }
+
+ #if TRACE_RESOURCES
+ deinit {
+ _ = Resources.decrementTotal()
+ }
+ #endif
+}
+
+// MARK: description
+
+extension VirtualTimeScheduler: CustomDebugStringConvertible {
+ /// A textual representation of `self`, suitable for debugging.
+ public var debugDescription: String {
+ return self._schedulerQueue.debugDescription
+ }
+}
+
+final class VirtualSchedulerItem<Time>
+ : Disposable {
+ typealias Action = () -> Disposable
+
+ let action: Action
+ let time: Time
+ let id: Int
+
+ var isDisposed: Bool {
+ return self.disposable.isDisposed
+ }
+
+ var disposable = SingleAssignmentDisposable()
+
+ init(action: @escaping Action, time: Time, id: Int) {
+ self.action = action
+ self.time = time
+ self.id = id
+ }
+
+ func invoke() {
+ self.disposable.setDisposable(self.action())
+ }
+
+ func dispose() {
+ self.disposable.dispose()
+ }
+}
+
+extension VirtualSchedulerItem
+ : CustomDebugStringConvertible {
+ var debugDescription: String {
+ return "\(self.time)"
+ }
+}
--- /dev/null
+//
+// Window.swift
+// RxSwift
+//
+// Created by Junior B. on 29/10/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Projects each element of an observable sequence into a window that is completed when either it’s full or a given amount of time has elapsed.
+
+ - seealso: [window operator on reactivex.io](http://reactivex.io/documentation/operators/window.html)
+
+ - parameter timeSpan: Maximum time length of a window.
+ - parameter count: Maximum element count of a window.
+ - parameter scheduler: Scheduler to run windowing timers on.
+ - returns: An observable sequence of windows (instances of `Observable`).
+ */
+ public func window(timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType)
+ -> Observable<Observable<Element>> {
+ return WindowTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler)
+ }
+}
+
+final private class WindowTimeCountSink<Element, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType where Observer.Element == Observable<Element> {
+ typealias Parent = WindowTimeCount<Element>
+
+ private let _parent: Parent
+
+ let _lock = RecursiveLock()
+
+ private var _subject = PublishSubject<Element>()
+ private var _count = 0
+ private var _windowId = 0
+
+ private let _timerD = SerialDisposable()
+ private let _refCountDisposable: RefCountDisposable
+ private let _groupDisposable = CompositeDisposable()
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+
+ _ = self._groupDisposable.insert(self._timerD)
+
+ self._refCountDisposable = RefCountDisposable(disposable: self._groupDisposable)
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+
+ self.forwardOn(.next(AddRef(source: self._subject, refCount: self._refCountDisposable).asObservable()))
+ self.createTimer(self._windowId)
+
+ _ = self._groupDisposable.insert(self._parent._source.subscribe(self))
+ return self._refCountDisposable
+ }
+
+ func startNewWindowAndCompleteCurrentOne() {
+ self._subject.on(.completed)
+ self._subject = PublishSubject<Element>()
+
+ self.forwardOn(.next(AddRef(source: self._subject, refCount: self._refCountDisposable).asObservable()))
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ var newWindow = false
+ var newId = 0
+
+ switch event {
+ case .next(let element):
+ self._subject.on(.next(element))
+
+ do {
+ _ = try incrementChecked(&self._count)
+ } catch let e {
+ self._subject.on(.error(e as Swift.Error))
+ self.dispose()
+ }
+
+ if self._count == self._parent._count {
+ newWindow = true
+ self._count = 0
+ self._windowId += 1
+ newId = self._windowId
+ self.startNewWindowAndCompleteCurrentOne()
+ }
+
+ case .error(let error):
+ self._subject.on(.error(error))
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ self._subject.on(.completed)
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+
+ if newWindow {
+ self.createTimer(newId)
+ }
+ }
+
+ func createTimer(_ windowId: Int) {
+ if self._timerD.isDisposed {
+ return
+ }
+
+ if self._windowId != windowId {
+ return
+ }
+
+ let nextTimer = SingleAssignmentDisposable()
+
+ self._timerD.disposable = nextTimer
+
+ let scheduledRelative = self._parent._scheduler.scheduleRelative(windowId, dueTime: self._parent._timeSpan) { previousWindowId in
+
+ var newId = 0
+
+ self._lock.performLocked {
+ if previousWindowId != self._windowId {
+ return
+ }
+
+ self._count = 0
+ self._windowId = self._windowId &+ 1
+ newId = self._windowId
+ self.startNewWindowAndCompleteCurrentOne()
+ }
+
+ self.createTimer(newId)
+
+ return Disposables.create()
+ }
+
+ nextTimer.setDisposable(scheduledRelative)
+ }
+}
+
+final private class WindowTimeCount<Element>: Producer<Observable<Element>> {
+ fileprivate let _timeSpan: RxTimeInterval
+ fileprivate let _count: Int
+ fileprivate let _scheduler: SchedulerType
+ fileprivate let _source: Observable<Element>
+
+ init(source: Observable<Element>, timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) {
+ self._source = source
+ self._timeSpan = timeSpan
+ self._count = count
+ self._scheduler = scheduler
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Observable<Element> {
+ let sink = WindowTimeCountSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// WithLatestFrom.swift
+// RxSwift
+//
+// Created by Yury Korolev on 10/19/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+
+ /**
+ Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter second: Second observable source.
+ - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any.
+ - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function.
+ */
+ public func withLatestFrom<Source: ObservableConvertibleType, ResultType>(_ second: Source, resultSelector: @escaping (Element, Source.Element) throws -> ResultType) -> Observable<ResultType> {
+ return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector)
+ }
+
+ /**
+ Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emits an element.
+
+ - seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
+
+ - parameter second: Second observable source.
+ - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function.
+ */
+ public func withLatestFrom<Source: ObservableConvertibleType>(_ second: Source) -> Observable<Source.Element> {
+ return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: { $1 })
+ }
+}
+
+final private class WithLatestFromSink<FirstType, SecondType, Observer: ObserverType>
+ : Sink<Observer>
+ , ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias ResultType = Observer.Element
+ typealias Parent = WithLatestFrom<FirstType, SecondType, ResultType>
+ typealias Element = FirstType
+
+ private let _parent: Parent
+
+ fileprivate var _lock = RecursiveLock()
+ fileprivate var _latest: SecondType?
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func run() -> Disposable {
+ let sndSubscription = SingleAssignmentDisposable()
+ let sndO = WithLatestFromSecond(parent: self, disposable: sndSubscription)
+
+ sndSubscription.setDisposable(self._parent._second.subscribe(sndO))
+ let fstSubscription = self._parent._first.subscribe(self)
+
+ return Disposables.create(fstSubscription, sndSubscription)
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case let .next(value):
+ guard let latest = self._latest else { return }
+ do {
+ let res = try self._parent._resultSelector(value, latest)
+
+ self.forwardOn(.next(res))
+ } catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ case .completed:
+ self.forwardOn(.completed)
+ self.dispose()
+ case let .error(error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+ }
+}
+
+final private class WithLatestFromSecond<FirstType, SecondType, Observer: ObserverType>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+
+ typealias ResultType = Observer.Element
+ typealias Parent = WithLatestFromSink<FirstType, SecondType, Observer>
+ typealias Element = SecondType
+
+ private let _parent: Parent
+ private let _disposable: Disposable
+
+ var _lock: RecursiveLock {
+ return self._parent._lock
+ }
+
+ init(parent: Parent, disposable: Disposable) {
+ self._parent = parent
+ self._disposable = disposable
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ switch event {
+ case let .next(value):
+ self._parent._latest = value
+ case .completed:
+ self._disposable.dispose()
+ case let .error(error):
+ self._parent.forwardOn(.error(error))
+ self._parent.dispose()
+ }
+ }
+}
+
+final private class WithLatestFrom<FirstType, SecondType, ResultType>: Producer<ResultType> {
+ typealias ResultSelector = (FirstType, SecondType) throws -> ResultType
+
+ fileprivate let _first: Observable<FirstType>
+ fileprivate let _second: Observable<SecondType>
+ fileprivate let _resultSelector: ResultSelector
+
+ init(first: Observable<FirstType>, second: Observable<SecondType>, resultSelector: @escaping ResultSelector) {
+ self._first = first
+ self._second = second
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == ResultType {
+ let sink = WithLatestFromSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+//
+// Zip+Collection.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 8/30/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<Collection: Swift.Collection>(_ collection: Collection, resultSelector: @escaping ([Collection.Element.Element]) throws -> Element) -> Observable<Element>
+ where Collection.Element: ObservableType {
+ return ZipCollectionType(sources: collection, resultSelector: resultSelector)
+ }
+
+ /**
+ Merges the specified observable sequences into one observable sequence whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<Collection: Swift.Collection>(_ collection: Collection) -> Observable<[Element]>
+ where Collection.Element: ObservableType, Collection.Element.Element == Element {
+ return ZipCollectionType(sources: collection, resultSelector: { $0 })
+ }
+
+}
+
+final private class ZipCollectionTypeSink<Collection: Swift.Collection, Observer: ObserverType>
+ : Sink<Observer> where Collection.Element: ObservableConvertibleType {
+ typealias Result = Observer.Element
+ typealias Parent = ZipCollectionType<Collection, Result>
+ typealias SourceElement = Collection.Element.Element
+
+ private let _parent: Parent
+
+ private let _lock = RecursiveLock()
+
+ // state
+ private var _numberOfValues = 0
+ private var _values: [Queue<SourceElement>]
+ private var _isDone: [Bool]
+ private var _numberOfDone = 0
+ private var _subscriptions: [SingleAssignmentDisposable]
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ self._values = [Queue<SourceElement>](repeating: Queue(capacity: 4), count: parent.count)
+ self._isDone = [Bool](repeating: false, count: parent.count)
+ self._subscriptions = [SingleAssignmentDisposable]()
+ self._subscriptions.reserveCapacity(parent.count)
+
+ for _ in 0 ..< parent.count {
+ self._subscriptions.append(SingleAssignmentDisposable())
+ }
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func on(_ event: Event<SourceElement>, atIndex: Int) {
+ self._lock.lock(); defer { self._lock.unlock() } // {
+ switch event {
+ case .next(let element):
+ self._values[atIndex].enqueue(element)
+
+ if self._values[atIndex].count == 1 {
+ self._numberOfValues += 1
+ }
+
+ if self._numberOfValues < self._parent.count {
+ if self._numberOfDone == self._parent.count - 1 {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ return
+ }
+
+ do {
+ var arguments = [SourceElement]()
+ arguments.reserveCapacity(self._parent.count)
+
+ // recalculate number of values
+ self._numberOfValues = 0
+
+ for i in 0 ..< self._values.count {
+ arguments.append(self._values[i].dequeue()!)
+ if !self._values[i].isEmpty {
+ self._numberOfValues += 1
+ }
+ }
+
+ let result = try self._parent.resultSelector(arguments)
+ self.forwardOn(.next(result))
+ }
+ catch let error {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+
+ case .error(let error):
+ self.forwardOn(.error(error))
+ self.dispose()
+ case .completed:
+ if self._isDone[atIndex] {
+ return
+ }
+
+ self._isDone[atIndex] = true
+ self._numberOfDone += 1
+
+ if self._numberOfDone == self._parent.count {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ else {
+ self._subscriptions[atIndex].dispose()
+ }
+ }
+ // }
+ }
+
+ func run() -> Disposable {
+ var j = 0
+ for i in self._parent.sources {
+ let index = j
+ let source = i.asObservable()
+
+ let disposable = source.subscribe(AnyObserver { event in
+ self.on(event, atIndex: index)
+ })
+ self._subscriptions[j].setDisposable(disposable)
+ j += 1
+ }
+
+ if self._parent.sources.isEmpty {
+ self.forwardOn(.completed)
+ }
+
+ return Disposables.create(_subscriptions)
+ }
+}
+
+final private class ZipCollectionType<Collection: Swift.Collection, Result>: Producer<Result> where Collection.Element: ObservableConvertibleType {
+ typealias ResultSelector = ([Collection.Element.Element]) throws -> Result
+
+ let sources: Collection
+ let resultSelector: ResultSelector
+ let count: Int
+
+ init(sources: Collection, resultSelector: @escaping ResultSelector) {
+ self.sources = sources
+ self.resultSelector = resultSelector
+ self.count = self.sources.count
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipCollectionTypeSink(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
--- /dev/null
+// This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project
+//
+// Zip+arity.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/23/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+
+
+// 2
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType>
+ (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip2(
+ source1: source1.asObservable(), source2: source2.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType>
+ (_ source1: O1, _ source2: O2)
+ -> Observable<(O1.Element, O2.Element)> {
+ return Zip2(
+ source1: source1.asObservable(), source2: source2.asObservable(),
+ resultSelector: { ($0, $1) }
+ )
+ }
+}
+
+final class ZipSink2_<E1, E2, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip2<E1, E2, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 2, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+
+ return Disposables.create([
+ subscription1,
+ subscription2
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!)
+ }
+}
+
+final class Zip2<E1, E2, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink2_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 3
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip3(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3)
+ -> Observable<(O1.Element, O2.Element, O3.Element)> {
+ return Zip3(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(),
+ resultSelector: { ($0, $1, $2) }
+ )
+ }
+}
+
+final class ZipSink3_<E1, E2, E3, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip3<E1, E2, E3, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 3, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!)
+ }
+}
+
+final class Zip3<E1, E2, E3, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink3_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 4
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip4(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element)> {
+ return Zip4(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(),
+ resultSelector: { ($0, $1, $2, $3) }
+ )
+ }
+}
+
+final class ZipSink4_<E1, E2, E3, E4, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip4<E1, E2, E3, E4, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+ var _values4: Queue<E4> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 4, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+ case 3: return !self._values4.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+ let observer4 = ZipObserver(lock: self._lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent.source4.subscribe(observer4))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!, self._values4.dequeue()!)
+ }
+}
+
+final class Zip4<E1, E2, E3, E4, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+ let source4: Observable<E4>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+ self.source4 = source4
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink4_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 5
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip5(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element)> {
+ return Zip5(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4) }
+ )
+ }
+}
+
+final class ZipSink5_<E1, E2, E3, E4, E5, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip5<E1, E2, E3, E4, E5, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+ var _values4: Queue<E4> = Queue(capacity: 2)
+ var _values5: Queue<E5> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 5, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+ case 3: return !self._values4.isEmpty
+ case 4: return !self._values5.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+ let observer4 = ZipObserver(lock: self._lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4)
+ let observer5 = ZipObserver(lock: self._lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent.source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent.source5.subscribe(observer5))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!, self._values4.dequeue()!, self._values5.dequeue()!)
+ }
+}
+
+final class Zip5<E1, E2, E3, E4, E5, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+ let source4: Observable<E4>
+ let source5: Observable<E5>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+ self.source4 = source4
+ self.source5 = source5
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink5_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 6
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip6(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element)> {
+ return Zip6(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5) }
+ )
+ }
+}
+
+final class ZipSink6_<E1, E2, E3, E4, E5, E6, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip6<E1, E2, E3, E4, E5, E6, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+ var _values4: Queue<E4> = Queue(capacity: 2)
+ var _values5: Queue<E5> = Queue(capacity: 2)
+ var _values6: Queue<E6> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 6, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+ case 3: return !self._values4.isEmpty
+ case 4: return !self._values5.isEmpty
+ case 5: return !self._values6.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+ let observer4 = ZipObserver(lock: self._lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4)
+ let observer5 = ZipObserver(lock: self._lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5)
+ let observer6 = ZipObserver(lock: self._lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent.source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent.source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent.source6.subscribe(observer6))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!, self._values4.dequeue()!, self._values5.dequeue()!, self._values6.dequeue()!)
+ }
+}
+
+final class Zip6<E1, E2, E3, E4, E5, E6, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+ let source4: Observable<E4>
+ let source5: Observable<E5>
+ let source6: Observable<E6>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+ self.source4 = source4
+ self.source5 = source5
+ self.source6 = source6
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink6_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 7
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip7(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element)> {
+ return Zip7(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5, $6) }
+ )
+ }
+}
+
+final class ZipSink7_<E1, E2, E3, E4, E5, E6, E7, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip7<E1, E2, E3, E4, E5, E6, E7, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+ var _values4: Queue<E4> = Queue(capacity: 2)
+ var _values5: Queue<E5> = Queue(capacity: 2)
+ var _values6: Queue<E6> = Queue(capacity: 2)
+ var _values7: Queue<E7> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 7, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+ case 3: return !self._values4.isEmpty
+ case 4: return !self._values5.isEmpty
+ case 5: return !self._values6.isEmpty
+ case 6: return !self._values7.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+ let subscription7 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+ let observer4 = ZipObserver(lock: self._lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4)
+ let observer5 = ZipObserver(lock: self._lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5)
+ let observer6 = ZipObserver(lock: self._lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6)
+ let observer7 = ZipObserver(lock: self._lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent.source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent.source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent.source6.subscribe(observer6))
+ subscription7.setDisposable(self._parent.source7.subscribe(observer7))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6,
+ subscription7
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!, self._values4.dequeue()!, self._values5.dequeue()!, self._values6.dequeue()!, self._values7.dequeue()!)
+ }
+}
+
+final class Zip7<E1, E2, E3, E4, E5, E6, E7, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+ let source4: Observable<E4>
+ let source5: Observable<E5>
+ let source6: Observable<E6>
+ let source7: Observable<E7>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+ self.source4 = source4
+ self.source5 = source5
+ self.source6 = source6
+ self.source7 = source7
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink7_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
+
+// 8
+
+extension ObservableType {
+ /**
+ Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
+ - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType, O8: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element)
+ -> Observable<Element> {
+ return Zip8(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(),
+ resultSelector: resultSelector
+ )
+ }
+}
+
+extension ObservableType where Element == Any {
+ /**
+ Merges the specified observable sequences into one observable sequence of tuples whenever all of the observable sequences have produced an element at a corresponding index.
+
+ - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
+
+ - returns: An observable sequence containing the result of combining elements of the sources.
+ */
+ public static func zip<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType, O8: ObservableType>
+ (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8)
+ -> Observable<(O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element)> {
+ return Zip8(
+ source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(),
+ resultSelector: { ($0, $1, $2, $3, $4, $5, $6, $7) }
+ )
+ }
+}
+
+final class ZipSink8_<E1, E2, E3, E4, E5, E6, E7, E8, Observer: ObserverType> : ZipSink<Observer> {
+ typealias Result = Observer.Element
+ typealias Parent = Zip8<E1, E2, E3, E4, E5, E6, E7, E8, Result>
+
+ let _parent: Parent
+
+ var _values1: Queue<E1> = Queue(capacity: 2)
+ var _values2: Queue<E2> = Queue(capacity: 2)
+ var _values3: Queue<E3> = Queue(capacity: 2)
+ var _values4: Queue<E4> = Queue(capacity: 2)
+ var _values5: Queue<E5> = Queue(capacity: 2)
+ var _values6: Queue<E6> = Queue(capacity: 2)
+ var _values7: Queue<E7> = Queue(capacity: 2)
+ var _values8: Queue<E8> = Queue(capacity: 2)
+
+ init(parent: Parent, observer: Observer, cancel: Cancelable) {
+ self._parent = parent
+ super.init(arity: 8, observer: observer, cancel: cancel)
+ }
+
+ override func hasElements(_ index: Int) -> Bool {
+ switch index {
+ case 0: return !self._values1.isEmpty
+ case 1: return !self._values2.isEmpty
+ case 2: return !self._values3.isEmpty
+ case 3: return !self._values4.isEmpty
+ case 4: return !self._values5.isEmpty
+ case 5: return !self._values6.isEmpty
+ case 6: return !self._values7.isEmpty
+ case 7: return !self._values8.isEmpty
+
+ default:
+ rxFatalError("Unhandled case (Function)")
+ }
+ }
+
+ func run() -> Disposable {
+ let subscription1 = SingleAssignmentDisposable()
+ let subscription2 = SingleAssignmentDisposable()
+ let subscription3 = SingleAssignmentDisposable()
+ let subscription4 = SingleAssignmentDisposable()
+ let subscription5 = SingleAssignmentDisposable()
+ let subscription6 = SingleAssignmentDisposable()
+ let subscription7 = SingleAssignmentDisposable()
+ let subscription8 = SingleAssignmentDisposable()
+
+ let observer1 = ZipObserver(lock: self._lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1)
+ let observer2 = ZipObserver(lock: self._lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2)
+ let observer3 = ZipObserver(lock: self._lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3)
+ let observer4 = ZipObserver(lock: self._lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4)
+ let observer5 = ZipObserver(lock: self._lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5)
+ let observer6 = ZipObserver(lock: self._lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6)
+ let observer7 = ZipObserver(lock: self._lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7)
+ let observer8 = ZipObserver(lock: self._lock, parent: self, index: 7, setNextValue: { self._values8.enqueue($0) }, this: subscription8)
+
+ subscription1.setDisposable(self._parent.source1.subscribe(observer1))
+ subscription2.setDisposable(self._parent.source2.subscribe(observer2))
+ subscription3.setDisposable(self._parent.source3.subscribe(observer3))
+ subscription4.setDisposable(self._parent.source4.subscribe(observer4))
+ subscription5.setDisposable(self._parent.source5.subscribe(observer5))
+ subscription6.setDisposable(self._parent.source6.subscribe(observer6))
+ subscription7.setDisposable(self._parent.source7.subscribe(observer7))
+ subscription8.setDisposable(self._parent.source8.subscribe(observer8))
+
+ return Disposables.create([
+ subscription1,
+ subscription2,
+ subscription3,
+ subscription4,
+ subscription5,
+ subscription6,
+ subscription7,
+ subscription8
+ ])
+ }
+
+ override func getResult() throws -> Result {
+ return try self._parent._resultSelector(self._values1.dequeue()!, self._values2.dequeue()!, self._values3.dequeue()!, self._values4.dequeue()!, self._values5.dequeue()!, self._values6.dequeue()!, self._values7.dequeue()!, self._values8.dequeue()!)
+ }
+}
+
+final class Zip8<E1, E2, E3, E4, E5, E6, E7, E8, Result> : Producer<Result> {
+ typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Result
+
+ let source1: Observable<E1>
+ let source2: Observable<E2>
+ let source3: Observable<E3>
+ let source4: Observable<E4>
+ let source5: Observable<E5>
+ let source6: Observable<E6>
+ let source7: Observable<E7>
+ let source8: Observable<E8>
+
+ let _resultSelector: ResultSelector
+
+ init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, source8: Observable<E8>, resultSelector: @escaping ResultSelector) {
+ self.source1 = source1
+ self.source2 = source2
+ self.source3 = source3
+ self.source4 = source4
+ self.source5 = source5
+ self.source6 = source6
+ self.source7 = source7
+ self.source8 = source8
+
+ self._resultSelector = resultSelector
+ }
+
+ override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Result {
+ let sink = ZipSink8_(parent: self, observer: observer, cancel: cancel)
+ let subscription = sink.run()
+ return (sink: sink, subscription: subscription)
+ }
+}
+
+
--- /dev/null
+//
+// Zip.swift
+// RxSwift
+//
+// Created by Krunoslav Zaher on 5/23/15.
+// Copyright © 2015 Krunoslav Zaher. All rights reserved.
+//
+
+protocol ZipSinkProtocol : class
+{
+ func next(_ index: Int)
+ func fail(_ error: Swift.Error)
+ func done(_ index: Int)
+}
+
+class ZipSink<Observer: ObserverType> : Sink<Observer>, ZipSinkProtocol {
+ typealias Element = Observer.Element
+
+ let _arity: Int
+
+ let _lock = RecursiveLock()
+
+ // state
+ private var _isDone: [Bool]
+
+ init(arity: Int, observer: Observer, cancel: Cancelable) {
+ self._isDone = [Bool](repeating: false, count: arity)
+ self._arity = arity
+
+ super.init(observer: observer, cancel: cancel)
+ }
+
+ func getResult() throws -> Element {
+ rxAbstractMethod()
+ }
+
+ func hasElements(_ index: Int) -> Bool {
+ rxAbstractMethod()
+ }
+
+ func next(_ index: Int) {
+ var hasValueAll = true
+
+ for i in 0 ..< self._arity {
+ if !self.hasElements(i) {
+ hasValueAll = false
+ break
+ }
+ }
+
+ if hasValueAll {
+ do {
+ let result = try self.getResult()
+ self.forwardOn(.next(result))
+ }
+ catch let e {
+ self.forwardOn(.error(e))
+ self.dispose()
+ }
+ }
+ }
+
+ func fail(_ error: Swift.Error) {
+ self.forwardOn(.error(error))
+ self.dispose()
+ }
+
+ func done(_ index: Int) {
+ self._isDone[index] = true
+
+ var allDone = true
+
+ for done in self._isDone where !done {
+ allDone = false
+ break
+ }
+
+ if allDone {
+ self.forwardOn(.completed)
+ self.dispose()
+ }
+ }
+}
+
+final class ZipObserver<Element>
+ : ObserverType
+ , LockOwnerType
+ , SynchronizedOnType {
+ typealias ValueSetter = (Element) -> Void
+
+ private var _parent: ZipSinkProtocol?
+
+ let _lock: RecursiveLock
+
+ // state
+ private let _index: Int
+ private let _this: Disposable
+ private let _setNextValue: ValueSetter
+
+ init(lock: RecursiveLock, parent: ZipSinkProtocol, index: Int, setNextValue: @escaping ValueSetter, this: Disposable) {
+ self._lock = lock
+ self._parent = parent
+ self._index = index
+ self._this = this
+ self._setNextValue = setNextValue
+ }
+
+ func on(_ event: Event<Element>) {
+ self.synchronizedOn(event)
+ }
+
+ func _synchronized_on(_ event: Event<Element>) {
+ if self._parent != nil {
+ switch event {
+ case .next:
+ break
+ case .error:
+ self._this.dispose()
+ case .completed:
+ self._this.dispose()
+ }
+ }
+
+ if let parent = self._parent {
+ switch event {
+ case .next(let value):
+ self._setNextValue(value)
+ parent.next(self._index)
+ case .error(let error):
+ parent.fail(error)
+ case .completed:
+ parent.done(self._index)
+ }
+ }
+ }
+}
--- /dev/null
+//
+// AES256CBC.swift
+// AES256CBC https://github.com/SwiftyBeaver/AES256CBC
+//
+// Created by Sebastian Kreutzberger on 2/9/16.
+// Copyright © 2016 SwiftyBeaver. All rights reserved.
+//
+
+import Foundation
+
+/// cross-platform random numbers generator
+private struct Random {
+ #if os(Linux)
+ static var initialized = false
+ #endif
+
+ public static func generate(_ upperBound: Int) -> Int {
+ #if os(Linux)
+ if !Random.initialized {
+ srandom(UInt32(time(nil)))
+ Random.initialized = true
+ }
+ return Int(random() % upperBound)
+ #else
+ return Int(arc4random_uniform(UInt32(upperBound)))
+ #endif
+ }
+}
+
+final class AES256CBC {
+
+ /// returns optional encrypted string via AES-256CBC
+ /// automatically generates and puts a random IV at first 16 chars
+ /// the password must be exactly 32 chars long for AES-256
+ class func encryptString(_ str: String, password: String) -> String? {
+ if !str.isEmpty && password.length == 32 {
+ let iv = randomText(16)
+ let key = password
+
+ guard let encryptedString = try? aesEncrypt(str, key: key, iv: iv) else {
+ print("an error occured while encrypting")
+ return nil
+ }
+ return iv + encryptedString
+ }
+ return nil
+ }
+
+ /// returns optional decrypted string via AES-256CBC
+ /// IV need to be at first 16 chars, password must be 32 chars long
+ class func decryptString(_ str: String, password: String) -> String? {
+ if str.length > 16 && password.length == 32 {
+ // get AES initialization vector from first 16 chars
+ #if swift(>=4.0)
+ let iv = String(str[..<str.index(str.startIndex, offsetBy: 16)])
+ #else
+ let iv = str.substring(to: str.index(str.startIndex, offsetBy: 16))
+ #endif
+
+ let encryptedString = str.replacingOccurrences(of: iv, with: "",
+ options: String.CompareOptions.literal, range: nil) // remove IV
+
+ guard let decryptedString = try? aesDecrypt(encryptedString, key: password, iv: iv) else {
+ print("an error occured while decrypting")
+ return nil
+ }
+ return decryptedString
+ }
+ return nil
+ }
+
+ /// returns random string (uppercase & lowercase, no spaces) of 32 characters length
+ /// which can be used as SHA-256 compatbile password
+ class func generatePassword() -> String {
+ return randomText(32)
+ }
+
+ /// returns random text of a defined length.
+ /// Optional bool parameter justLowerCase to just generate random lowercase text and
+ /// whitespace to exclude the whitespace character
+ class func randomText(_ length: Int, justLowerCase: Bool = false, whitespace: Bool = false) -> String {
+ var chars = [UInt8]()
+
+ while chars.count < length {
+ let char = CharType.random(justLowerCase, whitespace).randomCharacter()
+ if char == 32 && (chars.last ?? 0) == char {
+ // do not allow two consecutive spaces
+ continue
+ }
+ chars.append(char)
+ }
+ return String(bytes: chars, encoding: .ascii)!
+ }
+
+ /// Used for random text generation
+ fileprivate enum CharType: Int {
+ case LowerCase, UpperCase, Digit, Space
+
+ func randomCharacter() -> UInt8 {
+ switch self {
+ case .LowerCase:
+ return UInt8(Random.generate(26)) + 97
+ case .UpperCase:
+ return UInt8(Random.generate(26)) + 65
+ case .Digit:
+ return UInt8(Random.generate(10)) + 48
+ case .Space:
+ return 32
+ }
+ }
+
+ static func random(_ justLowerCase: Bool, _ allowWhitespace: Bool) -> CharType {
+ if justLowerCase {
+ return .LowerCase
+ } else {
+ return CharType(rawValue: Int(Random.generate(allowWhitespace ? 4 : 3)))!
+ }
+ }
+ }
+
+ /// returns encrypted string, IV must be 16 chars long
+ fileprivate class func aesEncrypt(_ str: String, key: String, iv: String) throws -> String {
+ let keyData = key.data(using: String.Encoding.utf8)!
+ let ivData = iv.data(using: String.Encoding.utf8)!
+ let data = str.data(using: String.Encoding.utf8)!
+ #if swift(>=5)
+ let enc = try Data(AESCipher(key: keyData.bytes,
+ iv: ivData.bytes).encrypt(bytes: data.bytes))
+ #else
+ let enc = try Data(bytes: AESCipher(key: keyData.bytes,
+ iv: ivData.bytes).encrypt(bytes: data.bytes))
+ #endif
+ // Swift 3.1.x has a bug with base64encoding under Linux, so we are using our own
+ #if os(Linux)
+ return Base64.encode([UInt8](enc))
+ #else
+ return enc.base64EncodedString(options: [])
+ #endif
+ }
+
+ /// returns decrypted string, IV must be 16 chars long
+ fileprivate class func aesDecrypt(_ str: String, key: String, iv: String) throws -> String {
+ let keyData = key.data(using: String.Encoding.utf8)!
+ let ivData = iv.data(using: String.Encoding.utf8)!
+ let data = Data(base64Encoded: str)!
+ #if swift(>=5)
+ let dec = try Data(AESCipher(key: keyData.bytes,
+ iv: ivData.bytes).decrypt(bytes: data.bytes))
+ #else
+ let dec = try Data(bytes: AESCipher(key: keyData.bytes,
+ iv: ivData.bytes).decrypt(bytes: data.bytes))
+ #endif
+ guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
+ throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
+ }
+ return decryptStr
+ }
+
+}
+
+// swiftlint:disable line_length
+// The following is an altered source version that only includes AES-CBC. The original software can be found at:
+// https://github.com/krzyzanowskim/CryptoSwift
+//
+// This is the original copyright notice:
+//
+// Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
+// This software is provided 'as-is', without any express or implied warranty.
+//
+// In no event will the authors be held liable for any damages arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+// - This notice may not be removed or altered from any source or binary distribution.
+//
+// swiftlint:enable line_length
+
+// MARK: - AESCipher
+
+private typealias Key = Array<UInt8>
+
+final private class AESCipher {
+
+ enum Error: Swift.Error {
+ case blockSizeExceeded
+ case dataPaddingRequired
+ case invalidKeyOrInitializationVector
+ case invalidInitializationVector
+ }
+
+ enum Variant: Int {
+ case aes128 = 1, aes192, aes256
+
+ var Nk: Int { // Nk words
+ return [4, 6, 8][self.rawValue - 1]
+ }
+
+ var Nb: Int { // Nb words
+ return 4
+ }
+
+ var Nr: Int { // Nr
+ return Nk + 6
+ }
+ }
+
+ static let blockSize: Int = 16 // 128 /8
+
+ var variant: Variant {
+ switch self.key.count * 8 {
+ case 128:
+ return .aes128
+ case 192:
+ return .aes192
+ case 256:
+ return .aes256
+ default:
+ preconditionFailure("Unknown AES variant for given key.")
+ }
+ }
+ private let key: [UInt8]
+ private let iv: [UInt8]?
+ private let blockMode = CBCBlockMode()
+ private lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
+ private lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
+
+ private lazy var sBoxes:(sBox: [UInt32], invSBox: [UInt32]) = self.calculateSBox()
+ private lazy var sBox: [UInt32] = self.sBoxes.sBox
+ private lazy var sBoxInv: [UInt32] = self.sBoxes.invSBox
+
+ // Parameters for Linear Congruence Generators
+ private let Rcon: [UInt8] = [
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
+ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
+ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
+ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
+ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
+ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
+ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
+ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
+ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
+ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
+ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
+ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
+ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]
+
+ // swiftlint:disable line_length
+ fileprivate let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
+ fileprivate let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
+ fileprivate let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
+ fileprivate let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
+ fileprivate let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
+ fileprivate let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
+ fileprivate let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
+ fileprivate let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
+ fileprivate var U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
+ fileprivate var U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
+ fileprivate var U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
+ fileprivate var U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
+ // swiftlint:enable line_length
+
+ init(key: [UInt8], iv: [UInt8]) throws {
+ self.key = key
+ self.iv = iv
+ }
+
+ convenience init(key: [UInt8]) throws {
+ // default IV is all 0x00...
+ let defaultIV = [UInt8](repeating: 0, count: AESCipher.blockSize)
+ try self.init(key: key, iv: defaultIV)
+ }
+
+ /**
+ Encrypt message. If padding is necessary, then Cookies_PKCS7 padding is added and needs to be removed after decryption.
+
+ - parameter message: Plaintext data
+ - parameter padding: Optional padding
+
+ - returns: Encrypted data
+ */
+
+ func encrypt(bytes: [UInt8]) throws -> [UInt8] {
+ let finalBytes = Cookies_PKCS7().add(bytes: bytes, blockSize: AESCipher.blockSize)
+ let blocks = finalBytes.chunks(size: AESCipher.blockSize)
+ return try blockMode.encrypt(blocks: blocks, iv: self.iv, cipherOperation: encrypt)
+ }
+
+ private func encrypt(block: [UInt8]) -> [UInt8]? {
+ let rounds = self.variant.Nr
+ let rk = self.expandedKey
+ var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
+
+ var t = [UInt32](repeating: 0, count: 4)
+
+ for r in 0..<rounds - 1 {
+ t[0] = b[0] ^ rk[r][0]
+ t[1] = b[1] ^ rk[r][1]
+ t[2] = b[2] ^ rk[r][2]
+ t[3] = b[3] ^ rk[r][3]
+
+ let lb00 = T0[Int(t[0] & 0xFF)]
+ let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
+ let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
+ let lb03 = T3[Int(t[3] >> 24)]
+ b[0] = lb00 ^ lb01 ^ lb02 ^ lb03
+
+ let lb10 = T0[Int(t[1] & 0xFF)]
+ let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
+ let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
+ let lb13 = T3[Int(t[0] >> 24)]
+ b[1] = lb10 ^ lb11 ^ lb12 ^ lb13
+
+ let lb20 = T0[Int(t[2] & 0xFF)]
+ let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
+ let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
+ let lb23 = T3[Int(t[1] >> 24)]
+ b[2] = lb20 ^ lb21 ^ lb22 ^ lb23
+
+ let lb30 = T0[Int(t[3] & 0xFF)]
+ let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
+ let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
+ let lb33 = T3[Int(t[2] >> 24)]
+ b[3] = lb30 ^ lb31 ^ lb32 ^ lb33
+ }
+
+ // last round
+ let r = rounds - 1
+
+ t[0] = b[0] ^ rk[r][0]
+ t[1] = b[1] ^ rk[r][1]
+ t[2] = b[2] ^ rk[r][2]
+ t[3] = b[3] ^ rk[r][3]
+
+ // rounds
+ b[0] = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
+ b[1] = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
+ b[2] = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
+ b[3] = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
+
+ var out = [UInt8]()
+ out.reserveCapacity(b.count * 4)
+ for num in b {
+ out.append(UInt8(num & 0xFF))
+ out.append(UInt8((num >> 8) & 0xFF))
+ out.append(UInt8((num >> 16) & 0xFF))
+ out.append(UInt8((num >> 24) & 0xFF))
+ }
+
+ return out
+ }
+
+ func decrypt(bytes: [UInt8]) throws -> [UInt8] {
+ if (bytes.count % AESCipher.blockSize) != 0 {
+ throw Error.blockSizeExceeded
+ }
+
+ let blocks = bytes.chunks(size: AESCipher.blockSize)
+ return try Cookies_PKCS7().remove(bytes: blockMode.decrypt(blocks: blocks, iv: self.iv, cipherOperation: decrypt), blockSize: AESCipher.blockSize)
+ }
+
+ private func decrypt(block: [UInt8]) -> [UInt8]? {
+ let rounds = self.variant.Nr
+ let rk = expandedKeyInv
+ var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
+
+ var t = [UInt32](repeating: 0, count: 4)
+
+ for r in (2...rounds).reversed() {
+ t[0] = b[0] ^ rk[r][0]
+ t[1] = b[1] ^ rk[r][1]
+ t[2] = b[2] ^ rk[r][2]
+ t[3] = b[3] ^ rk[r][3]
+
+ let b00 = T0_INV[Int(t[0] & 0xFF)]
+ let b01 = T1_INV[Int((t[3] >> 8) & 0xFF)]
+ let b02 = T2_INV[Int((t[2] >> 16) & 0xFF)]
+ let b03 = T3_INV[Int(t[1] >> 24)]
+ b[0] = b00 ^ b01 ^ b02 ^ b03
+
+ let b10 = T0_INV[Int(t[1] & 0xFF)]
+ let b11 = T1_INV[Int((t[0] >> 8) & 0xFF)]
+ let b12 = T2_INV[Int((t[3] >> 16) & 0xFF)]
+ let b13 = T3_INV[Int(t[2] >> 24)]
+ b[1] = b10 ^ b11 ^ b12 ^ b13
+
+ let b20 = T0_INV[Int(t[2] & 0xFF)]
+ let b21 = T1_INV[Int((t[1] >> 8) & 0xFF)]
+ let b22 = T2_INV[Int((t[0] >> 16) & 0xFF)]
+ let b23 = T3_INV[Int(t[3] >> 24)]
+ b[2] = b20 ^ b21 ^ b22 ^ b23
+
+ let b30 = T0_INV[Int(t[3] & 0xFF)]
+ let b31 = T1_INV[Int((t[2] >> 8) & 0xFF)]
+ let b32 = T2_INV[Int((t[1] >> 16) & 0xFF)]
+ let b33 = T3_INV[Int(t[0] >> 24)]
+ b[3] = b30 ^ b31 ^ b32 ^ b33
+ }
+
+ // last round
+ t[0] = b[0] ^ rk[1][0]
+ t[1] = b[1] ^ rk[1][1]
+ t[2] = b[2] ^ rk[1][2]
+ t[3] = b[3] ^ rk[1][3]
+
+ // rounds
+
+ let lb00 = sBoxInv[Int(B0(t[0]))]
+ let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
+ let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
+ let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
+ b[0] = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
+
+ let lb10 = sBoxInv[Int(B0(t[1]))]
+ let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
+ let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
+ let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
+ b[1] = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
+
+ let lb20 = sBoxInv[Int(B0(t[2]))]
+ let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
+ let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
+ let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
+ b[2] = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
+
+ let lb30 = sBoxInv[Int(B0(t[3]))]
+ let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
+ let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
+ let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
+ b[3] = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
+
+ var out = [UInt8]()
+ out.reserveCapacity(b.count * 4)
+ for num in b {
+ out.append(UInt8(num & 0xFF))
+ out.append(UInt8((num >> 8) & 0xFF))
+ out.append(UInt8((num >> 16) & 0xFF))
+ out.append(UInt8((num >> 24) & 0xFF))
+ }
+
+ return out
+ }
+
+ fileprivate func expandKeyInv(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
+ let rounds = variant.Nr
+ var rk2: Array<Array<UInt32>> = expandKey(key, variant: variant)
+
+ for r in 1..<rounds {
+ var w: UInt32
+
+ w = rk2[r][0]
+ let u1 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
+ let u2 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
+ rk2[r][0] = u1 ^ u2
+
+ w = rk2[r][1]
+ let u11 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
+ let u12 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
+ rk2[r][1] = u11 ^ u12
+
+ w = rk2[r][2]
+ let u22 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
+ let u23 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
+ rk2[r][2] = u22 ^ u23
+
+ w = rk2[r][3]
+ let u33 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
+ let u34 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
+ rk2[r][3] = u33 ^ u34
+ }
+
+ return rk2
+ }
+
+ fileprivate func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
+
+ func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
+ var arr = Array<UInt32>()
+ for idx in stride(from: expanded.startIndex, to: expanded.endIndex, by: 4) {
+ let four = Array(expanded[idx..<idx.advanced(by: 4)].reversed())
+ let num = UInt32(bytes: four)
+ arr.append(num)
+ }
+
+ var allarr = Array<Array<UInt32>>()
+ for idx in stride(from: arr.startIndex, to: arr.endIndex, by: 4) {
+ allarr.append(Array(arr[idx..<idx.advanced(by: 4)]))
+ }
+ return allarr
+ }
+
+ /*
+ * Function used in the Key Expansion routine that takes a four-byte
+ * input word and applies an S-box to each of the four bytes to
+ * produce an output word.
+ */
+ func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
+ var result = word
+ for i in 0..<4 {
+ result[i] = UInt8(sBox[Int(word[i])])
+ }
+ return result
+ }
+
+ var w = Array<UInt8>(repeating: 0, count: variant.Nb * (variant.Nr + 1) * 4)
+ for i in 0..<variant.Nk {
+ for wordIdx in 0..<4 {
+ w[(4*i)+wordIdx] = key[(4*i)+wordIdx]
+ }
+ }
+
+ var tmp: Array<UInt8>
+
+ for i in variant.Nk..<variant.Nb * (variant.Nr + 1) {
+ tmp = Array<UInt8>(repeating: 0, count: 4)
+
+ for wordIdx in 0..<4 {
+ tmp[wordIdx] = w[4*(i-1)+wordIdx]
+ }
+ if (i % variant.Nk) == 0 {
+ tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: MemoryLayout<UInt32>.size))
+ tmp[0] = tmp.first! ^ Rcon[i/variant.Nk]
+ } else if variant.Nk > 6 && (i % variant.Nk) == 4 {
+ tmp = subWord(tmp)
+ }
+
+ // xor array of bytes
+ for wordIdx in 0..<4 {
+ w[4*i+wordIdx] = w[4*(i-variant.Nk)+wordIdx]^tmp[wordIdx]
+ }
+ }
+ return convertExpandedKey(w)
+ }
+
+}
+
+extension AESCipher {
+
+ fileprivate func B0(_ x: UInt32) -> UInt32 {
+ return x & 0xFF
+ }
+
+ fileprivate func B1(_ x: UInt32) -> UInt32 {
+ return (x >> 8) & 0xFF
+ }
+
+ fileprivate func B2(_ x: UInt32) -> UInt32 {
+ return (x >> 16) & 0xFF
+ }
+
+ fileprivate func B3(_ x: UInt32) -> UInt32 {
+ return (x >> 24) & 0xFF
+ }
+
+ fileprivate func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
+ var result: UInt32 = 0
+ result |= UInt32(B1(T0[Int(x0 & 255)]))
+ result |= UInt32(B1(T0[Int((x1 >> 8) & 255)])) << 8
+ result |= UInt32(B1(T0[Int((x2 >> 16) & 255)])) << 16
+ result |= UInt32(B1(T0[Int(x3 >> 24)])) << 24
+ return result
+ }
+
+ fileprivate func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
+ var sbox = Array<UInt32>(repeating: 0, count: 256)
+ var invsbox = sbox
+ sbox[0] = 0x63
+
+ var p: UInt8 = 1, q: UInt8 = 1
+
+ repeat {
+ #if swift(>=4.0)
+ p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
+ #else
+ p = p ^ (UInt8(truncatingBitPattern: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
+
+ #endif
+ q ^= q << 1
+ q ^= q << 2
+ q ^= q << 4
+ q ^= (q & 0x80) == 0x80 ? 0x09 : 0
+
+ let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
+
+ sbox[Int(p)] = UInt32(s)
+ invsbox[Int(s)] = UInt32(p)
+ } while (p != 1)
+
+ return (sBox: sbox, invSBox: invsbox)
+ }
+}
+
+// MARK: Foundation
+
+extension AESCipher {
+
+ convenience init(key: String, iv: String) throws {
+ guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes,
+ // swiftlint:disable conditional_binding_cascade
+ let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
+ // swiftlint:disable conditional_binding_cascade
+ throw Error.invalidKeyOrInitializationVector
+ }
+
+ try self.init(key: kkey, iv: iiv)
+ }
+}
+
+// MARK: - CBC
+
+// I have no better name for that
+private typealias Cookies_CipherOperationOnBlock = ([UInt8]) -> [UInt8]?
+
+private struct CBCBlockMode {
+ enum BlockError: Swift.Error {
+ case MissingInitializationVector
+ }
+
+ func encrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: Cookies_CipherOperationOnBlock) throws -> [UInt8] {
+ precondition(!blocks.isEmpty)
+ guard let iv = iv else {
+ throw BlockError.MissingInitializationVector
+ }
+
+ var out: [UInt8] = [UInt8]()
+ out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
+ var prevCiphertext = iv // for the first time prevCiphertext = iv
+ for plaintext in blocks {
+ if let encrypted = cipherOperation(xor(prevCiphertext, plaintext)) {
+ out.append(contentsOf: encrypted)
+ prevCiphertext = encrypted
+ }
+ }
+ return out
+ }
+
+ func decrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: Cookies_CipherOperationOnBlock) throws -> [UInt8] {
+ precondition(!blocks.isEmpty)
+ guard let iv = iv else {
+ throw BlockError.MissingInitializationVector
+ }
+
+ var out: [UInt8] = [UInt8]()
+ out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
+ var prevCiphertext = iv // for the first time prevCiphertext = iv
+ for ciphertext in blocks {
+ if let decrypted = cipherOperation(ciphertext) { // decrypt
+ out.append(contentsOf: xor(prevCiphertext, decrypted))
+ }
+ prevCiphertext = ciphertext
+ }
+
+ return out
+ }
+}
+
+// MARK: - Padding
+
+private struct Cookies_PKCS7 {
+
+ init() {
+ }
+
+ func add(bytes: [UInt8], blockSize: Int) -> [UInt8] {
+ let padding = UInt8(blockSize - (bytes.count % blockSize))
+ var withPadding = bytes
+ if padding == 0 {
+ // If the original data is a multiple of N bytes, then an extra block of bytes with value N is added.
+ for _ in 0..<blockSize {
+ withPadding.append(contentsOf: [UInt8(blockSize)])
+ }
+ } else {
+ // The value of each added byte is the number of bytes that are added
+ for _ in 0..<padding {
+ withPadding.append(contentsOf: [UInt8(padding)])
+ }
+ }
+ return withPadding
+ }
+
+ func remove(bytes: [UInt8], blockSize: Int?) -> [UInt8] {
+ let lastByte = bytes.last!
+ let padding = Int(lastByte) // last byte
+ let finalLength = bytes.count - padding
+
+ if finalLength < 0 {
+ return bytes
+ }
+
+ if padding >= 1 {
+ return Array(bytes[0..<finalLength])
+ }
+ return bytes
+ }
+}
+
+// MARK: - Utils
+
+private func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
+ var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
+ for i in 0..<xored.count {
+ xored[i] = a[i] ^ b[i]
+ }
+ return xored
+}
+
+//private func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
+// return ((value << by) & 0xFF) | (value >> (8 - by))
+//}
+
+//private func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
+// return ((value << by) & 0xFFFFFFFF) | (value >> (32 - by))
+//}
+
+private protocol BitshiftOperationsType {
+ static func << (lhs: Self, rhs: Self) -> Self
+}
+
+private protocol ByteConvertible {
+ init(_ value: UInt8)
+ init(truncatingBitPattern: UInt64)
+}
+
+#if swift(>=4.0)
+#else
+ extension UInt32: BitshiftOperationsType, ByteConvertible { }
+#endif
+
+fileprivate extension UInt32 {
+// init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
+// self = bytes.toInteger()
+// }
+
+ func bytes(totalBytes: Int = MemoryLayout<UInt32>.size) -> Array<UInt8> {
+ return arrayOfBytes(value: self, length: totalBytes)
+ }
+}
+
+private func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
+ var result = Array<UInt32>()
+ result.reserveCapacity(16)
+ for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
+ var val: UInt32 = 0
+ val |= slice.count > 3 ? UInt32(slice[idx.advanced(by: 3)]) << 24 : 0
+ val |= slice.count > 2 ? UInt32(slice[idx.advanced(by: 2)]) << 16 : 0
+ val |= slice.count > 1 ? UInt32(slice[idx.advanced(by: 1)]) << 8 : 0
+ val |= !slice.isEmpty ? UInt32(slice[idx]) : 0
+ result.append(val)
+ }
+
+ return result
+}
+
+/// Array of bytes, little-endian representation. Don't use if not necessary.
+/// I found this method slow
+private func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
+ let totalBytes = length ?? MemoryLayout<T>.size
+
+ let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+ valuePointer.pointee = value
+
+ let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
+ var bytes = Array<UInt8>(repeating: 0, count: totalBytes)
+ for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
+ bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
+ }
+
+ #if swift(>=4.1)
+ valuePointer.deinitialize(count: 1)
+ valuePointer.deallocate()
+ #else
+ valuePointer.deinitialize()
+ valuePointer.deallocate(capacity: 1)
+ #endif
+
+ return bytes
+}
+
+fileprivate extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
+ #if swift(>=4.0)
+ func toInteger<T>() -> T where T: FixedWidthInteger {
+ if self.isEmpty {
+ return 0
+ }
+
+ let size = MemoryLayout<T>.size
+ var bytes = self.reversed()
+ if bytes.count < MemoryLayout<T>.size {
+ let paddingCount = MemoryLayout<T>.size - bytes.count
+ if paddingCount > 0 {
+ bytes += Array<UInt8>(repeating: 0, count: paddingCount)
+ }
+ }
+
+ if size == 1 {
+ return T(truncatingIfNeeded: UInt64(bytes[0]))
+ }
+
+ var result: T = 0
+ for byte in bytes.reversed() {
+ result = result << 8 | T(byte)
+ }
+ return result
+ }
+ #else
+ func toInteger<T: Integer>() -> T where T: ByteConvertible, T: BitshiftOperationsType {
+ if self.isEmpty {
+ return 0
+ }
+
+ var bytes = self.reversed()
+ if bytes.count < MemoryLayout<T>.size {
+ let paddingCount = MemoryLayout<T>.size - bytes.count
+ if paddingCount > 0 {
+ bytes += Array<UInt8>(repeating: 0, count: paddingCount)
+ }
+ }
+
+ if MemoryLayout<T>.size == 1 {
+ return T(truncatingBitPattern: UInt64(bytes[0]))
+ }
+
+ var result: T = 0
+ for byte in bytes.reversed() {
+ result = result << 8 | T(byte)
+ }
+ return result
+ }
+ #endif
+}
+
+fileprivate extension Array {
+ /** split in chunks with given chunk size */
+ func chunks(size chunksize: Int) -> Array<Array<Element>> {
+ var words = Array<Array<Element>>()
+ words.reserveCapacity(self.count / chunksize)
+ for idx in stride(from: chunksize, through: self.count, by: chunksize) {
+ words.append(Array(self[idx - chunksize..<idx])) // slow for large table
+ }
+ let reminder = self.suffix(self.count % chunksize)
+ if !reminder.isEmpty {
+ words.append(Array(reminder))
+ }
+ return words
+ }
+}
+
+fileprivate extension Data {
+// var bytes: Array<UInt8> {
+// return Array(self)
+// }
+}
--- /dev/null
+//
+// Base64.swift
+// SwiftyBeaver (macOS)
+//
+// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
+//
+#if os(Linux)
+import Foundation
+
+struct InvalidBase64: Error {}
+
+struct Base64 {
+ static func decode(_ string: String) throws -> [UInt8] {
+ return try decode([UInt8](string.utf8))
+ }
+
+ /// Decodes a Base64 encoded String into Data
+ ///
+ /// - throws: If the string isn't base64 encoded
+ static func decode(_ string: [UInt8]) throws -> [UInt8] {
+ let lookupTable: [UInt8] = [
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+ 64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
+ 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+ ]
+
+ let remainder = string.count % 4
+ let length = (string.count - remainder) / 4
+
+ var decoded = [UInt8]()
+ decoded.reserveCapacity(length)
+
+ var index = 0
+ var i0: UInt8 = 0
+ var i1: UInt8 = 0
+ var i2: UInt8 = 0
+ var i3: UInt8 = 0
+
+ while index &+ 4 < string.count {
+ i0 = lookupTable[numericCast(string[index])]
+ i1 = lookupTable[numericCast(string[index &+ 1])]
+ i2 = lookupTable[numericCast(string[index &+ 2])]
+ i3 = lookupTable[numericCast(string[index &+ 3])]
+
+ if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 {
+ throw InvalidBase64()
+ }
+
+ decoded.append(i0 << 2 | i1 >> 4)
+ decoded.append(i1 << 4 | i2 >> 2)
+ decoded.append(i2 << 6 | i3)
+ index += 4
+ }
+
+ if string.count &- index > 1 {
+ i0 = lookupTable[numericCast(string[index])]
+ i1 = lookupTable[numericCast(string[index &+ 1])]
+
+ if i1 > 63 {
+ guard string[index] == 61 else {
+ throw InvalidBase64()
+ }
+
+ return decoded
+ }
+
+ if i2 > 63 {
+ guard string[index &+ 2] == 61 else {
+ throw InvalidBase64()
+ }
+
+ return decoded
+ }
+
+ decoded.append(i0 << 2 | i1 >> 4)
+
+ if string.count &- index > 2 {
+ i2 = lookupTable[numericCast(string[index &+ 2])]
+
+ if i2 > 63 {
+ guard string[index &+ 2] == 61 else {
+ throw InvalidBase64()
+ }
+
+ return decoded
+ }
+
+ decoded.append(i1 << 4 | i2 >> 2)
+
+ if string.count &- index > 3 {
+ i3 = lookupTable[numericCast(string[index &+ 3])]
+
+ if i3 > 63 {
+ guard string[index &+ 3] == 61 else {
+ throw InvalidBase64()
+ }
+
+ return decoded
+ }
+
+ decoded.append(i2 << 6 | i3)
+ }
+ }
+ }
+
+ return decoded
+ }
+
+ static func encode(_ data: [UInt8]) -> String {
+ let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ var encoded: String = ""
+
+ func appendCharacterFromBase(_ character: Int) {
+ encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)])
+ }
+
+ func byte(_ index: Int) -> Int {
+ return Int(data[index])
+ }
+
+ let decodedBytes = data.map { Int($0) }
+
+ var i = 0
+
+ while i < decodedBytes.count - 2 {
+ appendCharacterFromBase((byte(i) >> 2) & 0x3F)
+ appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
+ appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6))
+ appendCharacterFromBase(byte(i + 2) & 0x3F)
+ i += 3
+ }
+
+ if i < decodedBytes.count {
+ appendCharacterFromBase((byte(i) >> 2) & 0x3F)
+
+ if i == decodedBytes.count - 1 {
+ appendCharacterFromBase(((byte(i) & 0x3) << 4))
+ encoded.append("=")
+ } else {
+ appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
+ appendCharacterFromBase(((byte(i + 1) & 0xF) << 2))
+ }
+
+ encoded.append("=")
+ }
+
+ return encoded
+ }
+}
+
+#endif
--- /dev/null
+//
+// BaseDestination.swift
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 05.12.15.
+// Copyright © 2015 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+import Dispatch
+
+// store operating system / platform
+#if os(iOS)
+let OS = "iOS"
+#elseif os(OSX)
+let OS = "OSX"
+#elseif os(watchOS)
+let OS = "watchOS"
+#elseif os(tvOS)
+let OS = "tvOS"
+#elseif os(Linux)
+let OS = "Linux"
+#elseif os(FreeBSD)
+let OS = "FreeBSD"
+#elseif os(Windows)
+let OS = "Windows"
+#elseif os(Android)
+let OS = "Android"
+#else
+let OS = "Unknown"
+#endif
+
+/// destination which all others inherit from. do not directly use
+open class BaseDestination: Hashable, Equatable {
+
+ /// output format pattern, see documentation for syntax
+ open var format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
+
+ /// runs in own serial background thread for better performance
+ open var asynchronously = true
+
+ /// do not log any message which has a lower level than this one
+ open var minLevel = SwiftyBeaver.Level.verbose
+
+ /// set custom log level words for each level
+ open var levelString = LevelString()
+
+ /// set custom log level colors for each level
+ open var levelColor = LevelColor()
+
+ public struct LevelString {
+ public var verbose = "VERBOSE"
+ public var debug = "DEBUG"
+ public var info = "INFO"
+ public var warning = "WARNING"
+ public var error = "ERROR"
+ }
+
+ // For a colored log level word in a logged line
+ // empty on default
+ public struct LevelColor {
+ public var verbose = "" // silver
+ public var debug = "" // green
+ public var info = "" // blue
+ public var warning = "" // yellow
+ public var error = "" // red
+ }
+
+ var reset = ""
+ var escape = ""
+
+ var filters = [FilterType]()
+ let formatter = DateFormatter()
+ let startDate = Date()
+
+ // each destination class must have an own hashValue Int
+ #if swift(>=4.2)
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(defaultHashValue)
+ }
+ #else
+ lazy public var hashValue: Int = self.defaultHashValue
+ #endif
+
+ open var defaultHashValue: Int {return 0}
+
+ // each destination instance must have an own serial queue to ensure serial output
+ // GCD gives it a prioritization between User Initiated and Utility
+ var queue: DispatchQueue? //dispatch_queue_t?
+ var debugPrint = false // set to true to debug the internal filter logic of the class
+
+ public init() {
+ let uuid = NSUUID().uuidString
+ let queueLabel = "swiftybeaver-queue-" + uuid
+ queue = DispatchQueue(label: queueLabel, target: queue)
+ }
+
+ /// send / store the formatted log message to the destination
+ /// returns the formatted log message for processing by inheriting method
+ /// and for unit tests (nil if error)
+ open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String,
+ function: String, line: Int, context: Any? = nil) -> String? {
+
+ if format.hasPrefix("$J") {
+ return messageToJSON(level, msg: msg, thread: thread,
+ file: file, function: function, line: line, context: context)
+
+ } else {
+ return formatMessage(format, level: level, msg: msg, thread: thread,
+ file: file, function: function, line: line, context: context)
+ }
+ }
+
+ public func execute(synchronously: Bool, block: @escaping () -> Void) {
+ guard let queue = queue else {
+ fatalError("Queue not set")
+ }
+ if synchronously {
+ queue.sync(execute: block)
+ } else {
+ queue.async(execute: block)
+ }
+ }
+
+ public func executeSynchronously<T>(block: @escaping () throws -> T) rethrows -> T {
+ guard let queue = queue else {
+ fatalError("Queue not set")
+ }
+ return try queue.sync(execute: block)
+ }
+
+ ////////////////////////////////
+ // MARK: Format
+ ////////////////////////////////
+
+ /// returns (padding length value, offset in string after padding info)
+ private func parsePadding(_ text: String) -> (Int, Int) {
+ // look for digits followed by a alpha character
+ var s: String!
+ var sign: Int = 1
+ if text.firstChar == "-" {
+ sign = -1
+ s = String(text.suffix(from: text.index(text.startIndex, offsetBy: 1)))
+ } else {
+ s = text
+ }
+ let numStr = s.prefix { $0 >= "0" && $0 <= "9" }
+ if let num = Int(String(numStr)) {
+ return (sign * num, (sign == -1 ? 1 : 0) + numStr.count)
+ } else {
+ return (0, 0)
+ }
+ }
+
+ private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String {
+ if toLength > 0 {
+ // Pad to the left of the string
+ if text.count > toLength {
+ // Hm... better to use suffix or prefix?
+ return truncating ? String(text.suffix(toLength)) : text
+ } else {
+ return "".padding(toLength: toLength - text.count, withPad: " ", startingAt: 0) + text
+ }
+ } else if toLength < 0 {
+ // Pad to the right of the string
+ let maxLength = truncating ? -toLength : max(-toLength, text.count)
+ return text.padding(toLength: maxLength, withPad: " ", startingAt: 0)
+ } else {
+ return text
+ }
+ }
+
+ /// returns the log message based on the format pattern
+ func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String,
+ file: String, function: String, line: Int, context: Any? = nil) -> String {
+
+ var text = ""
+ // Prepend a $I for 'ignore' or else the first character is interpreted as a format character
+ // even if the format string did not start with a $.
+ let phrases: [String] = ("$I" + format).components(separatedBy: "$")
+
+ for phrase in phrases where !phrase.isEmpty {
+ let (padding, offset) = parsePadding(phrase)
+ let formatCharIndex = phrase.index(phrase.startIndex, offsetBy: offset)
+ let formatChar = phrase[formatCharIndex]
+ let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..<phrase.endIndex
+ let remainingPhrase = phrase[rangeAfterFormatChar]
+
+ switch formatChar {
+ case "I": // ignore
+ text += remainingPhrase
+ case "L":
+ text += paddedString(levelWord(level), padding) + remainingPhrase
+ case "M":
+ text += paddedString(msg, padding) + remainingPhrase
+ case "T":
+ text += paddedString(thread, padding) + remainingPhrase
+ case "N":
+ // name of file without suffix
+ text += paddedString(fileNameWithoutSuffix(file), padding) + remainingPhrase
+ case "n":
+ // name of file with suffix
+ text += paddedString(fileNameOfFile(file), padding) + remainingPhrase
+ case "F":
+ text += paddedString(function, padding) + remainingPhrase
+ case "l":
+ text += paddedString(String(line), padding) + remainingPhrase
+ case "D":
+ // start of datetime format
+ #if swift(>=3.2)
+ text += paddedString(formatDate(String(remainingPhrase)), padding)
+ #else
+ text += paddedString(formatDate(remainingPhrase), padding)
+ #endif
+ case "d":
+ text += remainingPhrase
+ case "U":
+ text += paddedString(uptime(), padding) + remainingPhrase
+ case "Z":
+ // start of datetime format in UTC timezone
+ #if swift(>=3.2)
+ text += paddedString(formatDate(String(remainingPhrase), timeZone: "UTC"), padding)
+ #else
+ text += paddedString(formatDate(remainingPhrase, timeZone: "UTC"), padding)
+ #endif
+ case "z":
+ text += remainingPhrase
+ case "C":
+ // color code ("" on default)
+ text += escape + colorForLevel(level) + remainingPhrase
+ case "c":
+ text += reset + remainingPhrase
+ case "X":
+ // add the context
+ if let cx = context {
+ text += paddedString(String(describing: cx).trimmingCharacters(in: .whitespacesAndNewlines), padding) + remainingPhrase
+ } else {
+ text += paddedString("", padding) + remainingPhrase
+ }
+ default:
+ text += phrase
+ }
+ }
+ // right trim only
+ return text.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
+ }
+
+ /// returns the log payload as optional JSON string
+ func messageToJSON(_ level: SwiftyBeaver.Level, msg: String,
+ thread: String, file: String, function: String, line: Int, context: Any? = nil) -> String? {
+ var dict: [String: Any] = [
+ "timestamp": Date().timeIntervalSince1970,
+ "level": level.rawValue,
+ "message": msg,
+ "thread": thread,
+ "file": file,
+ "function": function,
+ "line": line
+ ]
+ if let cx = context {
+ dict["context"] = cx
+ }
+ return jsonStringFromDict(dict)
+ }
+
+ /// returns the string of a level
+ func levelWord(_ level: SwiftyBeaver.Level) -> String {
+
+ var str = ""
+
+ switch level {
+ case .debug:
+ str = levelString.debug
+
+ case .info:
+ str = levelString.info
+
+ case .warning:
+ str = levelString.warning
+
+ case .error:
+ str = levelString.error
+
+ default:
+ // Verbose is default
+ str = levelString.verbose
+ }
+ return str
+ }
+
+ /// returns color string for level
+ func colorForLevel(_ level: SwiftyBeaver.Level) -> String {
+ var color = ""
+
+ switch level {
+ case .debug:
+ color = levelColor.debug
+
+ case .info:
+ color = levelColor.info
+
+ case .warning:
+ color = levelColor.warning
+
+ case .error:
+ color = levelColor.error
+
+ default:
+ color = levelColor.verbose
+ }
+ return color
+ }
+
+ /// returns the filename of a path
+ func fileNameOfFile(_ file: String) -> String {
+ let fileParts = file.components(separatedBy: "/")
+ if let lastPart = fileParts.last {
+ return lastPart
+ }
+ return ""
+ }
+
+ /// returns the filename without suffix (= file ending) of a path
+ func fileNameWithoutSuffix(_ file: String) -> String {
+ let fileName = fileNameOfFile(file)
+
+ if !fileName.isEmpty {
+ let fileNameParts = fileName.components(separatedBy: ".")
+ if let firstPart = fileNameParts.first {
+ return firstPart
+ }
+ }
+ return ""
+ }
+
+ /// returns a formatted date string
+ /// optionally in a given abbreviated timezone like "UTC"
+ func formatDate(_ dateFormat: String, timeZone: String = "") -> String {
+ if !timeZone.isEmpty {
+ formatter.timeZone = TimeZone(abbreviation: timeZone)
+ }
+ formatter.dateFormat = dateFormat
+ //let dateStr = formatter.string(from: NSDate() as Date)
+ let dateStr = formatter.string(from: Date())
+ return dateStr
+ }
+
+ /// returns a uptime string
+ func uptime() -> String {
+ let interval = Date().timeIntervalSince(startDate)
+
+ let hours = Int(interval) / 3600
+ let minutes = Int(interval / 60) - Int(hours * 60)
+ let seconds = Int(interval) - (Int(interval / 60) * 60)
+ let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
+
+ return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds])
+ }
+
+ /// returns the json-encoded string value
+ /// after it was encoded by jsonStringFromDict
+ func jsonStringValue(_ jsonString: String?, key: String) -> String {
+ guard let str = jsonString else {
+ return ""
+ }
+
+ // remove the leading {"key":" from the json string and the final }
+ let offset = key.length + 5
+ let endIndex = str.index(str.startIndex,
+ offsetBy: str.length - 2)
+ let range = str.index(str.startIndex, offsetBy: offset)..<endIndex
+ #if swift(>=3.2)
+ return String(str[range])
+ #else
+ return str[range]
+ #endif
+ }
+
+ /// turns dict into JSON-encoded string
+ func jsonStringFromDict(_ dict: [String: Any]) -> String? {
+ var jsonString: String?
+
+ // try to create JSON string
+ do {
+ let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
+ jsonString = String(data: jsonData, encoding: .utf8)
+ } catch {
+ print("SwiftyBeaver could not create JSON from dict.")
+ }
+ return jsonString
+ }
+
+ ////////////////////////////////
+ // MARK: Filters
+ ////////////////////////////////
+
+ /// Add a filter that determines whether or not a particular message will be logged to this destination
+ public func addFilter(_ filter: FilterType) {
+ filters.append(filter)
+ }
+
+ /// Remove a filter from the list of filters
+ public func removeFilter(_ filter: FilterType) {
+ #if swift(>=5)
+ let index = filters.firstIndex {
+ return ObjectIdentifier($0) == ObjectIdentifier(filter)
+ }
+ #else
+ let index = filters.index {
+ return ObjectIdentifier($0) == ObjectIdentifier(filter)
+ }
+ #endif
+
+ guard let filterIndex = index else {
+ return
+ }
+
+ filters.remove(at: filterIndex)
+ }
+
+ /// Answer whether the destination has any message filters
+ /// returns boolean and is used to decide whether to resolve
+ /// the message before invoking shouldLevelBeLogged
+ func hasMessageFilters() -> Bool {
+ return !getFiltersTargeting(Filter.TargetType.Message(.Equals([], true)),
+ fromFilters: self.filters).isEmpty
+ }
+
+ /// checks if level is at least minLevel or if a minLevel filter for that path does exist
+ /// returns boolean and can be used to decide if a message should be logged or not
+ func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String,
+ function: String, message: String? = nil) -> Bool {
+
+ if filters.isEmpty {
+ if level.rawValue >= minLevel.rawValue {
+ if debugPrint {
+ print("filters are empty and level >= minLevel")
+ }
+ return true
+ } else {
+ if debugPrint {
+ print("filters are empty and level < minLevel")
+ }
+ return false
+ }
+ }
+
+ let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message))
+
+ // Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method)
+ switch filterCheckResult[.excluded] {
+ case .some(.someFiltersMatch):
+ // Exclusion filters are present and at least one of them matches the log entry
+ if debugPrint {
+ print("filters are not empty and message was excluded")
+ }
+ return false
+ case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break
+ }
+
+ // If required filters exist, we should validate or invalidate the log if all of them pass or not
+ switch filterCheckResult[.required] {
+ case .some(.allFiltersMatch): return true
+ case .some(.someFiltersMatch): return false
+ case .some(.noFiltersMatchingType), .none: break
+ }
+
+ let checkLogLevel: () -> Bool = {
+ // Check if the log message's level matches or exceeds the minLevel of the destination
+ return level.rawValue >= self.minLevel.rawValue
+ }
+
+ // Non-required filters should only be applied if the log entry matches the filter condition (e.g. path)
+ switch filterCheckResult[.nonRequired] {
+ case .some(.allFiltersMatch): return true
+ case .some(.noFiltersMatchingType), .none: return checkLogLevel()
+ case .some(.someFiltersMatch(let partialMatchData)):
+ if partialMatchData.fullMatchCount > 0 {
+ // The log entry matches at least one filter condition and the destination's log level
+ return true
+ } else if partialMatchData.conditionMatchCount > 0 {
+ // The log entry matches at least one filter condition, but does not match or exceed the destination's log level
+ return false
+ } else {
+ // There is no filter with a matching filter condition. Check the destination's log level
+ return checkLogLevel()
+ }
+ }
+ }
+
+ func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] {
+ return fromFilters.filter { filter in
+ return filter.getTarget() == target
+ }
+ }
+
+ /**
+ Triggered by main flush() method on each destination. Runs in background thread.
+ Use for destinations that buffer log items, implement this function to flush those
+ buffers to their final destination (web server...)
+ */
+ func flush() {
+ // no implementation in base destination needed
+ }
+}
+
+public func == (lhs: BaseDestination, rhs: BaseDestination) -> Bool {
+ return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
+}
--- /dev/null
+//
+// ConsoleDestination.swift
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger on 05.12.15.
+// Copyright © 2015 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+
+public class ConsoleDestination: BaseDestination {
+
+ /// use NSLog instead of print, default is false
+ public var useNSLog = false
+ /// uses colors compatible to Terminal instead of Xcode, default is false
+ public var useTerminalColors: Bool = false {
+ didSet {
+ if useTerminalColors {
+ // use Terminal colors
+ reset = "\u{001b}[0m"
+ escape = "\u{001b}[38;5;"
+ levelColor.verbose = "251m" // silver
+ levelColor.debug = "35m" // green
+ levelColor.info = "38m" // blue
+ levelColor.warning = "178m" // yellow
+ levelColor.error = "197m" // red
+
+ } else {
+ // use colored Emojis for better visual distinction
+ // of log level for Xcode 8
+ levelColor.verbose = "🟣 " // silver
+ levelColor.debug = "🟢 " // green
+ levelColor.info = "🔵 " // blue
+ levelColor.warning = "🟡 " // yellow
+ levelColor.error = "🔴 " // red
+
+ }
+ }
+ }
+
+ override public var defaultHashValue: Int { return 1 }
+
+ public override init() {
+ super.init()
+ levelColor.verbose = "🟣 " // silver
+ levelColor.debug = "🟢 " // green
+ levelColor.info = "🔵 " // blue
+ levelColor.warning = "🟡 " // yellow
+ levelColor.error = "🔴 " // red
+ }
+
+ // print to Xcode Console. uses full base class functionality
+ override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
+ file: String, function: String, line: Int, context: Any? = nil) -> String? {
+ let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
+
+ if let str = formattedString {
+ if useNSLog {
+ #if os(Linux)
+ print(str)
+ #else
+ NSLog("%@", str)
+ #endif
+ } else {
+ print(str)
+ }
+ }
+ return formattedString
+ }
+
+}
--- /dev/null
+//
+// Extensions.swift
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger on 13.12.17.
+// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
+//
+
+import Foundation
+
+extension String {
+ /// cross-Swift compatible characters count
+ var length: Int {
+ return self.count
+ }
+
+ /// cross-Swift-compatible first character
+ var firstChar: Character? {
+ return self.first
+ }
+
+ /// cross-Swift-compatible last character
+ var lastChar: Character? {
+ return self.last
+ }
+
+ /// cross-Swift-compatible index
+ func find(_ char: Character) -> Index? {
+ #if swift(>=5)
+ return self.firstIndex(of: char)
+ #else
+ return self.index(of: char)
+ #endif
+ }
+}
--- /dev/null
+//
+// FileDestination.swift
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger on 05.12.15.
+// Copyright © 2015 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+
+public class FileDestination: BaseDestination {
+
+ public var logFileURL: URL?
+ public var syncAfterEachWrite: Bool = false
+ public var colored: Bool = false {
+ didSet {
+ if colored {
+ // bash font color, first value is intensity, second is color
+ // see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
+ // uses the 256-color table from http://bit.ly/1W1qJuH
+ reset = "\u{001b}[0m"
+ escape = "\u{001b}[38;5;"
+ levelColor.verbose = "251m" // silver
+ levelColor.debug = "35m" // green
+ levelColor.info = "38m" // blue
+ levelColor.warning = "178m" // yellow
+ levelColor.error = "197m" // red
+ } else {
+ reset = ""
+ escape = ""
+ levelColor.verbose = ""
+ levelColor.debug = ""
+ levelColor.info = ""
+ levelColor.warning = ""
+ levelColor.error = ""
+ }
+ }
+ }
+
+ override public var defaultHashValue: Int {return 2}
+ let fileManager = FileManager.default
+ var fileHandle: FileHandle?
+
+ public override init() {
+ // platform-dependent logfile directory default
+ var baseURL: URL?
+ #if os(OSX)
+ if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
+ baseURL = url
+ // try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
+ if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
+ do {
+ if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
+ try fileManager.createDirectory(at: appURL,
+ withIntermediateDirectories: true, attributes: nil)
+ baseURL = appURL
+ }
+ } catch {
+ print("Warning! Could not create folder /Library/Caches/\(appName)")
+ }
+ }
+ }
+ #else
+ #if os(Linux)
+ baseURL = URL(fileURLWithPath: "/var/cache")
+ #else
+ // iOS, watchOS, etc. are using the caches directory
+ if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
+ baseURL = url
+ }
+ #endif
+ #endif
+
+ if let baseURL = baseURL {
+ logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
+ }
+ super.init()
+ }
+
+ // append to file. uses full base class functionality
+ override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
+ file: String, function: String, line: Int, context: Any? = nil) -> String? {
+ let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
+
+ if let str = formattedString {
+ _ = saveToFile(str: str)
+ }
+ return formattedString
+ }
+
+ deinit {
+ // close file handle if set
+ if let fileHandle = fileHandle {
+ fileHandle.closeFile()
+ }
+ }
+
+ /// appends a string as line to a file.
+ /// returns boolean about success
+ func saveToFile(str: String) -> Bool {
+ guard let url = logFileURL else { return false }
+ do {
+ if fileManager.fileExists(atPath: url.path) == false {
+
+ let directoryURL = url.deletingLastPathComponent()
+ if fileManager.fileExists(atPath: directoryURL.path) == false {
+ try fileManager.createDirectory(
+ at: directoryURL,
+ withIntermediateDirectories: true
+ )
+ }
+
+ // create file if not existing
+ let line = str + "\n"
+ try line.write(to: url, atomically: true, encoding: .utf8)
+
+ #if os(iOS) || os(watchOS)
+ if #available(iOS 10.0, watchOS 3.0, *) {
+ var attributes = try fileManager.attributesOfItem(atPath: url.path)
+ attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
+ try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
+ }
+ #endif
+ } else {
+ // append to end of file
+ if fileHandle == nil {
+ // initial setting of file handle
+ fileHandle = try FileHandle(forWritingTo: url as URL)
+ }
+ if let fileHandle = fileHandle {
+ _ = fileHandle.seekToEndOfFile()
+ let line = str + "\n"
+ if let data = line.data(using: String.Encoding.utf8) {
+ fileHandle.write(data)
+ if syncAfterEachWrite {
+ fileHandle.synchronizeFile()
+ }
+ }
+ }
+ }
+ return true
+ } catch {
+ print("SwiftyBeaver File Destination could not write to file \(url).")
+ return false
+ }
+ }
+
+ /// deletes log file.
+ /// returns true if file was removed or does not exist, false otherwise
+ public func deleteLogFile() -> Bool {
+ guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
+ do {
+ try fileManager.removeItem(at: url)
+ fileHandle = nil
+ return true
+ } catch {
+ print("SwiftyBeaver File Destination could not remove file \(url).")
+ return false
+ }
+ }
+}
--- /dev/null
+//
+// FilterValidator.swift
+// SwiftyBeaver (iOS)
+//
+// Created by Felix Lisczyk on 07.07.19.
+// Copyright © 2019 Sebastian Kreutzberger. All rights reserved.
+//
+
+import Foundation
+
+/// FilterValidator is a utility class used by BaseDestination.
+/// It encapsulates the filtering logic for excluded, required
+/// and non-required filters.
+///
+/// FilterValidator evaluates a set of filters for a single log
+/// entry. It determines if these filters apply to the log entry
+/// based on their condition (path, function, message) and their
+/// minimum log level.
+
+struct FilterValidator {
+
+ // These are the different filter types that the user can set
+ enum ValidationType: CaseIterable {
+ case excluded
+ case required
+ case nonRequired
+
+ func apply(to filters: [FilterType]) -> [FilterType] {
+ switch self {
+ case .excluded:
+ return filters.filter { $0.isExcluded() }
+ case .required:
+ return filters.filter { $0.isRequired() && !$0.isExcluded() }
+ case .nonRequired:
+ return filters.filter { !$0.isRequired() && !$0.isExcluded() }
+ }
+ }
+ }
+
+ // Wrapper object for input parameters
+ struct Input {
+ let filters: [FilterType]
+ let level: SwiftyBeaver.Level
+ let path: String
+ let function: String
+ let message: String?
+ }
+
+ // Result wrapper object
+ enum Result {
+ case allFiltersMatch // All filters fully match the log entry (condition + minimum log level)
+ case someFiltersMatch(PartialMatchData) // Only some filters fully match the log entry (condition + minimum log level)
+ case noFiltersMatchingType // There are no filters set for a particular type (excluded, required, nonRequired)
+
+ struct PartialMatchData {
+ let fullMatchCount: Int // Number of filters that match both the condition and the minimum log level of the log entry
+ let conditionMatchCount: Int // Number of filters that match ONLY the condition of the log entry (path, function, message)
+ let logLevelMatchCount: Int // Number of filters that match ONLY the minimum log level of the log entry
+ }
+ }
+
+ static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] {
+ var results = [ValidationType: Result]()
+ for type in types {
+ let filtersToValidate = type.apply(to: input.filters)
+
+ if filtersToValidate.isEmpty {
+ // There are no filters set for this particular type
+ results[type] = .noFiltersMatchingType
+ } else {
+ var fullMatchCount: Int = 0
+ var conditionMatchCount: Int = 0
+ var logLevelMatchCount: Int = 0
+
+ for filter in filtersToValidate {
+ let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message)
+ let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level)
+
+ switch (filterMatchesCondition, filterMatchesMinLogLevel) {
+ // Filter matches both the condition and the minimum log level
+ case (true, true): fullMatchCount += 1
+ // Filter matches only the condition (path, function, message)
+ case (true, false): conditionMatchCount += 1
+ // Filter matches only the minimum log level
+ case (false, true): logLevelMatchCount += 1
+ // Filter does not match the condition nor the minimum log level
+ case (false, false): break
+ }
+ }
+
+ if filtersToValidate.count == fullMatchCount {
+ // All filters fully match the log entry
+ results[type] = .allFiltersMatch
+ } else {
+ // Only some filters match the log entry
+ results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount))
+ }
+ }
+ }
+
+ return results
+ }
+
+ private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level,
+ path: String, function: String, message: String?) -> Bool {
+ let passes: Bool
+
+ switch filter.getTarget() {
+ case .Path(_):
+ passes = filter.apply(path)
+
+ case .Function(_):
+ passes = filter.apply(function)
+
+ case .Message(_):
+ guard let message = message else {
+ return false
+ }
+
+ passes = filter.apply(message)
+ }
+
+ return passes
+ }
+
+ private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool {
+ return filter.reachedMinLevel(level)
+ }
+}
--- /dev/null
+//
+// GoogleCloudDestination.swift
+// SwiftyBeaver
+//
+// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
+//
+
+import Foundation
+
+public final class GoogleCloudDestination: BaseDestination {
+
+ private let serviceName: String
+
+ public init(serviceName: String) {
+ self.serviceName = serviceName
+ super.init()
+ }
+
+ override public var asynchronously: Bool {
+ get {
+ return false
+ }
+ set {
+ return
+ }
+ }
+
+ override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
+ file: String, function: String, line: Int, context: Any? = nil) -> String? {
+
+ let reportLocation: [String: Any] = ["filePath": file, "lineNumber": line, "functionName": function]
+ var gcpContext: [String: Any] = ["reportLocation": reportLocation]
+ if let context = context as? [String: Any] {
+ if let httpRequestContext = context["httpRequest"] as? [String: Any] {
+ gcpContext["httpRequest"] = httpRequestContext
+ }
+
+ if let user = context["user"] as? String {
+ gcpContext["user"] = user
+ }
+ }
+
+ let gcpJSON: [String: Any] = [
+ "serviceContext": [
+ "service": serviceName
+ ],
+ "message": msg,
+ "severity": level.severity,
+ "context": gcpContext
+ ]
+
+ let finalLogString: String
+
+ do {
+ finalLogString = try jsonString(obj: gcpJSON)
+ } catch {
+ let uncrashableLogString = "{\"context\":{\"reportLocation\":{\"filePath\": \"\(file)\"" +
+ ",\"functionName\":\"\(function)\"" +
+ ",\"lineNumber\":\(line)},\"severity\"" +
+ ":\"CRITICAL\",\"message\":\"Error encoding " +
+ "JSON log entry. You may be losing log messages!\"}"
+ finalLogString = uncrashableLogString.description
+ }
+ print(finalLogString)
+ return finalLogString
+ }
+
+ private func jsonString(obj: Dictionary<String, Any>) throws -> String {
+ let json = try JSONSerialization.data(withJSONObject: obj, options: [])
+ guard let string = String(data: json, encoding: .utf8) else {
+ throw GCPError.serialization
+ }
+ return string
+ }
+}
+
+///
+/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
+extension SwiftyBeaver.Level {
+
+ /// Verbose is reported as Debug to GCP.
+ /// Recommend you don't bother using it.
+ var severity: String {
+ switch self {
+ // There is only one level below "Debug": "Default", which becomes "Any" and is considered as a potential error as well
+ case .verbose: return "DEBUG"
+ case .debug: return "DEBUG"
+ case .info: return "INFO"
+ case .warning: return "WARNING"
+ case .error: return "ERROR"
+ }
+ }
+}
+
+private enum GCPError: Error {
+ case serialization
+}
--- /dev/null
+//
+// Filter.swift
+// SwiftyBeaver
+//
+// Created by Jeff Roberts on 5/31/16.
+// Copyright © 2015 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+
+/// FilterType is a protocol that describes something that determines
+/// whether or not a message gets logged. A filter answers a Bool when it
+/// is applied to a value. If the filter passes, it shall return true,
+/// false otherwise.
+///
+/// A filter must contain a target, which identifies what it filters against
+/// A filter can be required meaning that all required filters against a specific
+/// target must pass in order for the message to be logged.
+public protocol FilterType : class {
+ func apply(_ value: Any) -> Bool
+ func getTarget() -> Filter.TargetType
+ func isRequired() -> Bool
+ func isExcluded() -> Bool
+ func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool
+}
+
+/// Filters is syntactic sugar used to easily construct filters
+public class Filters {
+ public static let Path = PathFilterFactory.self
+ public static let Function = FunctionFilterFactory.self
+ public static let Message = MessageFilterFactory.self
+}
+
+/// Filter is an abstract base class for other filters
+public class Filter {
+ public enum TargetType {
+ case Path(Filter.ComparisonType)
+ case Function(Filter.ComparisonType)
+ case Message(Filter.ComparisonType)
+ }
+
+ public enum ComparisonType {
+ case StartsWith([String], Bool)
+ case Contains([String], Bool)
+ case Excludes([String], Bool)
+ case EndsWith([String], Bool)
+ case Equals([String], Bool)
+ case Custom((String) -> Bool)
+ }
+
+ let targetType: Filter.TargetType
+ let required: Bool
+ let minLevel: SwiftyBeaver.Level
+
+ public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
+ self.targetType = target
+ self.required = required
+ self.minLevel = minLevel
+ }
+
+ public func getTarget() -> Filter.TargetType {
+ return self.targetType
+ }
+
+ public func isRequired() -> Bool {
+ return self.required
+ }
+
+ public func isExcluded() -> Bool {
+ return false
+ }
+
+ /// returns true of set minLevel is >= as given level
+ public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool {
+ //print("checking if given level \(level) >= \(minLevel)")
+ return level.rawValue >= minLevel.rawValue
+ }
+}
+
+/// CompareFilter is a FilterType that can filter based upon whether a target
+/// starts with, contains or ends with a specific string. CompareFilters can be
+/// case sensitive.
+public class CompareFilter: Filter, FilterType {
+
+ private var filterComparisonType: Filter.ComparisonType?
+
+ override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
+ super.init(target, required: required, minLevel: minLevel)
+
+ let comparisonType: Filter.ComparisonType?
+ switch self.getTarget() {
+ case let .Function(comparison):
+ comparisonType = comparison
+
+ case let .Path(comparison):
+ comparisonType = comparison
+
+ case let .Message(comparison):
+ comparisonType = comparison
+
+ /*default:
+ comparisonType = nil*/
+ }
+ self.filterComparisonType = comparisonType
+ }
+
+ public func apply(_ value: Any) -> Bool {
+ guard let value = value as? String else {
+ return false
+ }
+
+ guard let filterComparisonType = self.filterComparisonType else {
+ return false
+ }
+
+ let matches: Bool
+ switch filterComparisonType {
+ case let .Contains(strings, caseSensitive):
+ matches = !strings.filter { string in
+ return caseSensitive ? value.contains(string) :
+ value.lowercased().contains(string.lowercased())
+ }.isEmpty
+
+ case let .Excludes(strings, caseSensitive):
+ matches = !strings.filter { string in
+ return caseSensitive ? !value.contains(string) :
+ !value.lowercased().contains(string.lowercased())
+ }.isEmpty
+
+ case let .StartsWith(strings, caseSensitive):
+ matches = !strings.filter { string in
+ return caseSensitive ? value.hasPrefix(string) :
+ value.lowercased().hasPrefix(string.lowercased())
+ }.isEmpty
+
+ case let .EndsWith(strings, caseSensitive):
+ matches = !strings.filter { string in
+ return caseSensitive ? value.hasSuffix(string) :
+ value.lowercased().hasSuffix(string.lowercased())
+ }.isEmpty
+
+ case let .Equals(strings, caseSensitive):
+ matches = !strings.filter { string in
+ return caseSensitive ? value == string :
+ value.lowercased() == string.lowercased()
+ }.isEmpty
+ case let .Custom(predicate):
+ matches = predicate(value)
+ }
+
+ return matches
+ }
+
+ override public func isExcluded() -> Bool {
+ guard let filterComparisonType = self.filterComparisonType else { return false }
+
+ switch filterComparisonType {
+ case .Excludes:
+ return true
+ default:
+ return false
+ }
+ }
+}
+
+// Syntactic sugar for creating a function comparison filter
+public class FunctionFilterFactory {
+ public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func contains(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func excludes(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func equals(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
+ return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel)
+ }
+}
+
+// Syntactic sugar for creating a message comparison filter
+public class MessageFilterFactory {
+ public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func contains(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func excludes(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func equals(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
+ return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel)
+ }
+}
+
+// Syntactic sugar for creating a path comparison filter
+public class PathFilterFactory {
+ public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func contains(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func excludes(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func equals(_ strings: String..., caseSensitive: Bool = false,
+ required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
+ return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
+ }
+
+ public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
+ return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel)
+ }
+}
+
+extension Filter.TargetType: Equatable {
+}
+
+// The == does not compare associated values for each enum. Instead == evaluates to true
+// if both enums are the same "types", ignoring the associated values of each enum
+public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
+ switch (lhs, rhs) {
+
+ case (.Path, .Path):
+ return true
+
+ case (.Function, .Function):
+ return true
+
+ case (.Message, .Message):
+ return true
+
+ default:
+ return false
+ }
+}
--- /dev/null
+//
+// SBPlatformDestination
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger on 22.01.16.
+// Copyright © 2016 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+// platform-dependent import frameworks to get device details
+// valid values for os(): OSX, iOS, watchOS, tvOS, Linux
+// in Swift 3 the following were added: FreeBSD, Windows, Android
+#if os(iOS) || os(tvOS) || os(watchOS)
+ import UIKit
+ var DEVICE_MODEL: String {
+ get {
+ var systemInfo = utsname()
+ uname(&systemInfo)
+ let machineMirror = Mirror(reflecting: systemInfo.machine)
+ let identifier = machineMirror.children.reduce("") { identifier, element in
+ guard let value = element.value as? Int8, value != 0 else { return identifier }
+ return identifier + String(UnicodeScalar(UInt8(value)))
+ }
+ return identifier
+ }
+ }
+#else
+ let DEVICE_MODEL = ""
+#endif
+
+#if os(iOS) || os(tvOS)
+ var DEVICE_NAME = UIDevice.current.name
+#else
+ // under watchOS UIDevice is not existing, http://apple.co/26ch5J1
+ let DEVICE_NAME = ""
+#endif
+
+public class SBPlatformDestination: BaseDestination {
+
+ public var appID = ""
+ public var appSecret = ""
+ public var encryptionKey = ""
+ public var analyticsUserName = "" // user email, ID, name, etc.
+ public var analyticsUUID: String { return uuid }
+
+ // when to send to server
+ public struct SendingPoints {
+ public var verbose = 0
+ public var debug = 1
+ public var info = 5
+ public var warning = 8
+ public var error = 10
+ public var threshold = 10 // send to server if points reach that value
+ }
+ public var sendingPoints = SendingPoints()
+ public var showNSLog = false // executes toNSLog statements to debug the class
+ var points = 0
+
+ public var serverURL = URL(string: "https://api.swiftybeaver.com/api/entries/") // optional
+ public var entriesFileURL = URL(fileURLWithPath: "") // not optional
+ public var sendingFileURL = URL(fileURLWithPath: "")
+ public var analyticsFileURL = URL(fileURLWithPath: "")
+
+ private let minAllowedThreshold = 1 // over-rules SendingPoints.Threshold
+ private let maxAllowedThreshold = 1000 // over-rules SendingPoints.Threshold
+ private var sendingInProgress = false
+ private var initialSending = true
+
+ // analytics
+ var uuid = ""
+
+ // destination
+ override public var defaultHashValue: Int {return 3}
+ let fileManager = FileManager.default
+ let isoDateFormatter = DateFormatter()
+
+ /// init platform with default internal filenames
+ public init(appID: String, appSecret: String, encryptionKey: String,
+ serverURL: URL? = URL(string: "https://api.swiftybeaver.com/api/entries/"),
+ entriesFileName: String = "sbplatform_entries.json",
+ sendingfileName: String = "sbplatform_entries_sending.json",
+ analyticsFileName: String = "sbplatform_analytics.json") {
+ super.init()
+ self.serverURL = serverURL
+ self.appID = appID
+ self.appSecret = appSecret
+ self.encryptionKey = encryptionKey
+
+ // setup where to write the json files
+ var baseURL: URL?
+ #if os(OSX)
+ if let url = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
+ baseURL = url
+ // try to use ~/Library/Application Support/APP NAME instead of ~/Library/Application Support
+ if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
+ do {
+ if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
+ try fileManager.createDirectory(at: appURL,
+ withIntermediateDirectories: true, attributes: nil)
+ baseURL = appURL
+ }
+ } catch {
+ // it is too early in the class lifetime to be able to use toNSLog()
+ print("Warning! Could not create folder ~/Library/Application Support/\(appName).")
+ }
+ }
+ }
+ #else
+ #if os(tvOS)
+ // tvOS can just use the caches directory
+ if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
+ baseURL = url
+ }
+ #elseif os(Linux)
+ // Linux is using /var/cache
+ let baseDir = "/var/cache/"
+ entriesFileURL = URL(fileURLWithPath: baseDir + entriesFileName)
+ sendingFileURL = URL(fileURLWithPath: baseDir + sendingfileName)
+ analyticsFileURL = URL(fileURLWithPath: baseDir + analyticsFileName)
+ #else
+ // iOS and watchOS are using the app’s document directory
+ if let url = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
+ baseURL = url
+ }
+ #endif
+ #endif
+
+ #if os(Linux)
+ // get, update loaded and save analytics data to file on start
+ let dict = analytics(analyticsFileURL, update: true)
+ _ = saveDictToFile(dict, url: analyticsFileURL)
+ #else
+ if let baseURL = baseURL {
+ // is just set for everything but not Linux
+ entriesFileURL = baseURL.appendingPathComponent(entriesFileName,
+ isDirectory: false)
+ sendingFileURL = baseURL.appendingPathComponent(sendingfileName,
+ isDirectory: false)
+ analyticsFileURL = baseURL.appendingPathComponent(analyticsFileName,
+ isDirectory: false)
+
+ // get, update loaded and save analytics data to file on start
+ let dict = analytics(analyticsFileURL, update: true)
+ _ = saveDictToFile(dict, url: analyticsFileURL)
+ }
+ #endif
+ }
+
+ // append to file, each line is a JSON dict
+ override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
+ file: String, function: String, line: Int, context: Any? = nil) -> String? {
+
+ var jsonString: String?
+
+ let dict: [String: Any] = [
+ "timestamp": Date().timeIntervalSince1970,
+ "level": level.rawValue,
+ "message": msg,
+ "thread": thread,
+ "fileName": file.components(separatedBy: "/").last!,
+ "function": function,
+ "line": line]
+
+ jsonString = jsonStringFromDict(dict)
+
+ if let str = jsonString {
+ toNSLog("saving '\(msg)' to \(entriesFileURL)")
+ _ = saveToFile(str, url: entriesFileURL)
+ //toNSLog(entriesFileURL.path!)
+
+ // now decide if the stored log entries should be sent to the server
+ // add level points to current points amount and send to server if threshold is hit
+ let newPoints = sendingPointsForLevel(level)
+ points += newPoints
+ toNSLog("current sending points: \(points)")
+
+ if (points >= sendingPoints.threshold && points >= minAllowedThreshold) || points > maxAllowedThreshold {
+ toNSLog("\(points) points is >= threshold")
+ // above threshold, send to server
+ sendNow()
+
+ } else if initialSending {
+ initialSending = false
+ // first logging at this session
+ // send if json file still contains old log entries
+ if let logEntries = logsFromFile(entriesFileURL) {
+ let lines = logEntries.count
+ if lines > 1 {
+ var msg = "initialSending: \(points) points is below threshold "
+ msg += "but json file already has \(lines) lines."
+ toNSLog(msg)
+ sendNow()
+ }
+ }
+ }
+ }
+
+ return jsonString
+ }
+
+ // MARK: Send-to-Server Logic
+
+ /// does a (manual) sending attempt of all unsent log entries to SwiftyBeaver Platform
+ public func sendNow() {
+
+ if sendFileExists() {
+ toNSLog("reset points to 0")
+ points = 0
+ } else {
+ if !renameJsonToSendFile() {
+ return
+ }
+ }
+
+ if !sendingInProgress {
+ sendingInProgress = true
+ //let (jsonString, lines) = logsFromFile(sendingFileURL)
+ var lines = 0
+
+ guard let logEntries = logsFromFile(sendingFileURL) else {
+ sendingInProgress = false
+ return
+ }
+
+ lines = logEntries.count
+
+ if lines > 0 {
+ var payload = [String: Any]()
+ // merge device and analytics dictionaries
+ let deviceDetailsDict = deviceDetails()
+
+ var analyticsDict = analytics(analyticsFileURL)
+
+ for key in deviceDetailsDict.keys {
+ analyticsDict[key] = deviceDetailsDict[key]
+ }
+ payload["device"] = analyticsDict
+ payload["entries"] = logEntries
+
+ if let str = jsonStringFromDict(payload) {
+ //toNSLog(str) // uncomment to see full payload
+ toNSLog("Encrypting \(lines) log entries ...")
+ if let encryptedStr = encrypt(str) {
+ var msg = "Sending \(lines) encrypted log entries "
+ msg += "(\(encryptedStr.length) chars) to server ..."
+ toNSLog(msg)
+ sendToServerAsync(encryptedStr) { ok, _ in
+
+ self.toNSLog("Sent \(lines) encrypted log entries to server, received ok: \(ok)")
+ if ok {
+ _ = self.deleteFile(self.sendingFileURL)
+ }
+ self.sendingInProgress = false
+ self.points = 0
+ }
+ }
+ }
+ } else {
+ sendingInProgress = false
+ }
+ }
+ }
+
+ /// sends a string to the SwiftyBeaver Platform server, returns ok if status 200 and HTTP status
+ func sendToServerAsync(_ str: String?, complete: @escaping (_ ok: Bool, _ status: Int) -> Void) {
+
+ let timeout = 10.0
+
+ if let payload = str, let queue = self.queue, let serverURL = serverURL {
+
+ // create operation queue which uses current serial queue of destination
+ let operationQueue = OperationQueue()
+ operationQueue.underlyingQueue = queue
+
+ let session = URLSession(configuration:
+ URLSessionConfiguration.default,
+ delegate: nil, delegateQueue: operationQueue)
+
+ toNSLog("assembling request ...")
+
+ // assemble request
+ var request = URLRequest(url: serverURL,
+ cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
+ timeoutInterval: timeout)
+ request.httpMethod = "POST"
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.addValue("application/json", forHTTPHeaderField: "Accept")
+
+ // basic auth header (just works on Linux for Swift 3.1+, macOS is fine)
+ guard let credentials = "\(appID):\(appSecret)".data(using: String.Encoding.utf8) else {
+ toNSLog("Error! Could not set basic auth header")
+ return complete(false, 0)
+ }
+
+ #if os(Linux)
+ let base64Credentials = Base64.encode([UInt8](credentials))
+ #else
+ let base64Credentials = credentials.base64EncodedString(options: [])
+ #endif
+ request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
+ //toNSLog("\nrequest:")
+ //print(request)
+
+ // POST parameters
+ let params = ["payload": payload]
+ do {
+ request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
+ } catch {
+ toNSLog("Error! Could not create JSON for server payload.")
+ return complete(false, 0)
+ }
+ toNSLog("sending params: \(params)")
+ toNSLog("sending ...")
+
+ sendingInProgress = true
+
+ // send request async to server on destination queue
+ let task = session.dataTask(with: request) { _, response, error in
+ var ok = false
+ var status = 0
+ self.toNSLog("received response from server")
+
+ if let error = error {
+ // an error did occur
+ self.toNSLog("Error! Could not send entries to server. \(error)")
+ } else {
+ if let response = response as? HTTPURLResponse {
+ status = response.statusCode
+ if status == 200 {
+ // all went well, entries were uploaded to server
+ ok = true
+ } else {
+ // status code was not 200
+ var msg = "Error! Sending entries to server failed "
+ msg += "with status code \(status)"
+ self.toNSLog(msg)
+ }
+ }
+ }
+ return complete(ok, status)
+ }
+ task.resume()
+ session.finishTasksAndInvalidate()
+ //while true {} // commenting this line causes a crash on Linux unit tests?!?
+ }
+ }
+
+ /// returns sending points based on level
+ func sendingPointsForLevel(_ level: SwiftyBeaver.Level) -> Int {
+
+ switch level {
+ case .debug:
+ return sendingPoints.debug
+ case .info:
+ return sendingPoints.info
+ case .warning:
+ return sendingPoints.warning
+ case .error:
+ return sendingPoints.error
+ default:
+ return sendingPoints.verbose
+ }
+ }
+
+ // MARK: File Handling
+
+ /// appends a string as line to a file.
+ /// returns boolean about success
+ func saveToFile(_ str: String, url: URL, overwrite: Bool = false) -> Bool {
+ do {
+ if fileManager.fileExists(atPath: url.path) == false || overwrite {
+ // create file if not existing
+ let line = str + "\n"
+ try line.write(to: url, atomically: true, encoding: String.Encoding.utf8)
+ } else {
+ // append to end of file
+ let fileHandle = try FileHandle(forWritingTo: url)
+ _ = fileHandle.seekToEndOfFile()
+ let line = str + "\n"
+ if let data = line.data(using: String.Encoding.utf8) {
+ fileHandle.write(data)
+ fileHandle.closeFile()
+ }
+ }
+ return true
+ } catch {
+ toNSLog("Error! Could not write to file \(url).")
+ return false
+ }
+ }
+
+ func sendFileExists() -> Bool {
+ return fileManager.fileExists(atPath: sendingFileURL.path)
+ }
+
+ func renameJsonToSendFile() -> Bool {
+ do {
+ try fileManager.moveItem(at: entriesFileURL, to: sendingFileURL)
+ return true
+ } catch {
+ toNSLog("SwiftyBeaver Platform Destination could not rename json file.")
+ return false
+ }
+ }
+
+ /// returns optional array of log dicts from a file which has 1 json string per line
+ func logsFromFile(_ url: URL) -> [[String: Any]]? {
+ var lines = 0
+ do {
+ // try to read file, decode every JSON line and put dict from each line in array
+ let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
+ let linesArray = fileContent.components(separatedBy: "\n")
+ var dicts = [[String: Any]()] // array of dictionaries
+ for lineJSON in linesArray {
+ lines += 1
+ if lineJSON.firstChar == "{" && lineJSON.lastChar == "}" {
+ // try to parse json string into dict
+ if let data = lineJSON.data(using: .utf8) {
+ do {
+ if let dict = try JSONSerialization.jsonObject(with: data,
+ options: .mutableContainers) as? [String: Any] {
+ if !dict.isEmpty {
+ dicts.append(dict)
+ }
+ }
+ } catch {
+ var msg = "Error! Could not parse "
+ msg += "line \(lines) in file \(url)."
+ toNSLog(msg)
+ }
+ }
+ }
+ }
+ dicts.removeFirst()
+ return dicts
+ } catch {
+ toNSLog("Error! Could not read file \(url).")
+ }
+ return nil
+ }
+
+ /// returns AES-256 CBC encrypted optional string
+ func encrypt(_ str: String) -> String? {
+ return AES256CBC.encryptString(str, password: encryptionKey)
+ }
+
+ /// Delete file to get started again
+ func deleteFile(_ url: URL) -> Bool {
+ do {
+ try FileManager.default.removeItem(at: url)
+ return true
+ } catch {
+ toNSLog("Warning! Could not delete file \(url).")
+ }
+ return false
+ }
+
+ // MARK: Device & Analytics
+
+ // returns dict with device details. Amount depends on platform
+ func deviceDetails() -> [String: String] {
+ var details = [String: String]()
+
+ details["os"] = OS
+ let osVersion = ProcessInfo.processInfo.operatingSystemVersion
+ // becomes for example 10.11.2 for El Capitan
+ var osVersionStr = String(osVersion.majorVersion)
+ osVersionStr += "." + String(osVersion.minorVersion)
+ osVersionStr += "." + String(osVersion.patchVersion)
+ details["osVersion"] = osVersionStr
+ details["hostName"] = ProcessInfo.processInfo.hostName
+ details["deviceName"] = ""
+ details["deviceModel"] = ""
+
+ if DEVICE_NAME != "" {
+ details["deviceName"] = DEVICE_NAME
+ }
+ if DEVICE_MODEL != "" {
+ details["deviceModel"] = DEVICE_MODEL
+ }
+ return details
+ }
+
+ /// returns (updated) analytics dict, optionally loaded from file.
+ func analytics(_ url: URL, update: Bool = false) -> [String: Any] {
+
+ var dict = [String: Any]()
+ let now = NSDate().timeIntervalSince1970
+
+ uuid = NSUUID().uuidString
+ dict["uuid"] = uuid
+ dict["firstStart"] = now
+ dict["lastStart"] = now
+ dict["starts"] = 1
+ dict["userName"] = analyticsUserName
+ dict["firstAppVersion"] = appVersion()
+ dict["appVersion"] = appVersion()
+ dict["firstAppBuild"] = appBuild()
+ dict["appBuild"] = appBuild()
+
+ if let loadedDict = dictFromFile(analyticsFileURL) {
+ if let val = loadedDict["firstStart"] as? Double {
+ dict["firstStart"] = val
+ }
+ if let val = loadedDict["lastStart"] as? Double {
+ if update {
+ dict["lastStart"] = now
+ } else {
+ dict["lastStart"] = val
+ }
+ }
+ if let val = loadedDict["starts"] as? Int {
+ if update {
+ dict["starts"] = val + 1
+ } else {
+ dict["starts"] = val
+ }
+ }
+ if let val = loadedDict["uuid"] as? String {
+ dict["uuid"] = val
+ uuid = val
+ }
+ if let val = loadedDict["userName"] as? String {
+ if update && !analyticsUserName.isEmpty {
+ dict["userName"] = analyticsUserName
+ } else {
+ if !val.isEmpty {
+ dict["userName"] = val
+ }
+ }
+ }
+ if let val = loadedDict["firstAppVersion"] as? String {
+ dict["firstAppVersion"] = val
+ }
+ if let val = loadedDict["firstAppBuild"] as? Int {
+ dict["firstAppBuild"] = val
+ }
+ }
+ return dict
+ }
+
+ /// Returns the current app version string (like 1.2.5) or empty string on error
+ func appVersion() -> String {
+ if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
+ return version
+ }
+ return ""
+ }
+
+ /// Returns the current app build as integer (like 563, always incrementing) or 0 on error
+ func appBuild() -> Int {
+ if let version = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
+ if let intVersion = Int(version) {
+ return intVersion
+ }
+ }
+ return 0
+ }
+
+ /// returns optional dict from a json encoded file
+ func dictFromFile(_ url: URL) -> [String: Any]? {
+ do {
+ let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
+ if let data = fileContent.data(using: .utf8) {
+ return try JSONSerialization.jsonObject(with: data,
+ options: .mutableContainers) as? [String: Any]
+ }
+ } catch {
+ toNSLog("SwiftyBeaver Platform Destination could not read file \(url)")
+ }
+ return nil
+ }
+
+ // turns dict into JSON and saves it to file
+ func saveDictToFile(_ dict: [String: Any], url: URL) -> Bool {
+ let jsonString = jsonStringFromDict(dict)
+
+ if let str = jsonString {
+ toNSLog("saving '\(str)' to \(url)")
+ return saveToFile(str, url: url, overwrite: true)
+ }
+ return false
+ }
+
+ // MARK: Debug Helpers
+
+ /// log String to toNSLog. Used to debug the class logic
+ func toNSLog(_ str: String) {
+ if showNSLog {
+ #if os(Linux)
+ print("SBPlatform: \(str)")
+ #else
+ NSLog("SBPlatform: \(str)")
+ #endif
+ }
+ }
+
+ /// returns the current thread name
+ class func threadName() -> String {
+
+ #if os(Linux)
+ // on 9/30/2016 not yet implemented in server-side Swift:
+ // > import Foundation
+ // > Thread.isMainThread
+ return ""
+ #else
+ if Thread.isMainThread {
+ return ""
+ } else {
+ let threadName = Thread.current.name
+ if let threadName = threadName, !threadName.isEmpty {
+ return threadName
+ } else {
+ return String(format: "%p", Thread.current)
+ }
+ }
+ #endif
+ }
+}
--- /dev/null
+//
+// SwiftyBeaver.swift
+// SwiftyBeaver
+//
+// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
+// Copyright © 2015 Sebastian Kreutzberger
+// Some rights reserved: http://opensource.org/licenses/MIT
+//
+
+import Foundation
+
+open class SwiftyBeaver {
+
+ /// version string of framework
+ public static let version = "1.8.4" // UPDATE ON RELEASE!
+ /// build number of framework
+ public static let build = 1840 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
+
+ public enum Level: Int {
+ case verbose = 0
+ case debug = 1
+ case info = 2
+ case warning = 3
+ case error = 4
+ }
+
+ // a set of active destinations
+ public private(set) static var destinations = Set<BaseDestination>()
+
+ // MARK: Destination Handling
+
+ /// returns boolean about success
+ @discardableResult
+ open class func addDestination(_ destination: BaseDestination) -> Bool {
+ if destinations.contains(destination) {
+ return false
+ }
+ destinations.insert(destination)
+ return true
+ }
+
+ /// returns boolean about success
+ @discardableResult
+ open class func removeDestination(_ destination: BaseDestination) -> Bool {
+ if destinations.contains(destination) == false {
+ return false
+ }
+ destinations.remove(destination)
+ return true
+ }
+
+ /// if you need to start fresh
+ open class func removeAllDestinations() {
+ destinations.removeAll()
+ }
+
+ /// returns the amount of destinations
+ open class func countDestinations() -> Int {
+ return destinations.count
+ }
+
+ /// returns the current thread name
+ class func threadName() -> String {
+
+ #if os(Linux)
+ // on 9/30/2016 not yet implemented in server-side Swift:
+ // > import Foundation
+ // > Thread.isMainThread
+ return ""
+ #else
+ if Thread.isMainThread {
+ return ""
+ } else {
+ let name = __dispatch_queue_get_label(nil)
+ return String(cString: name, encoding: .utf8) ?? Thread.current.description
+ }
+ #endif
+ }
+
+ // MARK: Levels
+
+ /// log something generally unimportant (lowest priority)
+ open class func verbose(_ message: @autoclosure () -> Any, _
+ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context)
+ #else
+ custom(level: .verbose, message: message, file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// log something which help during debugging (low priority)
+ open class func debug(_ message: @autoclosure () -> Any, _
+ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ custom(level: .debug, message: message(), file: file, function: function, line: line, context: context)
+ #else
+ custom(level: .debug, message: message, file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// log something which you are really interested but which is not an issue or error (normal priority)
+ open class func info(_ message: @autoclosure () -> Any, _
+ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ custom(level: .info, message: message(), file: file, function: function, line: line, context: context)
+ #else
+ custom(level: .info, message: message, file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// log something which may cause big trouble soon (high priority)
+ open class func warning(_ message: @autoclosure () -> Any, _
+ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ custom(level: .warning, message: message(), file: file, function: function, line: line, context: context)
+ #else
+ custom(level: .warning, message: message, file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// log something which will keep you awake at night (highest priority)
+ open class func error(_ message: @autoclosure () -> Any, _
+ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ custom(level: .error, message: message(), file: file, function: function, line: line, context: context)
+ #else
+ custom(level: .error, message: message, file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// custom logging to manually adjust values, should just be used by other frameworks
+ public class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
+ file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
+ #if swift(>=5)
+ dispatch_send(level: level, message: message(), thread: threadName(),
+ file: file, function: function, line: line, context: context)
+ #else
+ dispatch_send(level: level, message: message, thread: threadName(),
+ file: file, function: function, line: line, context: context)
+ #endif
+ }
+
+ /// internal helper which dispatches send to dedicated queue if minLevel is ok
+ class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
+ thread: String, file: String, function: String, line: Int, context: Any?) {
+ var resolvedMessage: String?
+ for dest in destinations {
+
+ guard let queue = dest.queue else {
+ continue
+ }
+
+ resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage
+ if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) {
+ // try to convert msg object to String and put it on queue
+ let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage!
+ let f = stripParams(function: function)
+
+ if dest.asynchronously {
+ queue.async {
+ _ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
+ }
+ } else {
+ queue.sync {
+ _ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ DEPRECATED & NEEDS COMPLETE REWRITE DUE TO SWIFT 3 AND GENERAL INCORRECT LOGIC
+ Flush all destinations to make sure all logging messages have been written out
+ Returns after all messages flushed or timeout seconds
+
+ - returns: true if all messages flushed, false if timeout or error occurred
+ */
+ public class func flush(secondTimeout: Int64) -> Bool {
+
+ /*
+ guard let grp = dispatch_group_create() else { return false }
+ for dest in destinations {
+ if let queue = dest.queue {
+ dispatch_group_enter(grp)
+ queue.asynchronously(execute: {
+ dest.flush()
+ grp.leave()
+ })
+ }
+ }
+ let waitUntil = DispatchTime.now(dispatch_time_t(DISPATCH_TIME_NOW), secondTimeout * 1000000000)
+ return dispatch_group_wait(grp, waitUntil) == 0
+ */
+ return true
+ }
+
+ /// removes the parameters from a function because it looks weird with a single param
+ class func stripParams(function: String) -> String {
+ var f = function
+ if let indexOfBrace = f.find("(") {
+ #if swift(>=4.0)
+ f = String(f[..<indexOfBrace])
+ #else
+ f = f.substring(to: indexOfBrace)
+ #endif
+ }
+ f += "()"
+ return f
+ }
+}
--- /dev/null
+// SwiftyJSON.swift
+//
+// Copyright (c) 2014 - 2017 Ruoyu Fu, Pinglin Tang
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import Foundation
+
+// MARK: - Error
+// swiftlint:disable line_length
+public enum SwiftyJSONError: Int, Swift.Error {
+ case unsupportedType = 999
+ case indexOutOfBounds = 900
+ case elementTooDeep = 902
+ case wrongType = 901
+ case notExist = 500
+ case invalidJSON = 490
+}
+
+extension SwiftyJSONError: CustomNSError {
+
+ /// return the error domain of SwiftyJSONError
+ public static var errorDomain: String { return "com.swiftyjson.SwiftyJSON" }
+
+ /// return the error code of SwiftyJSONError
+ public var errorCode: Int { return self.rawValue }
+
+ /// return the userInfo of SwiftyJSONError
+ public var errorUserInfo: [String: Any] {
+ switch self {
+ case .unsupportedType:
+ return [NSLocalizedDescriptionKey: "It is an unsupported type."]
+ case .indexOutOfBounds:
+ return [NSLocalizedDescriptionKey: "Array Index is out of bounds."]
+ case .wrongType:
+ return [NSLocalizedDescriptionKey: "Couldn't merge, because the JSONs differ in type on top level."]
+ case .notExist:
+ return [NSLocalizedDescriptionKey: "Dictionary key does not exist."]
+ case .invalidJSON:
+ return [NSLocalizedDescriptionKey: "JSON is invalid."]
+ case .elementTooDeep:
+ return [NSLocalizedDescriptionKey: "Element too deep. Increase maxObjectDepth and make sure there is no reference loop."]
+ }
+ }
+}
+
+// MARK: - JSON Type
+
+/**
+JSON's type definitions.
+
+See http://www.json.org
+*/
+public enum Type: Int {
+ case number
+ case string
+ case bool
+ case array
+ case dictionary
+ case null
+ case unknown
+}
+
+// MARK: - JSON Base
+
+public struct JSON {
+
+ /**
+ Creates a JSON using the data.
+
+ - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary
+ - parameter opt: The JSON serialization reading options. `[]` by default.
+
+ - returns: The created JSON
+ */
+ public init(data: Data, options opt: JSONSerialization.ReadingOptions = []) throws {
+ let object: Any = try JSONSerialization.jsonObject(with: data, options: opt)
+ self.init(jsonObject: object)
+ }
+
+ /**
+ Creates a JSON object
+ - note: this does not parse a `String` into JSON, instead use `init(parseJSON: String)`
+
+ - parameter object: the object
+
+ - returns: the created JSON object
+ */
+ public init(_ object: Any) {
+ switch object {
+ case let object as Data:
+ do {
+ try self.init(data: object)
+ } catch {
+ self.init(jsonObject: NSNull())
+ }
+ default:
+ self.init(jsonObject: object)
+ }
+ }
+
+ /**
+ Parses the JSON string into a JSON object
+
+ - parameter json: the JSON string
+
+ - returns: the created JSON object
+ */
+ public init(parseJSON jsonString: String) {
+ if let data = jsonString.data(using: .utf8) {
+ self.init(data)
+ } else {
+ self.init(NSNull())
+ }
+ }
+
+ /**
+ Creates a JSON using the object.
+
+ - parameter jsonObject: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity.
+
+ - returns: The created JSON
+ */
+ fileprivate init(jsonObject: Any) {
+ object = jsonObject
+ }
+
+ /**
+ Merges another JSON into this JSON, whereas primitive values which are not present in this JSON are getting added,
+ present values getting overwritten, array values getting appended and nested JSONs getting merged the same way.
+
+ - parameter other: The JSON which gets merged into this JSON
+
+ - throws `ErrorWrongType` if the other JSONs differs in type on the top level.
+ */
+ public mutating func merge(with other: JSON) throws {
+ try self.merge(with: other, typecheck: true)
+ }
+
+ /**
+ Merges another JSON into this JSON and returns a new JSON, whereas primitive values which are not present in this JSON are getting added,
+ present values getting overwritten, array values getting appended and nested JSONS getting merged the same way.
+
+ - parameter other: The JSON which gets merged into this JSON
+
+ - throws `ErrorWrongType` if the other JSONs differs in type on the top level.
+
+ - returns: New merged JSON
+ */
+ public func merged(with other: JSON) throws -> JSON {
+ var merged = self
+ try merged.merge(with: other, typecheck: true)
+ return merged
+ }
+
+ /**
+ Private woker function which does the actual merging
+ Typecheck is set to true for the first recursion level to prevent total override of the source JSON
+ */
+ fileprivate mutating func merge(with other: JSON, typecheck: Bool) throws {
+ if type == other.type {
+ switch type {
+ case .dictionary:
+ for (key, _) in other {
+ try self[key].merge(with: other[key], typecheck: false)
+ }
+ case .array:
+ self = JSON(arrayValue + other.arrayValue)
+ default:
+ self = other
+ }
+ } else {
+ if typecheck {
+ throw SwiftyJSONError.wrongType
+ } else {
+ self = other
+ }
+ }
+ }
+
+ /// Private object
+ fileprivate var rawArray: [Any] = []
+ fileprivate var rawDictionary: [String: Any] = [:]
+ fileprivate var rawString: String = ""
+ fileprivate var rawNumber: NSNumber = 0
+ fileprivate var rawNull: NSNull = NSNull()
+ fileprivate var rawBool: Bool = false
+
+ /// JSON type, fileprivate setter
+ public fileprivate(set) var type: Type = .null
+
+ /// Error in JSON, fileprivate setter
+ public fileprivate(set) var error: SwiftyJSONError?
+
+ /// Object in JSON
+ public var object: Any {
+ get {
+ switch type {
+ case .array: return rawArray
+ case .dictionary: return rawDictionary
+ case .string: return rawString
+ case .number: return rawNumber
+ case .bool: return rawBool
+ default: return rawNull
+ }
+ }
+ set {
+ error = nil
+ switch unwrap(newValue) {
+ case let number as NSNumber:
+ if number.isBool {
+ type = .bool
+ rawBool = number.boolValue
+ } else {
+ type = .number
+ rawNumber = number
+ }
+ case let string as String:
+ type = .string
+ rawString = string
+ case _ as NSNull:
+ type = .null
+ case nil:
+ type = .null
+ case let array as [Any]:
+ type = .array
+ rawArray = array
+ case let dictionary as [String: Any]:
+ type = .dictionary
+ rawDictionary = dictionary
+ default:
+ type = .unknown
+ error = SwiftyJSONError.unsupportedType
+ }
+ }
+ }
+
+ /// The static null JSON
+ @available(*, unavailable, renamed:"null")
+ public static var nullJSON: JSON { return null }
+ public static var null: JSON { return JSON(NSNull()) }
+}
+
+/// Private method to unwarp an object recursively
+private func unwrap(_ object: Any) -> Any {
+ switch object {
+ case let json as JSON:
+ return unwrap(json.object)
+ case let array as [Any]:
+ return array.map(unwrap)
+ case let dictionary as [String: Any]:
+ var d = dictionary
+ dictionary.forEach { pair in
+ d[pair.key] = unwrap(pair.value)
+ }
+ return d
+ default:
+ return object
+ }
+}
+
+public enum Index<T: Any>: Comparable {
+ case array(Int)
+ case dictionary(DictionaryIndex<String, T>)
+ case null
+
+ static public func == (lhs: Index, rhs: Index) -> Bool {
+ switch (lhs, rhs) {
+ case (.array(let left), .array(let right)): return left == right
+ case (.dictionary(let left), .dictionary(let right)): return left == right
+ case (.null, .null): return true
+ default: return false
+ }
+ }
+
+ static public func < (lhs: Index, rhs: Index) -> Bool {
+ switch (lhs, rhs) {
+ case (.array(let left), .array(let right)): return left < right
+ case (.dictionary(let left), .dictionary(let right)): return left < right
+ default: return false
+ }
+ }
+}
+
+public typealias JSONIndex = Index<JSON>
+public typealias JSONRawIndex = Index<Any>
+
+extension JSON: Swift.Collection {
+
+ public typealias Index = JSONRawIndex
+
+ public var startIndex: Index {
+ switch type {
+ case .array: return .array(rawArray.startIndex)
+ case .dictionary: return .dictionary(rawDictionary.startIndex)
+ default: return .null
+ }
+ }
+
+ public var endIndex: Index {
+ switch type {
+ case .array: return .array(rawArray.endIndex)
+ case .dictionary: return .dictionary(rawDictionary.endIndex)
+ default: return .null
+ }
+ }
+
+ public func index(after i: Index) -> Index {
+ switch i {
+ case .array(let idx): return .array(rawArray.index(after: idx))
+ case .dictionary(let idx): return .dictionary(rawDictionary.index(after: idx))
+ default: return .null
+ }
+ }
+
+ public subscript (position: Index) -> (String, JSON) {
+ switch position {
+ case .array(let idx): return (String(idx), JSON(rawArray[idx]))
+ case .dictionary(let idx): return (rawDictionary[idx].key, JSON(rawDictionary[idx].value))
+ default: return ("", JSON.null)
+ }
+ }
+}
+
+// MARK: - Subscript
+
+/**
+ * To mark both String and Int can be used in subscript.
+ */
+public enum JSONKey {
+ case index(Int)
+ case key(String)
+}
+
+public protocol JSONSubscriptType {
+ var jsonKey: JSONKey { get }
+}
+
+extension Int: JSONSubscriptType {
+ public var jsonKey: JSONKey {
+ return JSONKey.index(self)
+ }
+}
+
+extension String: JSONSubscriptType {
+ public var jsonKey: JSONKey {
+ return JSONKey.key(self)
+ }
+}
+
+extension JSON {
+
+ /// If `type` is `.array`, return json whose object is `array[index]`, otherwise return null json with error.
+ fileprivate subscript(index index: Int) -> JSON {
+ get {
+ if type != .array {
+ var r = JSON.null
+ r.error = self.error ?? SwiftyJSONError.wrongType
+ return r
+ } else if rawArray.indices.contains(index) {
+ return JSON(rawArray[index])
+ } else {
+ var r = JSON.null
+ r.error = SwiftyJSONError.indexOutOfBounds
+ return r
+ }
+ }
+ set {
+ if type == .array &&
+ rawArray.indices.contains(index) &&
+ newValue.error == nil {
+ rawArray[index] = newValue.object
+ }
+ }
+ }
+
+ /// If `type` is `.dictionary`, return json whose object is `dictionary[key]` , otherwise return null json with error.
+ fileprivate subscript(key key: String) -> JSON {
+ get {
+ var r = JSON.null
+ if type == .dictionary {
+ if let o = rawDictionary[key] {
+ r = JSON(o)
+ } else {
+ r.error = SwiftyJSONError.notExist
+ }
+ } else {
+ r.error = self.error ?? SwiftyJSONError.wrongType
+ }
+ return r
+ }
+ set {
+ if type == .dictionary && newValue.error == nil {
+ rawDictionary[key] = newValue.object
+ }
+ }
+ }
+
+ /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`.
+ fileprivate subscript(sub sub: JSONSubscriptType) -> JSON {
+ get {
+ switch sub.jsonKey {
+ case .index(let index): return self[index: index]
+ case .key(let key): return self[key: key]
+ }
+ }
+ set {
+ switch sub.jsonKey {
+ case .index(let index): self[index: index] = newValue
+ case .key(let key): self[key: key] = newValue
+ }
+ }
+ }
+
+ /**
+ Find a json in the complex data structures by using array of Int and/or String as path.
+
+ Example:
+
+ ```
+ let json = JSON[data]
+ let path = [9,"list","person","name"]
+ let name = json[path]
+ ```
+
+ The same as: let name = json[9]["list"]["person"]["name"]
+
+ - parameter path: The target json's path.
+
+ - returns: Return a json found by the path or a null json with error
+ */
+ public subscript(path: [JSONSubscriptType]) -> JSON {
+ get {
+ return path.reduce(self) { $0[sub: $1] }
+ }
+ set {
+ switch path.count {
+ case 0: return
+ case 1: self[sub:path[0]].object = newValue.object
+ default:
+ var aPath = path
+ aPath.remove(at: 0)
+ var nextJSON = self[sub: path[0]]
+ nextJSON[aPath] = newValue
+ self[sub: path[0]] = nextJSON
+ }
+ }
+ }
+
+ /**
+ Find a json in the complex data structures by using array of Int and/or String as path.
+
+ - parameter path: The target json's path. Example:
+
+ let name = json[9,"list","person","name"]
+
+ The same as: let name = json[9]["list"]["person"]["name"]
+
+ - returns: Return a json found by the path or a null json with error
+ */
+ public subscript(path: JSONSubscriptType...) -> JSON {
+ get {
+ return self[path]
+ }
+ set {
+ self[path] = newValue
+ }
+ }
+}
+
+// MARK: - LiteralConvertible
+
+extension JSON: Swift.ExpressibleByStringLiteral {
+
+ public init(stringLiteral value: StringLiteralType) {
+ self.init(value)
+ }
+
+ public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
+ self.init(value)
+ }
+
+ public init(unicodeScalarLiteral value: StringLiteralType) {
+ self.init(value)
+ }
+}
+
+extension JSON: Swift.ExpressibleByIntegerLiteral {
+
+ public init(integerLiteral value: IntegerLiteralType) {
+ self.init(value)
+ }
+}
+
+extension JSON: Swift.ExpressibleByBooleanLiteral {
+
+ public init(booleanLiteral value: BooleanLiteralType) {
+ self.init(value)
+ }
+}
+
+extension JSON: Swift.ExpressibleByFloatLiteral {
+
+ public init(floatLiteral value: FloatLiteralType) {
+ self.init(value)
+ }
+}
+
+extension JSON: Swift.ExpressibleByDictionaryLiteral {
+ public init(dictionaryLiteral elements: (String, Any)...) {
+ let dictionary = elements.reduce(into: [String: Any](), { $0[$1.0] = $1.1})
+ self.init(dictionary)
+ }
+}
+
+extension JSON: Swift.ExpressibleByArrayLiteral {
+
+ public init(arrayLiteral elements: Any...) {
+ self.init(elements)
+ }
+}
+
+// MARK: - Raw
+
+extension JSON: Swift.RawRepresentable {
+
+ public init?(rawValue: Any) {
+ if JSON(rawValue).type == .unknown {
+ return nil
+ } else {
+ self.init(rawValue)
+ }
+ }
+
+ public var rawValue: Any {
+ return object
+ }
+
+ public func rawData(options opt: JSONSerialization.WritingOptions = JSONSerialization.WritingOptions(rawValue: 0)) throws -> Data {
+ guard JSONSerialization.isValidJSONObject(object) else {
+ throw SwiftyJSONError.invalidJSON
+ }
+
+ return try JSONSerialization.data(withJSONObject: object, options: opt)
+ }
+
+ public func rawString(_ encoding: String.Encoding = .utf8, options opt: JSONSerialization.WritingOptions = .prettyPrinted) -> String? {
+ do {
+ return try _rawString(encoding, options: [.jsonSerialization: opt])
+ } catch {
+ print("Could not serialize object to JSON because:", error.localizedDescription)
+ return nil
+ }
+ }
+
+ public func rawString(_ options: [writingOptionsKeys: Any]) -> String? {
+ let encoding = options[.encoding] as? String.Encoding ?? String.Encoding.utf8
+ let maxObjectDepth = options[.maxObjextDepth] as? Int ?? 10
+ do {
+ return try _rawString(encoding, options: options, maxObjectDepth: maxObjectDepth)
+ } catch {
+ print("Could not serialize object to JSON because:", error.localizedDescription)
+ return nil
+ }
+ }
+
+ fileprivate func _rawString(_ encoding: String.Encoding = .utf8, options: [writingOptionsKeys: Any], maxObjectDepth: Int = 10) throws -> String? {
+ guard maxObjectDepth > 0 else { throw SwiftyJSONError.invalidJSON }
+ switch type {
+ case .dictionary:
+ do {
+ if !(options[.castNilToNSNull] as? Bool ?? false) {
+ let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted
+ let data = try rawData(options: jsonOption)
+ return String(data: data, encoding: encoding)
+ }
+
+ guard let dict = object as? [String: Any?] else {
+ return nil
+ }
+ let body = try dict.keys.map { key throws -> String in
+ guard let value = dict[key] else {
+ return "\"\(key)\": null"
+ }
+ guard let unwrappedValue = value else {
+ return "\"\(key)\": null"
+ }
+
+ let nestedValue = JSON(unwrappedValue)
+ guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else {
+ throw SwiftyJSONError.elementTooDeep
+ }
+ if nestedValue.type == .string {
+ return "\"\(key)\": \"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""
+ } else {
+ return "\"\(key)\": \(nestedString)"
+ }
+ }
+
+ return "{\(body.joined(separator: ","))}"
+ } catch _ {
+ return nil
+ }
+ case .array:
+ do {
+ if !(options[.castNilToNSNull] as? Bool ?? false) {
+ let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted
+ let data = try rawData(options: jsonOption)
+ return String(data: data, encoding: encoding)
+ }
+
+ guard let array = object as? [Any?] else {
+ return nil
+ }
+ let body = try array.map { value throws -> String in
+ guard let unwrappedValue = value else {
+ return "null"
+ }
+
+ let nestedValue = JSON(unwrappedValue)
+ guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else {
+ throw SwiftyJSONError.invalidJSON
+ }
+ if nestedValue.type == .string {
+ return "\"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""
+ } else {
+ return nestedString
+ }
+ }
+
+ return "[\(body.joined(separator: ","))]"
+ } catch _ {
+ return nil
+ }
+ case .string: return rawString
+ case .number: return rawNumber.stringValue
+ case .bool: return rawBool.description
+ case .null: return "null"
+ default: return nil
+ }
+ }
+}
+
+// MARK: - Printable, DebugPrintable
+
+extension JSON: Swift.CustomStringConvertible, Swift.CustomDebugStringConvertible {
+
+ public var description: String {
+ return rawString(options: .prettyPrinted) ?? "unknown"
+ }
+
+ public var debugDescription: String {
+ return description
+ }
+}
+
+// MARK: - Array
+
+extension JSON {
+
+ //Optional [JSON]
+ public var array: [JSON]? {
+ return type == .array ? rawArray.map { JSON($0) } : nil
+ }
+
+ //Non-optional [JSON]
+ public var arrayValue: [JSON] {
+ return self.array ?? []
+ }
+
+ //Optional [Any]
+ public var arrayObject: [Any]? {
+ get {
+ switch type {
+ case .array: return rawArray
+ default: return nil
+ }
+ }
+ set {
+ self.object = newValue ?? NSNull()
+ }
+ }
+}
+
+// MARK: - Dictionary
+
+extension JSON {
+
+ //Optional [String : JSON]
+ public var dictionary: [String: JSON]? {
+ if type == .dictionary {
+ var d = [String: JSON](minimumCapacity: rawDictionary.count)
+ rawDictionary.forEach { pair in
+ d[pair.key] = JSON(pair.value)
+ }
+ return d
+ } else {
+ return nil
+ }
+ }
+
+ //Non-optional [String : JSON]
+ public var dictionaryValue: [String: JSON] {
+ return dictionary ?? [:]
+ }
+
+ //Optional [String : Any]
+
+ public var dictionaryObject: [String: Any]? {
+ get {
+ switch type {
+ case .dictionary: return rawDictionary
+ default: return nil
+ }
+ }
+ set {
+ object = newValue ?? NSNull()
+ }
+ }
+}
+
+// MARK: - Bool
+
+extension JSON { // : Swift.Bool
+
+ //Optional bool
+ public var bool: Bool? {
+ get {
+ switch type {
+ case .bool: return rawBool
+ default: return nil
+ }
+ }
+ set {
+ object = newValue ?? NSNull()
+ }
+ }
+
+ //Non-optional bool
+ public var boolValue: Bool {
+ get {
+ switch type {
+ case .bool: return rawBool
+ case .number: return rawNumber.boolValue
+ case .string: return ["true", "y", "t", "yes", "1"].contains { rawString.caseInsensitiveCompare($0) == .orderedSame }
+ default: return false
+ }
+ }
+ set {
+ object = newValue
+ }
+ }
+}
+
+// MARK: - String
+
+extension JSON {
+
+ //Optional string
+ public var string: String? {
+ get {
+ switch type {
+ case .string: return object as? String
+ default: return nil
+ }
+ }
+ set {
+ object = newValue ?? NSNull()
+ }
+ }
+
+ //Non-optional string
+ public var stringValue: String {
+ get {
+ switch type {
+ case .string: return object as? String ?? ""
+ case .number: return rawNumber.stringValue
+ case .bool: return (object as? Bool).map { String($0) } ?? ""
+ default: return ""
+ }
+ }
+ set {
+ object = newValue
+ }
+ }
+}
+
+// MARK: - Number
+
+extension JSON {
+
+ //Optional number
+ public var number: NSNumber? {
+ get {
+ switch type {
+ case .number: return rawNumber
+ case .bool: return NSNumber(value: rawBool ? 1 : 0)
+ default: return nil
+ }
+ }
+ set {
+ object = newValue ?? NSNull()
+ }
+ }
+
+ //Non-optional number
+ public var numberValue: NSNumber {
+ get {
+ switch type {
+ case .string:
+ let decimal = NSDecimalNumber(string: object as? String)
+ return decimal == .notANumber ? .zero : decimal
+ case .number: return object as? NSNumber ?? NSNumber(value: 0)
+ case .bool: return NSNumber(value: rawBool ? 1 : 0)
+ default: return NSNumber(value: 0.0)
+ }
+ }
+ set {
+ object = newValue
+ }
+ }
+}
+
+// MARK: - Null
+
+extension JSON {
+
+ public var null: NSNull? {
+ set {
+ object = NSNull()
+ }
+ get {
+ switch type {
+ case .null: return rawNull
+ default: return nil
+ }
+ }
+ }
+ public func exists() -> Bool {
+ if let errorValue = error, (400...1000).contains(errorValue.errorCode) {
+ return false
+ }
+ return true
+ }
+}
+
+// MARK: - URL
+
+extension JSON {
+
+ //Optional URL
+ public var url: URL? {
+ get {
+ switch type {
+ case .string:
+ // Check for existing percent escapes first to prevent double-escaping of % character
+ if rawString.range(of: "%[0-9A-Fa-f]{2}", options: .regularExpression, range: nil, locale: nil) != nil {
+ return Foundation.URL(string: rawString)
+ } else if let encodedString_ = rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
+ // We have to use `Foundation.URL` otherwise it conflicts with the variable name.
+ return Foundation.URL(string: encodedString_)
+ } else {
+ return nil
+ }
+ default:
+ return nil
+ }
+ }
+ set {
+ object = newValue?.absoluteString ?? NSNull()
+ }
+ }
+}
+
+// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64
+
+extension JSON {
+
+ public var double: Double? {
+ get {
+ return number?.doubleValue
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var doubleValue: Double {
+ get {
+ return numberValue.doubleValue
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var float: Float? {
+ get {
+ return number?.floatValue
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var floatValue: Float {
+ get {
+ return numberValue.floatValue
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var int: Int? {
+ get {
+ return number?.intValue
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var intValue: Int {
+ get {
+ return numberValue.intValue
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var uInt: UInt? {
+ get {
+ return number?.uintValue
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var uIntValue: UInt {
+ get {
+ return numberValue.uintValue
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var int8: Int8? {
+ get {
+ return number?.int8Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: Int(newValue))
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var int8Value: Int8 {
+ get {
+ return numberValue.int8Value
+ }
+ set {
+ object = NSNumber(value: Int(newValue))
+ }
+ }
+
+ public var uInt8: UInt8? {
+ get {
+ return number?.uint8Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var uInt8Value: UInt8 {
+ get {
+ return numberValue.uint8Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var int16: Int16? {
+ get {
+ return number?.int16Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var int16Value: Int16 {
+ get {
+ return numberValue.int16Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var uInt16: UInt16? {
+ get {
+ return number?.uint16Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var uInt16Value: UInt16 {
+ get {
+ return numberValue.uint16Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var int32: Int32? {
+ get {
+ return number?.int32Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var int32Value: Int32 {
+ get {
+ return numberValue.int32Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var uInt32: UInt32? {
+ get {
+ return number?.uint32Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var uInt32Value: UInt32 {
+ get {
+ return numberValue.uint32Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var int64: Int64? {
+ get {
+ return number?.int64Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var int64Value: Int64 {
+ get {
+ return numberValue.int64Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+
+ public var uInt64: UInt64? {
+ get {
+ return number?.uint64Value
+ }
+ set {
+ if let newValue = newValue {
+ object = NSNumber(value: newValue)
+ } else {
+ object = NSNull()
+ }
+ }
+ }
+
+ public var uInt64Value: UInt64 {
+ get {
+ return numberValue.uint64Value
+ }
+ set {
+ object = NSNumber(value: newValue)
+ }
+ }
+}
+
+// MARK: - Comparable
+
+extension JSON: Swift.Comparable {}
+
+public func == (lhs: JSON, rhs: JSON) -> Bool {
+
+ switch (lhs.type, rhs.type) {
+ case (.number, .number): return lhs.rawNumber == rhs.rawNumber
+ case (.string, .string): return lhs.rawString == rhs.rawString
+ case (.bool, .bool): return lhs.rawBool == rhs.rawBool
+ case (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+ case (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+ case (.null, .null): return true
+ default: return false
+ }
+}
+
+public func <= (lhs: JSON, rhs: JSON) -> Bool {
+
+ switch (lhs.type, rhs.type) {
+ case (.number, .number): return lhs.rawNumber <= rhs.rawNumber
+ case (.string, .string): return lhs.rawString <= rhs.rawString
+ case (.bool, .bool): return lhs.rawBool == rhs.rawBool
+ case (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+ case (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+ case (.null, .null): return true
+ default: return false
+ }
+}
+
+public func >= (lhs: JSON, rhs: JSON) -> Bool {
+
+ switch (lhs.type, rhs.type) {
+ case (.number, .number): return lhs.rawNumber >= rhs.rawNumber
+ case (.string, .string): return lhs.rawString >= rhs.rawString
+ case (.bool, .bool): return lhs.rawBool == rhs.rawBool
+ case (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+ case (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+ case (.null, .null): return true
+ default: return false
+ }
+}
+
+public func > (lhs: JSON, rhs: JSON) -> Bool {
+
+ switch (lhs.type, rhs.type) {
+ case (.number, .number): return lhs.rawNumber > rhs.rawNumber
+ case (.string, .string): return lhs.rawString > rhs.rawString
+ default: return false
+ }
+}
+
+public func < (lhs: JSON, rhs: JSON) -> Bool {
+
+ switch (lhs.type, rhs.type) {
+ case (.number, .number): return lhs.rawNumber < rhs.rawNumber
+ case (.string, .string): return lhs.rawString < rhs.rawString
+ default: return false
+ }
+}
+
+private let trueNumber = NSNumber(value: true)
+private let falseNumber = NSNumber(value: false)
+private let trueObjCType = String(cString: trueNumber.objCType)
+private let falseObjCType = String(cString: falseNumber.objCType)
+
+// MARK: - NSNumber: Comparable
+
+extension NSNumber {
+ fileprivate var isBool: Bool {
+ let objCType = String(cString: self.objCType)
+ if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) {
+ return true
+ } else {
+ return false
+ }
+ }
+}
+
+func == (lhs: NSNumber, rhs: NSNumber) -> Bool {
+ switch (lhs.isBool, rhs.isBool) {
+ case (false, true): return false
+ case (true, false): return false
+ default: return lhs.compare(rhs) == .orderedSame
+ }
+}
+
+func != (lhs: NSNumber, rhs: NSNumber) -> Bool {
+ return !(lhs == rhs)
+}
+
+func < (lhs: NSNumber, rhs: NSNumber) -> Bool {
+
+ switch (lhs.isBool, rhs.isBool) {
+ case (false, true): return false
+ case (true, false): return false
+ default: return lhs.compare(rhs) == .orderedAscending
+ }
+}
+
+func > (lhs: NSNumber, rhs: NSNumber) -> Bool {
+
+ switch (lhs.isBool, rhs.isBool) {
+ case (false, true): return false
+ case (true, false): return false
+ default: return lhs.compare(rhs) == ComparisonResult.orderedDescending
+ }
+}
+
+func <= (lhs: NSNumber, rhs: NSNumber) -> Bool {
+
+ switch (lhs.isBool, rhs.isBool) {
+ case (false, true): return false
+ case (true, false): return false
+ default: return lhs.compare(rhs) != .orderedDescending
+ }
+}
+
+func >= (lhs: NSNumber, rhs: NSNumber) -> Bool {
+
+ switch (lhs.isBool, rhs.isBool) {
+ case (false, true): return false
+ case (true, false): return false
+ default: return lhs.compare(rhs) != .orderedAscending
+ }
+}
+
+public enum writingOptionsKeys {
+ case jsonSerialization
+ case castNilToNSNull
+ case maxObjextDepth
+ case encoding
+}
+
+// MARK: - JSON: Codable
+extension JSON: Codable {
+ private static var codableTypes: [Codable.Type] {
+ return [
+ Bool.self,
+ Int.self,
+ Int8.self,
+ Int16.self,
+ Int32.self,
+ Int64.self,
+ UInt.self,
+ UInt8.self,
+ UInt16.self,
+ UInt32.self,
+ UInt64.self,
+ Double.self,
+ String.self,
+ [JSON].self,
+ [String: JSON].self
+ ]
+ }
+ public init(from decoder: Decoder) throws {
+ var object: Any?
+
+ if let container = try? decoder.singleValueContainer(), !container.decodeNil() {
+ for type in JSON.codableTypes {
+ if object != nil {
+ break
+ }
+ // try to decode value
+ switch type {
+ case let boolType as Bool.Type:
+ object = try? container.decode(boolType)
+ case let intType as Int.Type:
+ object = try? container.decode(intType)
+ case let int8Type as Int8.Type:
+ object = try? container.decode(int8Type)
+ case let int32Type as Int32.Type:
+ object = try? container.decode(int32Type)
+ case let int64Type as Int64.Type:
+ object = try? container.decode(int64Type)
+ case let uintType as UInt.Type:
+ object = try? container.decode(uintType)
+ case let uint8Type as UInt8.Type:
+ object = try? container.decode(uint8Type)
+ case let uint16Type as UInt16.Type:
+ object = try? container.decode(uint16Type)
+ case let uint32Type as UInt32.Type:
+ object = try? container.decode(uint32Type)
+ case let uint64Type as UInt64.Type:
+ object = try? container.decode(uint64Type)
+ case let doubleType as Double.Type:
+ object = try? container.decode(doubleType)
+ case let stringType as String.Type:
+ object = try? container.decode(stringType)
+ case let jsonValueArrayType as [JSON].Type:
+ object = try? container.decode(jsonValueArrayType)
+ case let jsonValueDictType as [String: JSON].Type:
+ object = try? container.decode(jsonValueDictType)
+ default:
+ break
+ }
+ }
+ }
+ self.init(object ?? NSNull())
+ }
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ if object is NSNull {
+ try container.encodeNil()
+ return
+ }
+ switch object {
+ case let intValue as Int:
+ try container.encode(intValue)
+ case let int8Value as Int8:
+ try container.encode(int8Value)
+ case let int32Value as Int32:
+ try container.encode(int32Value)
+ case let int64Value as Int64:
+ try container.encode(int64Value)
+ case let uintValue as UInt:
+ try container.encode(uintValue)
+ case let uint8Value as UInt8:
+ try container.encode(uint8Value)
+ case let uint16Value as UInt16:
+ try container.encode(uint16Value)
+ case let uint32Value as UInt32:
+ try container.encode(uint32Value)
+ case let uint64Value as UInt64:
+ try container.encode(uint64Value)
+ case let doubleValue as Double:
+ try container.encode(doubleValue)
+ case let boolValue as Bool:
+ try container.encode(boolValue)
+ case let stringValue as String:
+ try container.encode(stringValue)
+ case is [Any]:
+ let jsonValueArray = array ?? []
+ try container.encode(jsonValueArray)
+ case is [String: Any]:
+ let jsonValueDictValue = dictionary ?? [:]
+ try container.encode(jsonValueDictValue)
+ default:
+ break
+ }
+ }
+}
--- /dev/null
+-----BEGIN CERTIFICATE-----\r
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\r
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\r
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\r
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\r
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\r
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\r
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\r
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\r
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\r
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\r
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\r
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\r
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\r
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\r
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\r
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\r
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\r
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\r
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\r
+-----END CERTIFICATE-----
\ No newline at end of file
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 18BF1DCF2B81AB7C00B8D90F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DCE2B81AB7C00B8D90F /* AppDelegate.m */; };
+ 18BF1DD22B81AB7C00B8D90F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DD12B81AB7C00B8D90F /* ViewController.m */; };
+ 18BF1DD42B81AB7F00B8D90F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18BF1DD32B81AB7F00B8D90F /* Assets.xcassets */; };
+ 18BF1DD72B81AB7F00B8D90F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 18BF1DD52B81AB7F00B8D90F /* Main.storyboard */; };
+ 18BF1DD92B81AB7F00B8D90F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DD82B81AB7F00B8D90F /* main.m */; };
+ 18BF1DE42B81AB8000B8D90F /* MouseSdkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DE32B81AB8000B8D90F /* MouseSdkTests.m */; };
+ 18BF1DEE2B81AB8000B8D90F /* MouseSdkUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DED2B81AB8000B8D90F /* MouseSdkUITests.m */; };
+ 18BF1DF02B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1DEF2B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m */; };
+ 18BF1E052B81E18800B8D90F /* ConnectKitVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E042B81E18800B8D90F /* ConnectKitVC.swift */; };
+ 18BF1E222B83066200B8D90F /* BleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E212B83066200B8D90F /* BleManager.m */; };
+ 18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 18BF1E752B832D2E00B8D90F /* ScanVC.m */; };
+ 8A6657BD1406314D4CBB7CEF /* libPods-MouseSdk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0311DF3AD67ED792549B96BD /* libPods-MouseSdk.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 18BF1DE02B81AB8000B8D90F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 18BF1DC22B81AB7C00B8D90F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 18BF1DC92B81AB7C00B8D90F;
+ remoteInfo = MouseSdk;
+ };
+ 18BF1DEA2B81AB8000B8D90F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 18BF1DC22B81AB7C00B8D90F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 18BF1DC92B81AB7C00B8D90F;
+ remoteInfo = MouseSdk;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 0311DF3AD67ED792549B96BD /* libPods-MouseSdk.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MouseSdk.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 18BF1DCA2B81AB7C00B8D90F /* MouseSdk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MouseSdk.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 18BF1DCD2B81AB7C00B8D90F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 18BF1DCE2B81AB7C00B8D90F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 18BF1DD02B81AB7C00B8D90F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+ 18BF1DD12B81AB7C00B8D90F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+ 18BF1DD32B81AB7F00B8D90F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 18BF1DD62B81AB7F00B8D90F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 18BF1DD82B81AB7F00B8D90F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 18BF1DDA2B81AB7F00B8D90F /* MouseSdk.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MouseSdk.entitlements; sourceTree = "<group>"; };
+ 18BF1DDF2B81AB8000B8D90F /* MouseSdkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MouseSdkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 18BF1DE32B81AB8000B8D90F /* MouseSdkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MouseSdkTests.m; sourceTree = "<group>"; };
+ 18BF1DE92B81AB8000B8D90F /* MouseSdkUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MouseSdkUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 18BF1DED2B81AB8000B8D90F /* MouseSdkUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MouseSdkUITests.m; sourceTree = "<group>"; };
+ 18BF1DEF2B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MouseSdkUITestsLaunchTests.m; sourceTree = "<group>"; };
+ 18BF1E032B81E18700B8D90F /* MouseSdk-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MouseSdk-Bridging-Header.h"; sourceTree = "<group>"; };
+ 18BF1E042B81E18800B8D90F /* ConnectKitVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectKitVC.swift; sourceTree = "<group>"; };
+ 18BF1E202B83066200B8D90F /* BleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BleManager.h; sourceTree = "<group>"; };
+ 18BF1E212B83066200B8D90F /* BleManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BleManager.m; sourceTree = "<group>"; };
+ 18BF1E742B832D2E00B8D90F /* ScanVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanVC.h; sourceTree = "<group>"; };
+ 18BF1E752B832D2E00B8D90F /* ScanVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanVC.m; sourceTree = "<group>"; };
+ 51DE1118FCBA78EBEE6FE2CD /* Pods-MouseSdk.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MouseSdk.release.xcconfig"; path = "Target Support Files/Pods-MouseSdk/Pods-MouseSdk.release.xcconfig"; sourceTree = "<group>"; };
+ 811936622867478806C1A8BB /* Pods-MouseSdk.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MouseSdk.debug.xcconfig"; path = "Target Support Files/Pods-MouseSdk/Pods-MouseSdk.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 18BF1DC72B81AB7C00B8D90F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8A6657BD1406314D4CBB7CEF /* libPods-MouseSdk.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DDC2B81AB8000B8D90F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DE62B81AB8000B8D90F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 18BF1DC12B81AB7C00B8D90F = {
+ isa = PBXGroup;
+ children = (
+ 18BF1DFE2B81E00800B8D90F /* framework */,
+ 18BF1DCC2B81AB7C00B8D90F /* MouseSdk */,
+ 18BF1DE22B81AB8000B8D90F /* MouseSdkTests */,
+ 18BF1DEC2B81AB8000B8D90F /* MouseSdkUITests */,
+ 18BF1DCB2B81AB7C00B8D90F /* Products */,
+ 723588BAA058E1012143D29F /* Pods */,
+ 543EC4E5F9FC310D6B850947 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ 18BF1DCB2B81AB7C00B8D90F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1DCA2B81AB7C00B8D90F /* MouseSdk.app */,
+ 18BF1DDF2B81AB8000B8D90F /* MouseSdkTests.xctest */,
+ 18BF1DE92B81AB8000B8D90F /* MouseSdkUITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 18BF1DCC2B81AB7C00B8D90F /* MouseSdk */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1E1F2B83064D00B8D90F /* Ble */,
+ 18BF1DCD2B81AB7C00B8D90F /* AppDelegate.h */,
+ 18BF1DCE2B81AB7C00B8D90F /* AppDelegate.m */,
+ 18BF1DD02B81AB7C00B8D90F /* ViewController.h */,
+ 18BF1DD12B81AB7C00B8D90F /* ViewController.m */,
+ 18BF1E742B832D2E00B8D90F /* ScanVC.h */,
+ 18BF1E752B832D2E00B8D90F /* ScanVC.m */,
+ 18BF1E042B81E18800B8D90F /* ConnectKitVC.swift */,
+ 18BF1DD32B81AB7F00B8D90F /* Assets.xcassets */,
+ 18BF1DD52B81AB7F00B8D90F /* Main.storyboard */,
+ 18BF1DD82B81AB7F00B8D90F /* main.m */,
+ 18BF1DDA2B81AB7F00B8D90F /* MouseSdk.entitlements */,
+ 18BF1E032B81E18700B8D90F /* MouseSdk-Bridging-Header.h */,
+ );
+ path = MouseSdk;
+ sourceTree = "<group>";
+ };
+ 18BF1DE22B81AB8000B8D90F /* MouseSdkTests */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1DE32B81AB8000B8D90F /* MouseSdkTests.m */,
+ );
+ path = MouseSdkTests;
+ sourceTree = "<group>";
+ };
+ 18BF1DEC2B81AB8000B8D90F /* MouseSdkUITests */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1DED2B81AB8000B8D90F /* MouseSdkUITests.m */,
+ 18BF1DEF2B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m */,
+ );
+ path = MouseSdkUITests;
+ sourceTree = "<group>";
+ };
+ 18BF1DFE2B81E00800B8D90F /* framework */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ path = framework;
+ sourceTree = "<group>";
+ };
+ 18BF1E1F2B83064D00B8D90F /* Ble */ = {
+ isa = PBXGroup;
+ children = (
+ 18BF1E202B83066200B8D90F /* BleManager.h */,
+ 18BF1E212B83066200B8D90F /* BleManager.m */,
+ );
+ path = Ble;
+ sourceTree = "<group>";
+ };
+ 543EC4E5F9FC310D6B850947 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 0311DF3AD67ED792549B96BD /* libPods-MouseSdk.a */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 723588BAA058E1012143D29F /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 811936622867478806C1A8BB /* Pods-MouseSdk.debug.xcconfig */,
+ 51DE1118FCBA78EBEE6FE2CD /* Pods-MouseSdk.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 18BF1DC92B81AB7C00B8D90F /* MouseSdk */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 18BF1DF32B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdk" */;
+ buildPhases = (
+ 9A36963E54431D0A5AE88E2F /* [CP] Check Pods Manifest.lock */,
+ 18BF1DC62B81AB7C00B8D90F /* Sources */,
+ 18BF1DC72B81AB7C00B8D90F /* Frameworks */,
+ 18BF1DC82B81AB7C00B8D90F /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = MouseSdk;
+ productName = MouseSdk;
+ productReference = 18BF1DCA2B81AB7C00B8D90F /* MouseSdk.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 18BF1DDE2B81AB8000B8D90F /* MouseSdkTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 18BF1DF62B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdkTests" */;
+ buildPhases = (
+ 18BF1DDB2B81AB8000B8D90F /* Sources */,
+ 18BF1DDC2B81AB8000B8D90F /* Frameworks */,
+ 18BF1DDD2B81AB8000B8D90F /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 18BF1DE12B81AB8000B8D90F /* PBXTargetDependency */,
+ );
+ name = MouseSdkTests;
+ productName = MouseSdkTests;
+ productReference = 18BF1DDF2B81AB8000B8D90F /* MouseSdkTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 18BF1DE82B81AB8000B8D90F /* MouseSdkUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 18BF1DF92B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdkUITests" */;
+ buildPhases = (
+ 18BF1DE52B81AB8000B8D90F /* Sources */,
+ 18BF1DE62B81AB8000B8D90F /* Frameworks */,
+ 18BF1DE72B81AB8000B8D90F /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 18BF1DEB2B81AB8000B8D90F /* PBXTargetDependency */,
+ );
+ name = MouseSdkUITests;
+ productName = MouseSdkUITests;
+ productReference = 18BF1DE92B81AB8000B8D90F /* MouseSdkUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 18BF1DC22B81AB7C00B8D90F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastUpgradeCheck = 1500;
+ TargetAttributes = {
+ 18BF1DC92B81AB7C00B8D90F = {
+ CreatedOnToolsVersion = 15.0.1;
+ LastSwiftMigration = 1500;
+ };
+ 18BF1DDE2B81AB8000B8D90F = {
+ CreatedOnToolsVersion = 15.0.1;
+ TestTargetID = 18BF1DC92B81AB7C00B8D90F;
+ };
+ 18BF1DE82B81AB8000B8D90F = {
+ CreatedOnToolsVersion = 15.0.1;
+ TestTargetID = 18BF1DC92B81AB7C00B8D90F;
+ };
+ };
+ };
+ buildConfigurationList = 18BF1DC52B81AB7C00B8D90F /* Build configuration list for PBXProject "MouseSdk" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 18BF1DC12B81AB7C00B8D90F;
+ productRefGroup = 18BF1DCB2B81AB7C00B8D90F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 18BF1DC92B81AB7C00B8D90F /* MouseSdk */,
+ 18BF1DDE2B81AB8000B8D90F /* MouseSdkTests */,
+ 18BF1DE82B81AB8000B8D90F /* MouseSdkUITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 18BF1DC82B81AB7C00B8D90F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18BF1DD42B81AB7F00B8D90F /* Assets.xcassets in Resources */,
+ 18BF1DD72B81AB7F00B8D90F /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DDD2B81AB8000B8D90F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DE72B81AB8000B8D90F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 9A36963E54431D0A5AE88E2F /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-MouseSdk-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 18BF1DC62B81AB7C00B8D90F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18BF1E052B81E18800B8D90F /* ConnectKitVC.swift in Sources */,
+ 18BF1DD22B81AB7C00B8D90F /* ViewController.m in Sources */,
+ 18BF1DD92B81AB7F00B8D90F /* main.m in Sources */,
+ 18BF1E762B832D2E00B8D90F /* ScanVC.m in Sources */,
+ 18BF1E222B83066200B8D90F /* BleManager.m in Sources */,
+ 18BF1DCF2B81AB7C00B8D90F /* AppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DDB2B81AB8000B8D90F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18BF1DE42B81AB8000B8D90F /* MouseSdkTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 18BF1DE52B81AB8000B8D90F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 18BF1DEE2B81AB8000B8D90F /* MouseSdkUITests.m in Sources */,
+ 18BF1DF02B81AB8000B8D90F /* MouseSdkUITestsLaunchTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 18BF1DE12B81AB8000B8D90F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 18BF1DC92B81AB7C00B8D90F /* MouseSdk */;
+ targetProxy = 18BF1DE02B81AB8000B8D90F /* PBXContainerItemProxy */;
+ };
+ 18BF1DEB2B81AB8000B8D90F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 18BF1DC92B81AB7C00B8D90F /* MouseSdk */;
+ targetProxy = 18BF1DEA2B81AB8000B8D90F /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 18BF1DD52B81AB7F00B8D90F /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 18BF1DD62B81AB7F00B8D90F /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 18BF1DF12B81AB8000B8D90F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ 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;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ 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;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 18BF1DF22B81AB8000B8D90F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ 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;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ 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;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ 18BF1DF42B81AB8000B8D90F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 811936622867478806C1A8BB /* Pods-MouseSdk.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = MouseSdk/MouseSdk.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/framework",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "要打开蓝牙功能哦,才能继续使用APP";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INFOPLIST_KEY_NSMainStoryboardFile = Main;
+ INFOPLIST_KEY_NSPrincipalClass = NSApplication;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdk;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 18BF1DF52B81AB8000B8D90F /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 51DE1118FCBA78EBEE6FE2CD /* Pods-MouseSdk.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = MouseSdk/MouseSdk.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/framework",
+ );
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "要打开蓝牙功能哦,才能继续使用APP";
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INFOPLIST_KEY_NSMainStoryboardFile = Main;
+ INFOPLIST_KEY_NSPrincipalClass = NSApplication;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdk;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OBJC_BRIDGING_HEADER = "MouseSdk/MouseSdk-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 18BF1DF72B81AB8000B8D90F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdkTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MouseSdk.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MouseSdk";
+ };
+ name = Debug;
+ };
+ 18BF1DF82B81AB8000B8D90F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdkTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MouseSdk.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MouseSdk";
+ };
+ name = Release;
+ };
+ 18BF1DFA2B81AB8000B8D90F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdkUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TEST_TARGET_NAME = MouseSdk;
+ };
+ name = Debug;
+ };
+ 18BF1DFB2B81AB8000B8D90F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = zhihuishixun.MouseSdkUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TEST_TARGET_NAME = MouseSdk;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 18BF1DC52B81AB7C00B8D90F /* Build configuration list for PBXProject "MouseSdk" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 18BF1DF12B81AB8000B8D90F /* Debug */,
+ 18BF1DF22B81AB8000B8D90F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 18BF1DF32B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdk" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 18BF1DF42B81AB8000B8D90F /* Debug */,
+ 18BF1DF52B81AB8000B8D90F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 18BF1DF62B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdkTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 18BF1DF72B81AB8000B8D90F /* Debug */,
+ 18BF1DF82B81AB8000B8D90F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 18BF1DF92B81AB8000B8D90F /* Build configuration list for PBXNativeTarget "MouseSdkUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 18BF1DFA2B81AB8000B8D90F /* Debug */,
+ 18BF1DFB2B81AB8000B8D90F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 18BF1DC22B81AB7C00B8D90F /* Project object */;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SchemeUserState</key>
+ <dict>
+ <key>MouseSdk.xcscheme_^#shared#^_</key>
+ <dict>
+ <key>orderHint</key>
+ <integer>2</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:MouseSdk.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:Pods/Pods.xcodeproj">
+ </FileRef>
+</Workspace>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+ uuid = "96107861-D4C9-42B2-9605-61F7DD48CC72"
+ type = "0"
+ version = "2.0">
+</Bucket>
--- /dev/null
+//
+// AppDelegate.h
+// MouseSdk
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+
+@end
+
--- /dev/null
+//
+// AppDelegate.m
+// MouseSdk
+//
+// Created by incar on 2024/2/18.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+
+@end
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ // Insert code here to initialize your application
+}
+
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ // Insert code here to tear down your application
+}
+
+
+- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
+ return YES;
+}
+
+
+@end
--- /dev/null
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+ </dependencies>
+ <scenes>
+ <!--Application-->
+ <scene sceneID="JPo-4y-FX3">
+ <objects>
+ <application id="hnw-xV-0zn" sceneMemberID="viewController">
+ <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="MouseSdk" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="MouseSdk" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About MouseSdk" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide MouseSdk" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit MouseSdk" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
+ <connections>
+ <action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+ <items>
+ <menuItem title="Font" id="Gi5-1S-RQB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+ <items>
+ <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+ <connections>
+ <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+ <connections>
+ <action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+ <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+ <menuItem title="Kern" id="jBQ-r6-VK2">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+ <items>
+ <menuItem title="Use Default" id="GUa-eO-cwY">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="cDB-IK-hbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Tighten" id="46P-cB-AYj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Loosen" id="ogc-rX-tC1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Ligatures" id="o6e-r0-MWq">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+ <items>
+ <menuItem title="Use Default" id="agt-UL-0e3">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="J7y-lM-qPV">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use All" id="xQD-1f-W4t">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Baseline" id="OaQ-X3-Vso">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+ <items>
+ <menuItem title="Use Default" id="3Om-Ey-2VK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Superscript" id="Rqc-34-cIF">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Subscript" id="I0S-gh-46l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Raise" id="2h7-ER-AoG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Lower" id="1tx-W0-xDw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+ <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+ <connections>
+ <action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+ <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Text" id="Fal-I4-PZk">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Text" id="d9c-me-L2H">
+ <items>
+ <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+ <connections>
+ <action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+ <connections>
+ <action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Justify" id="J5U-5w-g23">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+ <connections>
+ <action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+ <menuItem title="Writing Direction" id="H1b-Si-o9J">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+ <items>
+ <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="YGs-j5-SAR">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
+ </connections>
+ </menuItem>
+ <menuItem id="Lbh-J2-qVU">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
+ </connections>
+ </menuItem>
+ <menuItem id="jFq-tB-4Kx">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+ <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="Nop-cj-93Q">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
+ </connections>
+ </menuItem>
+ <menuItem id="BgM-ve-c93">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
+ </connections>
+ </menuItem>
+ <menuItem id="RB4-Sm-HuC">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+ <menuItem title="Show Ruler" id="vLm-3I-IUL">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
+ <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="MouseSdk Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
+ </connections>
+ </application>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModuleProvider=""/>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="0.0"/>
+ </scene>
+ <!--Window Controller-->
+ <scene sceneID="R2V-B0-nI4">
+ <objects>
+ <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
+ <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="196" y="240" width="480" height="270"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <connections>
+ <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
+ </connections>
+ </window>
+ <connections>
+ <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
+ </connections>
+ </windowController>
+ <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="250"/>
+ </scene>
+ <!--View Controller-->
+ <scene sceneID="hIz-AP-VOD">
+ <objects>
+ <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+ <view key="view" id="m2S-Jp-Qdl">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </viewController>
+ <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="75" y="655"/>
+ </scene>
+ </scenes>
+</document>
--- /dev/null
+//
+// BleManager.h
+// AliIotSdkDemo
+//
+// Created by incar on 2024/1/30.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreBluetooth/CoreBluetooth.h>
+NS_ASSUME_NONNULL_BEGIN
+
+@interface BleManager : NSObject
+@property (nonatomic,strong)void (^discoverDevice)(CBPeripheral *peripheral,NSDictionary *advertisementData) ;
+@property (nonatomic,strong)void (^didConnectDevice)(CBPeripheral *peripheral) ;
+
++(id)shared;
+
+- (void)startScanDevice;
+
+- (void)stopScanDevice;
+
+- (void)connectPeripheral:(CBPeripheral *)peripheral;
+
+- (void)lpConnect;
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// BleManager.m
+// AliIotSdkDemo
+//
+// Created by incar on 2024/1/30.
+//
+
+#import "BleManager.h"
+
+@interface BleManager ()<CBCentralManagerDelegate,CBPeripheralDelegate>
+@property (nonatomic,strong) NSMutableArray *scanArray;
+@property(nonatomic, strong) CBCentralManager *centralManager;
+@property(nonatomic, strong) CBPeripheral *connectPeripheral;
+@property(nonatomic, strong) CBCharacteristic *writeCha;
+@property(nonatomic, strong) CBCharacteristic *notifyCha;
+
+@property(nonatomic, assign) BOOL isSdkInitSuccess;
+@end
+
+@implementation BleManager
++(id)shared{
+ static BleManager *manager = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ manager = [[BleManager alloc]init];
+ });
+ return manager;
+}
+
+-(NSMutableArray *)scanArray{
+ if (_scanArray == nil) {
+ _scanArray = [[NSMutableArray alloc] init];
+ }
+ return _scanArray;
+}
+
+- (instancetype)init{
+ self = [super init];
+ if (self) {
+ self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
+ }
+ return self;
+}
+
+#pragma mark - CBCentralManagerDelegate
+- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
+ switch (central.state) {
+
+ case CBManagerStateUnknown:
+
+ break;
+ case CBManagerStateResetting:
+
+ break;
+ case CBManagerStateUnsupported:
+
+ break;
+ case CBManagerStateUnauthorized:
+
+ break;
+ case CBManagerStatePoweredOff:
+
+ break;
+ case CBManagerStatePoweredOn:
+// [self startScanDevice];
+ break;
+ }
+}
+
+- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
+ if ([self.scanArray containsObject:peripheral]){
+ return;
+ }
+ [self.scanArray insertObject:peripheral atIndex:0];
+ if(self.discoverDevice){
+ self.discoverDevice(peripheral, advertisementData);
+ }
+}
+
+- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
+ if(self.didConnectDevice){
+ self.didConnectDevice(peripheral);
+ }
+
+ self.connectPeripheral = peripheral;
+ peripheral.delegate = self;
+ [peripheral discoverServices:nil];
+}
+-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
+
+}
+- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
+ NSLog(@"连接失败。。。");
+}
+#pragma mark - CBPeripheralDelegate
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
+ for (CBService *service in peripheral.services)
+ [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FED7"],[CBUUID UUIDWithString:@"FED8"]] forService:service];
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
+ for (CBCharacteristic *characteristic in service.characteristics){
+ if ([characteristic.UUID.UUIDString isEqualToString:@"FED7"]) {
+ self.writeCha = characteristic;
+ //找到写入的uuid后,调用sdk配置方法
+// [AliIot.shareInstance configSDK];
+ }
+ if ([characteristic.UUID.UUIDString isEqualToString:@"FED8"]) {
+ self.notifyCha = characteristic;
+ [peripheral setNotifyValue:YES forCharacteristic:characteristic];
+ }
+ }
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
+ NSLog(@"[receive] %@",characteristic.value);
+ //收到蓝牙设备发送过来的数据,转发给sdk处理
+// [AliIot.shareInstance handleReceivedData:characteristic.value];
+}
+
+#pragma mark - AliIotSDKCallbackProtocol
+- (void)handleSendData:(NSData *)data toDevice:(id)device error:(NSError *)error {
+ NSLog(@"[send] %@",data);
+ if (!error) {
+ //收到sdk回调的数据,发送给蓝牙设备
+ [self.connectPeripheral writeValue:data forCharacteristic:self.writeCha type:CBCharacteristicWriteWithoutResponse];
+ }
+}
+
+- (void)handleLog:(NSString *)log {
+ NSLog(@"[sdk log] %@",log);
+}
+
+- (void)sdkInitResult:(BOOL)isFinish{
+ self.isSdkInitSuccess = isFinish;
+ NSString *log = isFinish?@"初始化完成":@"初始化失败";
+ NSLog(@"%@",log);
+}
+
+#pragma mark - AliIotSdkMapCallbackProtocol
+- (void)addCollect:(nonnull NSString *)collectionId content:(nonnull NSString *)jsonString {
+ //传到服务器或者本地缓存
+}
+
+- (void)addTrajectory:(nonnull NSString *)trajectoryId content:(nonnull NSString *)jsonString {
+ //传到服务器或者本地缓存
+}
+
+- (void)getCollectListView:(NSInteger)page pageSize:(NSInteger)pageSize {
+ //从服务器或者本地缓存获取收藏数据,然后将数据传给sdk
+// [AliIot.shareInstance sendCollectList:jsonString];
+}
+
+- (void)getTrajectoryListView:(NSInteger)page pageSize:(NSInteger)pageSize {
+ //从服务器或者本地缓存获取轨迹数据,然后将数据传给sdk
+// [AliIot.shareInstance sendTrajectoryList:jsonString];
+}
+
+- (void)removeCollect:(nonnull NSString *)collectionId {
+ //从服务器或者本地缓存移除记录
+}
+
+- (void)removeTrajectory:(nonnull NSString *)trajectoryId {
+ //从服务器或者本地缓存移除记录
+}
+
+#pragma mark - public
+- (void)startScanDevice{
+ [self.scanArray removeAllObjects];
+ [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO]}];
+}
+
+- (void)stopScanDevice{
+ [self.centralManager stopScan];
+}
+
+- (void)connectPeripheral:(CBPeripheral *)peripheral{
+ [self.centralManager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : [NSNumber numberWithBool:YES]}];
+}
+
+//飞鸽书请求连接
+- (void)lpConnect{
+ NSData *data = [self tysm_dataWithHexString:@"000401"];
+ NSMutableData *mData = NSMutableData.data;
+ uint8_t cmd = 0x7f;
+ [mData appendBytes:&cmd length:1];
+ [mData appendData:data];
+// [AliIot.shareInstance handleReceivedData:mData.copy];
+}
+- (NSData *)tysm_dataWithHexString:(NSString *)hexStr {
+ hexStr = [hexStr stringByReplacingOccurrencesOfString:@" " withString:@""];
+ hexStr = [hexStr lowercaseString];
+ NSUInteger len = hexStr.length;
+ if (!len) return nil;
+ unichar *buf = malloc(sizeof(unichar) * len);
+ if (!buf) return nil;
+ [hexStr getCharacters:buf range:NSMakeRange(0, len)];
+
+ NSMutableData *result = [NSMutableData data];
+ unsigned char bytes;
+ char str[3] = { '\0', '\0', '\0' };
+ int i;
+ for (i = 0; i < len / 2; i++) {
+ str[0] = buf[i * 2];
+ str[1] = buf[i * 2 + 1];
+ bytes = strtol(str, NULL, 16);
+ [result appendBytes:&bytes length:1];
+ }
+ free(buf);
+ return result;
+}
+@end
--- /dev/null
+//
+// ConnectKitVC.swift
+// AliIotSdkDemo
+//
+// Created by incar on 2024/2/1.
+//
+
+import Cocoa
+//import AliIotConnectKit
+
+class ConnectKitVC: NSViewController {
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = "AliIotConnectKit"
+
+// //先设置代理
+// AliConnectMananger_C.shared.iotStateDelegate = self
+// AliConnectMananger_C.shared.bleSendDataDelegate = self
+// AliConnectMananger_C.shared.appVersion = "1.0"//一定要设置app版本
+// AliCloudIotLogManager_C.shared.switchLogStatus(true)
+
+// if true{
+// let btn = UIButton(type: .custom)
+// btn.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
+// btn.backgroundColor = .orange;
+// btn.setTitle("checkFGS", for: .normal)
+// btn.addTarget(self, action: #selector(checkFGSStatus), for: .touchUpInside)
+// view.addSubview(btn)
+// }
+// if true{
+// let btn = UIButton(type: .custom)
+// btn.frame = CGRect(x: 100, y: 300, width: 100, height: 100)
+// btn.backgroundColor = .orange;
+// btn.setTitle("connectLp", for: .normal)
+// btn.addTarget(self, action: #selector(connectLp), for: .touchUpInside)
+// view.addSubview(btn)
+// }
+ }
+
+// @objc func checkFGSStatus() {
+// AliConnectMananger_C.shared.checkFgsState { isSuccess, data in
+// if isSuccess {
+// debugPrint("已有三元组数据")
+// }else {
+// debugPrint("没有,错误日志 : \(String(describing: data["msg"]))")
+// }
+// }
+// }
+//
+// @objc func connectLp() {
+// //swift-Lp连接
+// AliConnectMananger_C.shared.startConnectLpState { isSuccess, data in
+// if isSuccess {
+// debugPrint("LP连接成功")
+// }else {
+// debugPrint("LP连接失败,错误日志 : \(String(describing: data["msg"]))")
+// }
+// }
+// }
+//
+// //Lp断开连接
+// @objc func disconnectLp() {
+// AliConnectMananger_C.shared.disConnect(result: { (_ isSuccess:Bool, _ data:[String:Any]) in
+//
+// })
+// }
+//
+// //App端接收到蓝牙信息,请调用以下接口
+// func onBleDataReceived(data: Data) {
+//// AliConnectMananger_C.shared.bleDataReceived(data: data)
+// }
+
+}
+
+//extension ConnectKitVC: BleNeedSendDataDelegate_C {
+// func sendBleData(data: Data) {
+// print(data)
+// debugPrint("在此处将数据发送给已连接的蓝牙")
+// }
+//}
+//
+//extension ConnectKitVC:IotStateDelatege_C{
+// func onIotConnected() {
+//
+// }
+//
+// func onIotConnectFailed() {
+//
+// }
+//
+// func onIotDisconeted() {
+//
+// }
+//
+// func onIotConnecting() {
+//
+// }
+//
+// func onLog(log: String) {
+//
+// }
+//}
--- /dev/null
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.device.bluetooth</key>
+ <true/>
+ <key>com.apple.security.files.user-selected.read-only</key>
+ <true/>
+</dict>
+</plist>
--- /dev/null
+//
+// ScanVC.h
+// MouseSdk
+//
+// Created by incar on 2024/2/19.
+//
+
+#import <Cocoa/Cocoa.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ScanVC : NSViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
--- /dev/null
+//
+// ScanVC.m
+// MouseSdk
+//
+// Created by incar on 2024/2/19.
+//
+
+#import "ScanVC.h"
+
+@interface ScanVC ()
+
+@end
+
+@implementation ScanVC
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do view setup here.
+}
+
+@end
--- /dev/null
+//
+// ViewController.h
+// MouseSdk
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface ViewController : NSViewController
+
+
+@end
+
--- /dev/null
+//
+// ViewController.m
+// MouseSdk
+//
+// Created by incar on 2024/2/18.
+//
+
+#import "ViewController.h"
+#import "ScanVC.h"
+#import "Ble/BleManager.h"
+//#import "AliIotConnectKit/AliIotConnectKit.h"
+
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Do any additional setup after loading the view.
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(20, 60, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"扫描"; // 按钮文字
+ [btn setTarget:self];
+ [btn setAction:@selector(scanAction)]; // 按钮触发的方法
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(100, 100, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"fgs"; // 按钮文字
+ [btn setTarget:self];
+ [btn setAction:@selector(checkFGSStatus)]; // 按钮触发的方法
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ {
+ NSButton *btn = [[NSButton alloc]initWithFrame:NSMakeRect(300, 100, 100, 45)];
+ btn.bordered = true;
+ btn.title = @"connectLp"; // 按钮文字
+ [btn setTarget:self];
+ [btn setAction:@selector(connectLp)]; // 按钮触发的方法
+ btn.state = 1;
+ [self.view addSubview:btn];
+ }
+ [self setupBleManger];
+}
+
+
+- (void)setRepresentedObject:(id)representedObject {
+ [super setRepresentedObject:representedObject];
+
+ // Update the view, if already loaded.
+}
+
+- (void)scanAction{
+ [[BleManager shared] startScanDevice];
+
+}
+- (void)setupBleManger{
+ BleManager *manager = [BleManager shared];
+ __weak typeof(ViewController) *weakSelf = self;
+ __weak typeof(BleManager) *weakManager = manager;
+ manager.discoverDevice = ^(CBPeripheral *peripheral,NSDictionary *advertisementData){
+ if(peripheral.name.length == 0){
+ return;
+ }
+ NSLog(@"%@",peripheral.name);
+ if([peripheral.name isEqualToString:@"Ublue_BT895x"]){
+ [weakManager connectPeripheral:peripheral];
+ [weakManager stopScanDevice];
+ }
+ };
+ manager.didConnectDevice = ^(CBPeripheral * _Nonnull peripheral) {
+ NSLog(@"连接成功");
+
+ };
+// [manager startScanDevice];
+}
+
+//- (void)checkFGSStatus{
+// [AliConnectMananger_C.shared checkFgsStateWithResult:^(BOOL isSuccess, NSDictionary<NSString *,id> * dict) {
+// NSLog(@"FGS成功 = %@,dict = %@",[NSNumber numberWithBool:isSuccess],dict);
+// }];
+//}
+//- (void)connectLp{
+// [AliConnectMananger_C.shared startConnectLpStateWithResult:^(BOOL isSuccess, NSDictionary<NSString *,id> * dict) {
+// NSLog(@"LP成功 = %@,dict = %@",[NSNumber numberWithBool:isSuccess],dict);
+// }];
+//}
+@end
--- /dev/null
+//
+// main.m
+// MouseSdk
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char * argv[]) {
+ @autoreleasepool {
+ // Setup code that might create autoreleased objects goes here.
+ }
+ return NSApplicationMain(argc, argv);
+}
--- /dev/null
+//
+// MouseSdkTests.m
+// MouseSdkTests
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <XCTest/XCTest.h>
+
+@interface MouseSdkTests : XCTestCase
+
+@end
+
+@implementation MouseSdkTests
+
+- (void)setUp {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+}
+
+- (void)testExample {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+}
+
+- (void)testPerformanceExample {
+ // This is an example of a performance test case.
+ [self measureBlock:^{
+ // Put the code you want to measure the time of here.
+ }];
+}
+
+@end
--- /dev/null
+//
+// MouseSdkUITests.m
+// MouseSdkUITests
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <XCTest/XCTest.h>
+
+@interface MouseSdkUITests : XCTestCase
+
+@end
+
+@implementation MouseSdkUITests
+
+- (void)setUp {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ // In UI tests it is usually best to stop immediately when a failure occurs.
+ self.continueAfterFailure = NO;
+
+ // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+}
+
+- (void)testExample {
+ // UI tests must launch the application that they test.
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+ [app launch];
+
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+}
+
+- (void)testLaunchPerformance {
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *)) {
+ // This measures how long it takes to launch your application.
+ [self measureWithMetrics:@[[[XCTApplicationLaunchMetric alloc] init]] block:^{
+ [[[XCUIApplication alloc] init] launch];
+ }];
+ }
+}
+
+@end
--- /dev/null
+//
+// MouseSdkUITestsLaunchTests.m
+// MouseSdkUITests
+//
+// Created by incar on 2024/2/18.
+//
+
+#import <XCTest/XCTest.h>
+
+@interface MouseSdkUITestsLaunchTests : XCTestCase
+
+@end
+
+@implementation MouseSdkUITestsLaunchTests
+
++ (BOOL)runsForEachTargetApplicationUIConfiguration {
+ return YES;
+}
+
+- (void)setUp {
+ self.continueAfterFailure = NO;
+}
+
+- (void)testLaunch {
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+ [app launch];
+
+ // Insert steps here to perform after app launch but before taking a screenshot,
+ // such as logging into a test account or navigating somewhere in the app
+
+ XCTAttachment *attachment = [XCTAttachment attachmentWithScreenshot:XCUIScreen.mainScreen.screenshot];
+ attachment.name = @"Launch Screen";
+ attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
+ [self addAttachment:attachment];
+}
+
+@end
--- /dev/null
+# Uncomment the next line to define a global platform for your project
+# platform :ios, '11.0'
+#platform :osx, '11.0'
+
+#target 'AliIotSdk' do
+# # Comment the next line if you don't want to use dynamic frameworks
+# use_frameworks!
+## platform :osx, '11.0'
+#
+# # Pods for AliIotSdk
+## pod 'NBULog'
+## pod 'fishhook'
+## pod 'Bugly'
+# target 'AliIotSdkTests' do
+# # Pods for testing
+# end
+#
+#end
+
+target 'MouseSdk' do
+ # Comment the next line if you don't want to use dynamic frameworks
+
+
+
+
+
+end
+
--- /dev/null
+PODFILE CHECKSUM: d7796ecd818f63eaac3969f5294c693f6619eef1
+
+COCOAPODS: 1.14.3
--- /dev/null
+PODFILE CHECKSUM: d7796ecd818f63eaac3969f5294c693f6619eef1
+
+COCOAPODS: 1.14.3
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 39E7BC8E2D772AB23854B1B1BBA42FCF /* Pods-MouseSdk-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 585C3A15C5CB6B03146AA00AAD335C03 /* Pods-MouseSdk-dummy.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 585C3A15C5CB6B03146AA00AAD335C03 /* Pods-MouseSdk-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MouseSdk-dummy.m"; sourceTree = "<group>"; };
+ 9A8CA8C9D66D3BFAB6F6CCFC79DC31C9 /* Pods-MouseSdk */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "Pods-MouseSdk"; path = "libPods-MouseSdk.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+ C852EDA6DDC5C8053C7F2FF7149F31E3 /* Pods-MouseSdk-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MouseSdk-acknowledgements.plist"; sourceTree = "<group>"; };
+ F9872DA2CEBC0ECDA2FC315617F2419F /* Pods-MouseSdk.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MouseSdk.release.xcconfig"; sourceTree = "<group>"; };
+ F9B0189607C6CE102AD4898A68202943 /* Pods-MouseSdk-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-MouseSdk-acknowledgements.markdown"; sourceTree = "<group>"; };
+ FF7FB93BB5F5277C8A2E23AFB399BC2B /* Pods-MouseSdk.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MouseSdk.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ A5D81B5F39AA705456897AC5FB6CEB8A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 022C06ECBA02AB2798C443196FAC13EC /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 9A8CA8C9D66D3BFAB6F6CCFC79DC31C9 /* Pods-MouseSdk */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0B3367E8FD03BB99810868B1CA43FAFE /* Targets Support Files */ = {
+ isa = PBXGroup;
+ children = (
+ 6E9E886ECCB3454D14D1DF90A3C68B2B /* Pods-MouseSdk */,
+ );
+ name = "Targets Support Files";
+ sourceTree = "<group>";
+ };
+ 6E9E886ECCB3454D14D1DF90A3C68B2B /* Pods-MouseSdk */ = {
+ isa = PBXGroup;
+ children = (
+ F9B0189607C6CE102AD4898A68202943 /* Pods-MouseSdk-acknowledgements.markdown */,
+ C852EDA6DDC5C8053C7F2FF7149F31E3 /* Pods-MouseSdk-acknowledgements.plist */,
+ 585C3A15C5CB6B03146AA00AAD335C03 /* Pods-MouseSdk-dummy.m */,
+ FF7FB93BB5F5277C8A2E23AFB399BC2B /* Pods-MouseSdk.debug.xcconfig */,
+ F9872DA2CEBC0ECDA2FC315617F2419F /* Pods-MouseSdk.release.xcconfig */,
+ );
+ name = "Pods-MouseSdk";
+ path = "Target Support Files/Pods-MouseSdk";
+ sourceTree = "<group>";
+ };
+ CF1408CF629C7361332E53B88F7BD30C = {
+ isa = PBXGroup;
+ children = (
+ 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */,
+ D89477F20FB1DE18A04690586D7808C4 /* Frameworks */,
+ 022C06ECBA02AB2798C443196FAC13EC /* Products */,
+ 0B3367E8FD03BB99810868B1CA43FAFE /* Targets Support Files */,
+ );
+ sourceTree = "<group>";
+ };
+ D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 81DA359FA00F482DE79C709C8D1316CC /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 488F4A0D7D6DE42ABD3D07A259BE62C3 /* Pods-MouseSdk */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = EB3B5F18B6AD5DE41DF7C967AF160E3F /* Build configuration list for PBXNativeTarget "Pods-MouseSdk" */;
+ buildPhases = (
+ 81DA359FA00F482DE79C709C8D1316CC /* Headers */,
+ EF13989D1A2E115A198198139CB8C424 /* Sources */,
+ A5D81B5F39AA705456897AC5FB6CEB8A /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Pods-MouseSdk";
+ productName = "Pods-MouseSdk";
+ productReference = 9A8CA8C9D66D3BFAB6F6CCFC79DC31C9 /* Pods-MouseSdk */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ BFDFE7DC352907FC980B868725387E98 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1500;
+ LastUpgradeCheck = 1500;
+ };
+ buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ Base,
+ en,
+ );
+ mainGroup = CF1408CF629C7361332E53B88F7BD30C;
+ productRefGroup = 022C06ECBA02AB2798C443196FAC13EC /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 488F4A0D7D6DE42ABD3D07A259BE62C3 /* Pods-MouseSdk */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ EF13989D1A2E115A198198139CB8C424 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 39E7BC8E2D772AB23854B1B1BBA42FCF /* Pods-MouseSdk-dummy.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1FAF1FE5F18D2BAD25DEA6DFF2EE1B9C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FF7FB93BB5F5277C8A2E23AFB399BC2B /* Pods-MouseSdk.debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+ EXECUTABLE_PREFIX = lib;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PODS_ROOT = "$(SRCROOT)";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ 5607522B196501B14655AA04C06E49DB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ 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 = (
+ "POD_CONFIGURATION_DEBUG=1",
+ "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;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ STRIP_INSTALLED_PRODUCT = NO;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ SYMROOT = "${SRCROOT}/../build";
+ };
+ name = Debug;
+ };
+ 6EBCEF872573FEE621FA40B74249C8DF /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F9872DA2CEBC0ECDA2FC315617F2419F /* Pods-MouseSdk.release.xcconfig */;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+ EXECUTABLE_PREFIX = lib;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ OTHER_LDFLAGS = "";
+ OTHER_LIBTOOLFLAGS = "";
+ PODS_ROOT = "$(SRCROOT)";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ 9F9F1BE0F35BB8BE3895661CA016E33F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ 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_PREPROCESSOR_DEFINITIONS = (
+ "POD_CONFIGURATION_RELEASE=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;
+ MACOSX_DEPLOYMENT_TARGET = 13.5;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ STRIP_INSTALLED_PRODUCT = NO;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ SWIFT_VERSION = 5.0;
+ SYMROOT = "${SRCROOT}/../build";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5607522B196501B14655AA04C06E49DB /* Debug */,
+ 9F9F1BE0F35BB8BE3895661CA016E33F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ EB3B5F18B6AD5DE41DF7C967AF160E3F /* Build configuration list for PBXNativeTarget "Pods-MouseSdk" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1FAF1FE5F18D2BAD25DEA6DFF2EE1B9C /* Debug */,
+ 6EBCEF872573FEE621FA40B74249C8DF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "488F4A0D7D6DE42ABD3D07A259BE62C3"
+ BuildableName = "libPods-MouseSdk.a"
+ BlueprintName = "Pods-MouseSdk"
+ ReferencedContainer = "container:Pods.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SchemeUserState</key>
+ <dict>
+ <key>Pods-MouseSdk.xcscheme</key>
+ <dict>
+ <key>isShown</key>
+ <false/>
+ <key>orderHint</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict/>
+</dict>
+</plist>
--- /dev/null
+# Acknowledgements
+This application makes use of the following third party libraries:
+Generated by CocoaPods - https://cocoapods.org
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreferenceSpecifiers</key>
+ <array>
+ <dict>
+ <key>FooterText</key>
+ <string>This application makes use of the following third party libraries:</string>
+ <key>Title</key>
+ <string>Acknowledgements</string>
+ <key>Type</key>
+ <string>PSGroupSpecifier</string>
+ </dict>
+ <dict>
+ <key>FooterText</key>
+ <string>Generated by CocoaPods - https://cocoapods.org</string>
+ <key>Title</key>
+ <string></string>
+ <key>Type</key>
+ <string>PSGroupSpecifier</string>
+ </dict>
+ </array>
+ <key>StringsTable</key>
+ <string>Acknowledgements</string>
+ <key>Title</key>
+ <string>Acknowledgements</string>
+</dict>
+</plist>
--- /dev/null
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Pods_MouseSdk : NSObject
+@end
+@implementation PodsDummy_Pods_MouseSdk
+@end
--- /dev/null
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
--- /dev/null
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES