654 Commits

Author SHA1 Message Date
Juraldinio 7a321edd97 Credentials 2026-01-12 19:15:19 +03:00
Jura Shikin 1deddd1f04 Merge branch 'feature/MALINKA-1095/modified_password_refactoring' into 'develop'
MALINKA-1095: Password logic refactoring

See merge request raspberry/mobile/ios!249
2023-03-10 11:40:54 +03:00
Андрей Геращенко f52cdb51bc MALINKA-1095: Password logic refactoring 2023-03-10 11:40:54 +03:00
Андрей Геращенко 3ce00d7e36 Merge branch 'feature/MALINKA-1179/chat_textfield' into 'develop'
MALINKA-1179: Chat textfield fix

See merge request raspberry/mobile/ios!271
2023-03-10 11:23:56 +03:00
Elena Nazarova 0a2e572bf2 MALINKA-1179: Chat textfield fix 2023-03-10 11:23:56 +03:00
Андрей Геращенко cc37b88f54 Merge branch 'feature/MALINKA-1198/token_icons' into 'develop'
MALINKA-1193: Added token icons

See merge request raspberry/mobile/ios!273
2023-03-09 19:00:07 +03:00
Elena Nazarova 9fc28d6a67 MALINKA-1193: Added token icons 2023-03-09 19:00:06 +03:00
Juraldinio 973e1290dc Documentation 2023-03-09 14:20:32 +03:00
Jura Shikin 6764fa2a68 Merge branch 'feature/MALINKA-1192/chat_from_transaction' into 'develop'
MALINKA-1192: Fix popup hide

See merge request raspberry/mobile/ios!272
2023-03-09 09:20:30 +03:00
Elena Nazarova 8c317938e4 MALINKA-1192: Fix popup hide 2023-03-09 09:20:30 +03:00
Juraldinio e8f6fd38f8 noissue - Update build script for detect xcode running 2023-03-06 17:27:55 +03:00
Никита Чирухин dbfb4b5d19 Merge branch 'feature/MALINKA-1182/searchfield_send_token' into 'develop'
MALINKA-1182: Added missed textfield

See merge request raspberry/mobile/ios!268
2023-03-06 15:06:51 +03:00
Elena Nazarova ca1daa7123 MALINKA-1182: Added missed textfield 2023-03-06 15:06:51 +03:00
Jura Shikin 220b52c161 Merge branch 'feature/MALINKA-1172/cryptoswift' into 'develop'
MALINKA-1172: CryptoSwift

See merge request raspberry/mobile/ios!267
2023-03-03 19:27:56 +03:00
Elena Nazarova 90e9d3830a MALINKA-1172: CryptoSwift 2023-03-03 19:27:56 +03:00
Elena Nazarova 0375e7e9ad MALINKA-1134: Remove references to parents 2023-03-02 18:28:42 +03:00
Андрей Геращенко 6f36b4ee9c MALINKA-1155 - update push tokens service 2023-03-02 18:28:42 +03:00
Elena Nazarova 4274eca8c2 MALINKA-743: Autolayout swap fix 2023-03-02 18:28:42 +03:00
Никита Чирухин fb1e0bd195 MALINKA-1136: Error creating a new chat 2023-03-02 18:28:42 +03:00
Juraldinio 6703834e74 noissue - Increase version to 1.5.0 2023-03-02 18:28:42 +03:00
Андрей Геращенко ce8f5033e5 MALINKA-1148: Fixed layout problems on inheritance setup controller 2023-03-02 18:28:42 +03:00
Elena Nazarova 6455e76c43 MALINKA-1094: CommonTextFieldSearch swiftui 2023-03-02 18:28:42 +03:00
Elena Nazarova c933334550 MALINKA-1024: MessageKit update 2023-03-02 18:28:42 +03:00
user d2efea75fc MALINKA-1149: Fixed incorrect localization 2023-03-02 18:28:42 +03:00
Elena Nazarova 8b8e03255a empty commit 2023-03-02 18:27:57 +03:00
Elena Nazarova e38fe4661e MALINKA-738: Autolayout issues of filters screen 2023-03-02 18:27:57 +03:00
Elena Nazarova 0078d6e0d7 MALINKA-735: Fixed autolayout issues on sell/buy screens 2023-03-02 18:27:57 +03:00
Elena Nazarova aa28d6ef42 MALINKA-740: Autolayout fixes on inheritance controller 2023-03-02 18:27:57 +03:00
Jura Shikin c6bb0d3645 noise - Fix project description yaml file 2023-03-02 16:57:07 +03:00
Jura Shikin 0df769f86b Merge branch 'master' into 'develop'
# Conflicts:
#   iOS/project.yml
2023-03-02 13:08:31 +00:00
Никита Чирухин 274a2c5261 Merge branch 'feature/MALINKA-1175/camera_on_cross' into 'develop'
MALINKA-1175: Fix of qr-camera cross behaviour

See merge request raspberry/mobile/ios!264
2023-03-02 15:38:13 +03:00
Elena Nazarova c2a64000b4 MALINKA-1175: Fix of qr-camera cross behaviour 2023-03-02 15:38:13 +03:00
Juraldinio aca9045648 MALINKA-1171 - Fix crash in chat 2023-03-01 20:43:56 +03:00
Juraldinio b53098d3eb MALINKA-498 - cherry pick from 1.5.0 release 2023-03-01 15:29:38 +03:00
Андрей Геращенко 49c4137975 Merge branch 'MALINKA-1170/send_token_loader' into 'develop'
MALINKA-1170: Add quantity of action check

See merge request raspberry/mobile/ios!262
2023-03-01 11:36:51 +03:00
Elena Nazarova 2bc0c83328 MALINKA-1170: Add quantity of action check 2023-03-01 11:36:51 +03:00
Никита Чирухин f86c434d1f Merge branch 'feature/MALINKA-1162/infinite-loading' into 'develop'
MALINKA-1162 - infinite loading

See merge request raspberry/mobile/ios!260
2023-02-28 10:44:13 +03:00
Никита Чирухин b98ca6d496 Merge branch 'feature/MALINKA-1029/Chats' into 'develop'
MALINKA-1029 - fix show chats

See merge request raspberry/mobile/ios!259
2023-02-28 09:33:06 +03:00
Jura Shikin 2ec2fee225 MALINKA-1029 - fix show chats 2023-02-28 09:33:05 +03:00
Juraldinio 9b280ce58c MALINKA-1162 - infinite loading 2023-02-27 16:17:17 +03:00
Jura Shikin 6f9f336bab Merge branch 'release/1.3.2' into 'master'
MALINKA-1161 - add rubles icon to application

See merge request raspberry/mobile/ios!258
2023-02-23 23:47:40 +03:00
Juraldinio 9f29129168 MALINKA-1161 - add rubles icon to application 2023-02-23 23:36:43 +03:00
Juraldinio e862226d7f MALINKA-1137 - remove build stage from MR request 2023-02-21 15:11:32 +03:00
Elena Nazarova 2a43809fac Merge branch 'feature/MALINKA-774/Incorrect_messages_order' into 'develop'
Resolve MALINKA-774 "Feature//incorrect messages order"

See merge request raspberry/mobile/ios!239
2023-02-06 22:49:01 +03:00
Андрей Геращенко 6e71b86fd0 Resolve MALINKA-774 "Feature//incorrect messages order" 2023-02-06 22:49:01 +03:00
Elena Nazarova 056387a79e Merge branch 'feature/MALINKA-1055/breaks_view_of_dialog_when_delete_message' into 'develop'
MALINKA-1055: Breaks the display of the dialog when deleting a message

See merge request raspberry/mobile/ios!232
2023-02-06 16:52:53 +03:00
Никита Чирухин 5488f23461 MALINKA-1055: Breaks the display of the dialog when deleting a message 2023-02-06 16:52:52 +03:00
Jura Shikin 26aa66a52e Merge branch 'feature/MALINKA-1110/Incorrent_price_representation' into 'develop'
MALINKA-1110: Fixed price representation

See merge request raspberry/mobile/ios!245
2023-02-06 16:36:32 +03:00
user 6df69c750b MALINKA-1110: Fixed price representation 2023-02-06 16:09:29 +03:00
Jura Shikin edeaf1f91a Merge branch 'feature/MALINKA-789/Token_transfer_deeplink' into 'develop'
MALINKA-789: Fixed incoming transfer deeplink

See merge request raspberry/mobile/ios!244
2023-02-06 09:07:53 +03:00
Андрей Геращенко d428f84985 MALINKA-789: Fixed incoming transfer deeplink 2023-02-06 09:07:53 +03:00
Андрей Геращенко c8f503268e Merge branch 'feature/MALINKA-1089/Chat_error_fix' into 'develop'
MALINKA-1089: Fixed error representation

See merge request raspberry/mobile/ios!242
2023-02-02 15:27:14 +03:00
user 93133dc17e MALINKA-1089: Fixed error representation 2023-02-02 15:10:15 +03:00
Никита Чирухин 43b81ebf3b Merge branch 'feature/MALINKA-1089/Error_opening_resources_screen' into 'develop'
MALINKA-1089: Fixed problem with opening resources error window and fixed presentation of alerts

See merge request raspberry/mobile/ios!235
2023-02-02 11:11:08 +03:00
user 76640eb4da MALINKA-1089: Fixed problem with opening resources error window and fixed presentation of alerts 2023-01-31 20:57:13 +03:00
Никита Чирухин b5e870050f Merge branch 'feature/MALINKA-1083/Pin_code_change_error' into 'develop'
MALINKA-1083: Fixed pin code issues

See merge request raspberry/mobile/ios!234
2023-01-31 15:39:43 +03:00
Jura Shikin af5516f201 Merge branch 'release/1.3.1' into 'master'
Hot fix

See merge request raspberry/mobile/ios!233
2023-01-31 12:08:00 +03:00
Elena Nazarova 09aa04ff79 Hot fix 2023-01-31 12:08:00 +03:00
user a92549347c MALINKA-1083: Fixed pin code issues 2023-01-31 03:11:42 +03:00
Никита Чирухин 80887fe6c0 Merge branch 'feature/MALINKA-1075/Autocomplete_error' into 'develop'
Resolve MALINKA-1075 "Feature//autocomplete error"

See merge request raspberry/mobile/ios!230
2023-01-30 18:46:33 +03:00
Андрей Геращенко c9fd8f012a Resolve MALINKA-1075 "Feature//autocomplete error" 2023-01-30 18:46:33 +03:00
Elena Nazarova 5a3b3f7f8e Merge branch 'MALINKA-1084/WalletModelAccountAction_refactoring' into 'develop'
MALINKA-1084: WalletModelAccountAction refactoring

See merge request raspberry/mobile/ios!229
2023-01-30 15:15:51 +03:00
Elena Nazarova 636ab46f5b MALINKA-1084: WalletModelAccountAction refactoring 2023-01-30 15:15:50 +03:00
Никита Чирухин d5a2b69f20 Merge branch 'feature/removePassword' into 'develop'
Remove passworf from Accounts service

See merge request raspberry/mobile/ios!227
2023-01-30 14:53:09 +03:00
Никита Чирухин ac9a088829 Merge branch 'feature/MALINKA-874/New_account_selection' into 'develop'
MALINKA-874: Fix selection of new account after its creation

See merge request raspberry/mobile/ios!228
2023-01-30 14:52:36 +03:00
Juraldinio 9c1c2ad2aa Remove passworf from Accounts service 2023-01-30 08:51:28 +03:00
user b2fbc0b83f MALINKA-874: Fix selection of new account after its creation 2023-01-30 02:20:38 +03:00
Jura Shikin 7518417578 Merge branch 'MALINKA-874/activate_accepted_account' into 'develop'
MALINKA-874: Activate accepted account

See merge request raspberry/mobile/ios!226
2023-01-27 16:23:53 +03:00
Elena Nazarova 741861c8f3 MALINKA-874: Activate accepted account 2023-01-27 16:23:53 +03:00
Никита Чирухин 90b936cd47 Merge branch 'feature/MALINKA-1082/Error_on_changing_pin_code' into 'develop'
MALINKA-1082: Fixed tabbar problems

See merge request raspberry/mobile/ios!224
2023-01-27 15:31:06 +03:00
Андрей Геращенко 9a4e02d2f3 MALINKA-1082: Fixed tabbar problems 2023-01-27 15:31:06 +03:00
Juraldinio 64a2313ba9 Remove warnings 2023-01-27 11:23:51 +03:00
Никита Чирухин bc313d7c1a Merge branch 'MALINKA-1071/ui_after_status_update' into 'develop'
MALINKA-1071: Update ui on account status change

See merge request raspberry/mobile/ios!222
2023-01-26 15:19:31 +03:00
Elena Nazarova 60e7c139bc MALINKA-1071: Update ui on account status change 2023-01-26 15:19:31 +03:00
Андрей Геращенко 877c4e2673 Merge branch 'MALINKA-1076/rubcash_history' into 'develop'
MALINKA-1076: Added missing parameters

See merge request raspberry/mobile/ios!220
2023-01-26 15:09:33 +03:00
Elena Nazarova 894f5d5dcc MALINKA-1076: Added missing parameters 2023-01-26 15:09:32 +03:00
Никита Чирухин eac2be21fd Merge branch 'feature/MALINKA-1033/Incorrect_pin_code_flow' into 'develop'
MALINKA-1033: Incorrect reset of pin code flow

See merge request raspberry/mobile/ios!221
2023-01-26 14:03:35 +03:00
Андрей Геращенко e795ec521c MALINKA-1033: Incorrect reset of pin code flow 2023-01-26 14:03:35 +03:00
Никита Чирухин ba8d09ee52 Merge branch 'feature/MALINKA-1020/Resets_default_token' into 'develop'
MALINKA-1020: Attempts to fix problem of reseting default token

See merge request raspberry/mobile/ios!219
2023-01-26 03:03:23 +03:00
Андрей Геращенко 86cd216417 MALINKA-1020: Attempts to fix problem of reseting default token 2023-01-26 03:03:23 +03:00
Никита Чирухин a68977db6f Merge branch 'MALINKA-1050/slow_succeed_alert' into 'develop'
MALINKA-1050: Loader until alert is shown

See merge request raspberry/mobile/ios!218
2023-01-26 03:02:06 +03:00
Elena Nazarova afccab2bd9 MALINKA-1050: Loader until alert is shown 2023-01-26 03:02:06 +03:00
Jura Shikin 6930978c91 Merge branch 'feature/MALINKA-1043/navbar_disappears_after_deleting_account' into 'develop'
MALINKA-1043: The lower bar disappears after deleting the account

See merge request raspberry/mobile/ios!217
2023-01-25 10:56:54 +03:00
Никита Чирухин 6e512a3ff2 MALINKA-1043: The lower bar disappears after deleting the account 2023-01-25 10:56:54 +03:00
Никита Чирухин 788b48f217 Merge branch 'MALINKA-1071/executed_ui' into 'develop'
MALINKA-1071: ui status fixes

See merge request raspberry/mobile/ios!215
2023-01-24 17:51:03 +03:00
Никита Чирухин dfe5b13c68 Merge branch 'MALINKA-1047/textfield_overlay' into 'develop'
MALINKA-1047: textfield overlay fix

See merge request raspberry/mobile/ios!216
2023-01-24 17:50:47 +03:00
Elena Nazarova 8f402d4e15 MALINKA-1047: textfield overlay fix 2023-01-24 13:08:05 +03:00
Elena Nazarova dc81f97030 MALINKA-1071: ui status fixes 2023-01-24 11:44:12 +03:00
Jura Shikin b06d9bc5a1 Merge branch 'feature/Messages' into 'develop'
WiP

See merge request raspberry/mobile/ios!214
2023-01-23 23:15:06 +03:00
Jura Shikin 9b34d78319 WiP 2023-01-23 23:15:06 +03:00
Jura Shikin 9eb99f52ac Merge branch 'MALINKA-1068/icon' into 'develop'
MALINKA-1068: Changed icon

See merge request raspberry/mobile/ios!213
2023-01-23 16:58:07 +03:00
Elena Nazarova 15f3f89189 MALINKA-1068: Changed icon 2023-01-23 16:58:07 +03:00
Jura Shikin ca423995df Merge branch 'feature/MALINKA-874/no_account_selected_after_creation' into 'develop'
MALINKA-874: No account is selected after creation

See merge request raspberry/mobile/ios!205
2023-01-23 15:12:12 +03:00
Никита Чирухин 5f66477a81 MALINKA-874: No account is selected after creation 2023-01-23 15:12:12 +03:00
Jura Shikin 913e5c79a9 Merge branch 'feature/MALINKA-1048/Crash_on_deleting_message' into 'develop'
MALINKA-1048: Fixed crash on deleting message

See merge request raspberry/mobile/ios!212
2023-01-23 12:54:57 +03:00
Андрей Геращенко 9d39001efd MALINKA-1048: Fixed crash on deleting message 2023-01-23 12:54:57 +03:00
Jura Shikin 0f6afd874a Merge branch 'feature/MALINKA-1030/Fix_crash_on_textfield' into 'develop'
MALINKA-1030: Fixed textfield input crash

See merge request raspberry/mobile/ios!210
2023-01-23 12:51:06 +03:00
Андрей Геращенко df2a5d73e5 MALINKA-1030: Fixed textfield input crash 2023-01-23 12:51:06 +03:00
Jura Shikin 42904783c3 Merge branch 'feature/syncPC' into 'develop'
MALINKA-1057 - Sync with paycash changes

See merge request raspberry/mobile/ios!211
2023-01-23 12:50:29 +03:00
Jura Shikin 7f4129ac72 MALINKA-1057 - Sync with paycash changes 2023-01-23 12:50:29 +03:00
Jura Shikin 74efc886cc Merge branch 'feature/MALINKA-1050/update_p2pControllerDashboard_screen_notification_appears_twice' into 'develop'
MALINKA-1050: When you update the Sell and Buy screen notification appears twice

See merge request raspberry/mobile/ios!208
2023-01-20 16:41:11 +03:00
Никита Чирухин a3cbaf6441 MALINKA-1050: When you update the Sell and Buy screen notification appears twice 2023-01-20 16:41:10 +03:00
Jura Shikin 119d53a3f0 Merge branch 'feature/MALINKA-1044/incorrect_display_of_textfield_in_the_dialog' into 'develop'
MALINKA-1044: Incorrect display of the input field in the dialog

See merge request raspberry/mobile/ios!206
2023-01-20 16:40:06 +03:00
Никита Чирухин b6b4ebc1a8 MALINKA-1044: Incorrect display of the input field in the dialog 2023-01-20 16:40:06 +03:00
Никита Чирухин f685698b87 Merge branch 'feature/MALINKA-1027/Fix_authorization_problems' into 'develop'
Resolve MALINKA-1027 "Feature//fix authorization problems"

See merge request raspberry/mobile/ios!204
2023-01-20 14:57:02 +03:00
Андрей Геращенко e8597d65da Resolve MALINKA-1027 "Feature//fix authorization problems" 2023-01-20 14:57:02 +03:00
Никита Чирухин 03903f906e Merge branch 'feature/MALINKA-1047/Chat_input_field_conflict' into 'develop'
MALINKA-1047: Fixed presentation of popup - accessory view no longer covers popup

See merge request raspberry/mobile/ios!209
2023-01-20 14:53:29 +03:00
user f4cd49636b MALINKA-1047: Fixed presentation of popup - accessory view no longer covers popup 2023-01-20 01:51:43 +03:00
Андрей Геращенко 7f85320305 Merge branch 'feature/MALINKA-1049/artifacts_locked_screen' into 'develop'
MALINKA-1049: Artifacts locked screen

See merge request raspberry/mobile/ios!207
2023-01-19 23:25:55 +03:00
Никита Чирухин 3f66b4bcf2 MALINKA-1049: Artifacts locked screen 2023-01-19 23:25:55 +03:00
Jura Shikin 0dfda056f7 Merge branch 'feature/MALINKA-997/double_navigationBar_on_account_resources' into 'develop'
MALINKA-997: double navigationbar on account resources

See merge request raspberry/mobile/ios!203
2023-01-18 18:25:14 +03:00
Никита Чирухин 515d69b821 MALINKA-997: double navigationbar on account resources 2023-01-18 18:25:14 +03:00
Juraldinio bf6f5a761c MALINKA-1030 - fix crash on search 2023-01-18 16:49:39 +03:00
Juraldinio 5e5d9c3faf MALINKA-1031 - remove crash 2023-01-18 16:35:20 +03:00
Juraldinio ca84952391 MALINKA-861 - show enter new pincode after fail 2023-01-17 19:10:42 +03:00
Juraldinio 42ac87962e MALINKA-1028 - fullscreen presentation 2023-01-17 16:08:48 +03:00
Андрей Геращенко e980c64fcd Merge branch 'feature/MALINKA-997/double_navigationBar_on_account_resources' into 'develop'
MALINKA-997: Double NavigationBar after ResourcesControllerSetup from chat

See merge request raspberry/mobile/ios!201
2023-01-17 15:10:50 +03:00
Никита Чирухин bfa6e3ac26 MALINKA-997: Double NavigationBar after ResourcesControllerSetup from chat 2023-01-17 15:10:50 +03:00
Никита Чирухин 3f8a9ccf54 Merge branch 'feature/MALINKA-1004/node_actions_model_unit_tests' into 'develop'
MALINKA-1004 Node actions model unit tests"

See merge request raspberry/mobile/ios!198
2023-01-17 15:09:45 +03:00
Андрей Геращенко edcc3274da MALINKA-1004 Node actions model unit tests" 2023-01-17 15:09:45 +03:00
Juraldinio 81ad8361d8 MALINKA-986 - move from node to hyperion loading 2023-01-17 00:10:17 +03:00
Juraldinio 6ef84a8afb MALINKA-986 - Fix crashes 2023-01-16 23:54:01 +03:00
Juraldinio 3f584acbd0 MALINKA-996 - Fix deletion also remove hude warnings 2023-01-16 22:55:11 +03:00
Juraldinio b9d5305ef6 MALINKA-861 - do not allow close window 2023-01-16 15:08:31 +03:00
Jura Shikin 418f4f373c Merge branch 'feature/MALINKA-970' into 'develop'
MALINKA-970 - Remove twice ask pincode

See merge request raspberry/mobile/ios!200
2023-01-16 14:39:30 +03:00
Jura Shikin 6e7509e3cf MALINKA-970 - Remove twice ask pincode 2023-01-16 14:39:30 +03:00
Jura Shikin dfea0fd2bb Merge branch 'feature/RemoveWallet' into 'develop'
MALINKA-861 - Remove wallet

See merge request raspberry/mobile/ios!199
2023-01-16 11:56:50 +03:00
Jura Shikin 2ffc46c0ec MALINKA-861 - Remove wallet 2023-01-16 11:56:50 +03:00
Jura Shikin b48079d295 Merge branch 'NetworkTests' into 'develop'
Some network tests

See merge request raspberry/mobile/ios!197
2023-01-14 01:40:26 +03:00
Jura Shikin cc0fdf6fe9 Some network tests 2023-01-14 01:40:25 +03:00
Андрей Геращенко ab5d2e3bbc Merge branch 'feature/MALINKA-956/disable_ability_to_select_same_tokens' into 'develop'
MALINKA-956: Disable the ability to select the same tokens in the liquidity deposit

See merge request raspberry/mobile/ios!195
2023-01-13 15:16:23 +03:00
Никита Чирухин 21d35c1f11 MALINKA-956: Disable the ability to select the same tokens in the liquidity deposit 2023-01-13 15:16:22 +03:00
Никита Чирухин 8df85ecec4 Merge branch 'feature/MALINKA-946/greymass_history_request' into 'develop'
MALINKA-946: Moved v1/history/get_actions request from the project to WalletNetwork library

See merge request raspberry/mobile/ios!193
2023-01-13 13:22:58 +03:00
Андрей Геращенко 0015980718 MALINKA-946: Moved v1/history/get_actions request from the project to WalletNetwork library 2023-01-13 13:22:58 +03:00
Juraldinio 13bed53acf MALINKA-1001 - add new options to usernames and backend 2023-01-12 22:41:48 +03:00
Никита Чирухин 258de53fd3 Merge branch 'MALINKA-955/disable_transactions_purchase_2' into 'develop'
MALINKA-955: Transaction purchase button ability

See merge request raspberry/mobile/ios!194
2023-01-12 20:46:38 +03:00
Elena Nazarova ed78569d7a MALINKA-955: Transaction purchase button ability 2023-01-12 20:46:38 +03:00
Андрей Геращенко f8751ba13c Merge branch 'feature/MALINKA-954/warranty_exchange_message' into 'develop'
MALINKA-954: Small text fix

See merge request raspberry/mobile/ios!196
2023-01-12 17:47:23 +03:00
Никита Чирухин dfa5757bff MALINKA-954: Small text fix 2023-01-12 17:47:23 +03:00
Jura Shikin cbf1f05258 Merge branch 'MALINKA-954/warranty_exchange_message_2' into 'develop'
MALINKA-954: removed extra condition

See merge request raspberry/mobile/ios!192
2023-01-12 10:57:22 +03:00
Никита Чирухин f16f4bb8a5 Merge branch 'MALINKA-969/old_tokens' into 'develop'
MALINKA-969: reload balances on viewWillAppear

See merge request raspberry/mobile/ios!191
2023-01-12 04:42:40 +03:00
Elena Nazarova 37ccc8883c MALINKA-954: removed extra condition 2023-01-11 16:12:45 +03:00
Elena Nazarova 3b034daa7c MALINKA-969: reload balances on viewWillAppear 2023-01-11 13:27:16 +03:00
Jura Shikin 6f341a403e Merge branch 'feature/MALINKA-964/Refresh_QR-code_screen' into 'develop'
MALINKA-964: Fixed app crash on changing account, added account refresh for...

See merge request raspberry/mobile/ios!187
2023-01-10 21:16:21 +03:00
Андрей Геращенко 4670cd12c4 MALINKA-964: Fixed app crash on changing account, added account refresh for... 2023-01-10 21:16:21 +03:00
Jura Shikin 4bd0e932f2 Merge branch 'MALINKA-954/warranty_exchange_message' into 'develop'
MALINKA-954: Warranty exchange message

See merge request raspberry/mobile/ios!189
2023-01-10 17:10:23 +03:00
Elena Nazarova 0e76a70430 MALINKA-954: Warranty exchange message 2023-01-10 17:10:23 +03:00
Jura Shikin 9bbd8b102e Merge branch 'feature/MALINKA-962/Crash_on_selecting_account' into 'develop'
MALINKA-962: Replaced unowned self with weak self

See merge request raspberry/mobile/ios!190
2023-01-10 17:08:51 +03:00
user 645ce066e4 MALINKA-962: Replaced unowned self with weak self 2023-01-10 14:01:33 +03:00
Elena Nazarova 8b28dadf18 Merge branch 'feature/MALINKA-911/flashing_screen_then_delete_last_account' into 'develop'
MALINKA-911: Flashing screen You don't have any accounts

See merge request raspberry/mobile/ios!188
2023-01-10 13:31:07 +03:00
Никита Чирухин 74551cebde MALINKA-911: Flashing screen You don't have any accounts 2023-01-10 13:31:07 +03:00
Никита Чирухин 23aac206bf Merge branch 'MALINKA-955/disable_transactions_purchase' into 'develop'
MALINKA-955: Check if there is enough token amount for transaction

See merge request raspberry/mobile/ios!186
2023-01-09 17:14:42 +03:00
Elena Nazarova 9a8161b864 MALINKA-955: Check if there is enough token amount for transaction 2023-01-09 17:14:42 +03:00
Никита Чирухин c24adc4ece Merge branch 'MALINKA-920/resources_loader' into 'develop'
MALINKA-920: Succeed alert

See merge request raspberry/mobile/ios!185
2023-01-09 15:02:46 +03:00
Elena Nazarova c5720accc8 MALINKA-920: Succeed alert 2023-01-09 15:02:46 +03:00
Jura Shikin 4c62dcad48 Merge branch 'MALINKA-944/headers' into 'develop'
MALINKA-944: Headers

See merge request raspberry/mobile/ios!181
2022-12-30 12:13:27 +03:00
Elena Nazarova c681978462 MALINKA-944: Headers 2022-12-30 12:13:27 +03:00
Juraldinio 72c4f0b4c6 Change addresses for WebSocket connection 2022-12-29 23:36:55 +03:00
Juraldinio b8633b3f54 MALINKA-966 - delete push tokien links with wallet 2022-12-29 23:24:39 +03:00
Jura Shikin 6a9bc68faa Merge branch 'feature/MALINKA-959/Infinite_loader' into 'develop'
MALINKA-959: Fixed infinite loader on exchange and buying resources

See merge request raspberry/mobile/ios!184
2022-12-29 20:37:36 +03:00
Jura Shikin 2e1158f790 Merge branch 'feature/MALINKA-726/RAM_Popups' into 'develop'
MALINKA-726: Buying Ram error popups

See merge request raspberry/mobile/ios!180
2022-12-29 20:36:10 +03:00
Андрей Геращенко 77f79a91f0 MALINKA-726: Buying Ram error popups 2022-12-29 20:36:09 +03:00
Jura Shikin 8df87cd766 Merge branch 'MALINKA-949/button_title' into 'develop'
MALINKA-949: Button title fix

See merge request raspberry/mobile/ios!182
2022-12-29 20:31:57 +03:00
Elena Nazarova 25c30715c6 MALINKA-949: Button title fix 2022-12-29 20:31:56 +03:00
Jura Shikin be52cc354f Merge branch 'feature/MALINKA-906/message_field_overlaps_dialog_box' into 'develop'
MALINKA-906: The message input field overlaps the dialog box

See merge request raspberry/mobile/ios!183
2022-12-29 20:25:44 +03:00
Никита Чирухин d7a88aa5f1 MALINKA-906: The message input field overlaps the dialog box 2022-12-29 20:25:44 +03:00
user 1583762844 MALINKA-959: Fixed infinite loader on exchange and buying resources 2022-12-29 19:55:06 +03:00
Jura Shikin 43155fe846 Merge branch 'feature/MALINKA-917/pin_code_requested_twice' into 'develop'
MALINKA-917: A faceID is requested twice

See merge request raspberry/mobile/ios!178
2022-12-29 12:00:48 +03:00
Никита Чирухин 8be55c7ede MALINKA-917: A faceID is requested twice 2022-12-29 12:00:48 +03:00
Juraldinio bd0a776129 Remove infinite reconnection 2022-12-28 17:48:50 +03:00
Juraldinio 3aa7409ac2 MALINKA-811 - add headers and logs to console 2022-12-28 13:29:23 +03:00
Juraldinio 56e24b4a78 MALINKA-950 - fix crash on exchange controller 2022-12-28 12:21:03 +03:00
Juraldinio 43762fafa6 MALINKA-851 - fix loading tokens 2022-12-27 12:57:12 +03:00
Никита Чирухин 73cf64e420 Merge branch 'MALINKA-920/ram_succeed' into 'develop'
MALINKA-920: Fixed ram buying/selling behaviour

See merge request raspberry/mobile/ios!170
2022-12-27 01:23:40 +03:00
Elena Nazarova b93191b335 MALINKA-920: Fixed ram buying/selling behaviour 2022-12-27 01:23:39 +03:00
Juraldinio 2d1ce6b8fd MALINKA-851 - Optimisations working with API 2022-12-26 22:59:04 +03:00
Jura Shikin a48e179ebc Merge branch 'feature/MALINKA-943' into 'develop'
Sync our develop and PayChash develop branches

See merge request raspberry/mobile/ios!179
2022-12-26 19:33:09 +03:00
Jura Shikin 86952acdaa Sync our develop and PayChash develop branches 2022-12-26 19:33:09 +03:00
Juraldinio c436bc254f MALINKA-874 - fixes selectin creating account 2022-12-26 16:33:23 +03:00
Juraldinio 3136bee08f MALINKA-936 - fix checking resource behaviour 2022-12-26 11:21:29 +03:00
Juraldinio 63597b81f3 Increase version to 1.4.0 2022-12-24 00:44:44 +03:00
Jura Shikin 4e6246e269 Merge branch 'merge_master' into 'develop'
Merge master

See merge request raspberry/mobile/ios!177
2022-12-24 00:20:22 +03:00
Jura Shikin 85e63aae6f Merge branch 'merge_master' into 'master'
Merge master

See merge request raspberry/mobile/ios!176
2022-12-23 18:04:26 +03:00
Elena Nazarova 497e9c4b72 removed duplicate 2022-12-23 17:53:43 +03:00
Elena Nazarova 962585298d Merge branch 'master' into merge_master
# Conflicts:
#	iOS/Wallet/Sources/Main/Controller/MainController.swift
#	iOS/Wallet/Sources/Main/Service/MainService.swift
#	iOS/Wallet/Sources/Wallet/Controller/WalletController.swift
#	iOS/project.yml
2022-12-23 17:40:01 +03:00
Jura Shikin 08c01dedd3 Merge branch 'MALINKA-938/headers' into 'develop'
MALINKA-938: Add headers to get_info

See merge request raspberry/mobile/ios!174
2022-12-23 16:10:26 +03:00
Elena Nazarova b455e5a154 MALINKA-938: Add headers to get_info 2022-12-23 16:10:26 +03:00
Никита Чирухин cb7efbe3b3 Merge branch 'MALINKA-809/log_error_request' into 'develop'
MALINKA-809: Added headers to error tracking request

See merge request raspberry/mobile/ios!173
2022-12-23 11:15:19 +03:00
Elena Nazarova 42ea52bcf3 MALINKA-809: Added headers to error tracking request 2022-12-23 11:15:19 +03:00
Juraldinio 3b117a010b MALINKA-914 - fix navigation after creation by private 2022-12-22 23:25:02 +03:00
Juraldinio 0b33ba74de MALINKA-929 - fix hide loader 2022-12-22 22:06:47 +03:00
Juraldinio ccd40b275c MALINKA-927 - fixes with wallet view controller 2022-12-22 19:12:32 +03:00
Juraldinio 6cf8826e0d Revert "MALINKA-923: Remove requests not from api"
This reverts commit 6cd670a9c2.
2022-12-22 14:16:17 +03:00
Jura Shikin d358c85c08 Merge branch 'MALINKA-929/fix' into 'develop'
MALINKA-924: Headers

See merge request raspberry/mobile/ios!171
2022-12-21 20:26:26 +03:00
Elena Nazarova 520e9ae9d3 MALINKA-924: Headers 2022-12-21 20:26:26 +03:00
Juraldinio 27755cce87 MALINKA-928 - fix logic for select wallets 2022-12-21 18:22:56 +03:00
Juraldinio 675ff396a5 MALINKA-928 - correct selecting accounts 2022-12-21 16:48:44 +03:00
Juraldinio 0b5853e069 MALINKA-926 - fix failed build 2022-12-21 15:49:51 +03:00
Juraldinio 221ec59015 MALINKA-926 - Show error for invalid key 2022-12-21 14:17:13 +03:00
Juraldinio a4373d5ec5 MALINKA-916 - fix activation 2022-12-20 21:47:31 +03:00
Jura Shikin 75430ad4fd Merge branch 'MALINKA-809/log_error' into 'develop'
MALINKA-809: Change url for error tracking

See merge request raspberry/mobile/ios!169
2022-12-20 19:46:45 +03:00
Elena Nazarova cbf9fdd3c5 MALINKA-809: Change url for error tracking 2022-12-20 19:46:45 +03:00
Jura Shikin 2ea9701a50 Merge branch 'MALINKA-827/requests_from_api' into 'develop'
MALINKA-923: Remove requests not from api

See merge request raspberry/mobile/ios!166
2022-12-20 19:23:46 +03:00
Elena Nazarova 6cd670a9c2 MALINKA-923: Remove requests not from api 2022-12-20 19:23:46 +03:00
Juraldinio 1efc74a3f9 MALINKA-916 - fix behaviour 2022-12-20 18:29:37 +03:00
Jura Shikin 7c09b215da Merge branch 'MALINKA-857/default_hyperions' into 'develop'
MALINKA-857: Default hyperions

See merge request raspberry/mobile/ios!163
2022-12-20 11:30:56 +03:00
Elena Nazarova 0e251a0a7d MALINKA-857: Default hyperions 2022-12-20 11:30:56 +03:00
Jura Shikin 5a3c10db48 Merge branch 'MALINKA-809/url_to_log_errors' into 'develop'
MALINKA-809: Change url to log errors

See merge request raspberry/mobile/ios!168
2022-12-19 23:54:39 +03:00
Elena Nazarova e8b2b4dca3 MALINKA-809: Change url to log errors 2022-12-19 23:54:39 +03:00
Jura Shikin ba913ade2a Merge branch 'feature/MALINKA-726/RAM_error_popup' into 'develop'
MALINKA-726: Popups for errors on Buy Ram screen

See merge request raspberry/mobile/ios!164
2022-12-19 17:20:47 +03:00
Никита Чирухин 23d37d4a88 MALINKA-726: Popups for errors on Buy Ram screen 2022-12-19 17:20:47 +03:00
Jura Shikin 328b941581 Merge branch 'feature/MALINKA-811/change_URL_request_web_sockets_to_backed_enviroment' into 'develop'
MALINKA-811: Change the URL to request web sockets to the application

See merge request raspberry/mobile/ios!165
2022-12-19 17:18:21 +03:00
Никита Чирухин 60dbf3c641 MALINKA-811: Change the URL to request web sockets to the application 2022-12-19 17:18:21 +03:00
Juraldinio e0318d5999 MALINKA-828 - new default configuration 2022-12-19 16:53:47 +03:00
Jura Shikin 0846342d14 Merge branch 'MALINKA-801/get_hyperions' into 'develop'
MALINKA-801: Add getEosHyperions

See merge request raspberry/mobile/ios!162
2022-12-19 15:18:14 +03:00
Elena Nazarova eed4af91ef MALINKA-801: Add getEosHyperions 2022-12-19 15:18:14 +03:00
Juraldinio e5c18ed350 nodes loading with try 2022-12-19 14:52:15 +03:00
Juraldinio 4d22d97b36 MALINKA-912 - token push notifications 2022-12-19 13:55:38 +03:00
Jura Shikin ce2de4f36c Merge branch 'MALINKA-828/default_raspberry_backend' into 'develop'
MALINKA-828: Raspberry backend by default

See merge request raspberry/mobile/ios!161
2022-12-19 13:06:30 +03:00
Elena Nazarova 7c0b98c3ce MALINKA-828: Raspberry backend by default 2022-12-19 13:06:30 +03:00
Jura Shikin 0532086cb7 Merge branch 'MALINKA-809/change_url_to_log_errors' into 'develop'
MALINKA-809: When changing environments, change the url to log errors

See merge request raspberry/mobile/ios!160
2022-12-19 13:01:21 +03:00
Elena Nazarova 3521bb43b1 MALINKA-809: When changing environments, change the url to log errors 2022-12-19 12:43:37 +03:00
Jura Shikin e9a43c9595 Merge branch 'MALINKA-781/offer_to_buy_ram' into 'develop'
MALINKA-781: Offer to buy ram for eos

See merge request raspberry/mobile/ios!158
2022-12-19 12:36:59 +03:00
Elena Nazarova 9b9c1d9527 MALINKA-781: Offer to buy ram for eos 2022-12-19 12:36:59 +03:00
Jura Shikin fa176eaf9d Merge branch 'MALINKA-760/ram_contract' into 'develop'
MALINKA-760: Ram contract

See merge request raspberry/mobile/ios!159
2022-12-19 12:25:56 +03:00
Elena Nazarova 0e11479be6 MALINKA-760: Ram contract 2022-12-19 12:25:56 +03:00
Juraldinio 723abd03c6 Fix misstyped 2022-12-19 12:13:06 +03:00
Juraldinio a34e0f32d1 Build app 2022-12-18 17:56:34 +03:00
Juraldinio bcd58591ec Add unit tests to main application 2022-12-18 16:22:09 +03:00
Juraldinio 98fa0aa835 Fix crash application when no Hyperions or incorrect version 2022-12-17 23:37:59 +03:00
Juraldinio 029044058d Small improvements for speed in version parsing 2022-12-16 19:41:20 +03:00
Juraldinio 9ecda74db9 MALINKA-901 - fix cancelling and passing biometrics 2022-12-16 18:55:34 +03:00
Jura Shikin a985c7f524 Merge branch 'MALINKA-909/back_to_accounts' into 'develop'
MALINKA-909: Check if is on boarding

See merge request raspberry/mobile/ios!156
2022-12-16 16:20:43 +03:00
Elena Nazarova 5204f69be5 MALINKA-909: Check if is on boarding 2022-12-16 16:20:43 +03:00
Jura Shikin ef66ee2473 Merge branch 'MALINKA-476/fixes' into 'develop'
MALINKA-476: UI fixes

See merge request raspberry/mobile/ios!155
2022-12-16 14:51:14 +03:00
Elena Nazarova 95e9d937da MALINKA-476: UI fixes 2022-12-16 14:51:14 +03:00
Juraldinio f2a9d3ab97 MALINKA-904 - fix flashing resource setup 2022-12-16 13:20:53 +03:00
Juraldinio f118c3db53 MALINKA-896, MALINKA-901 - fix crash on loading app 2022-12-16 13:06:02 +03:00
Никита Чирухин 5a31865355 Merge branch 'MALINKA-868/to_welcome' into 'develop'
MALINKA-868: When no accounts show welcome screen

See merge request raspberry/mobile/ios!154
2022-12-16 11:06:52 +03:00
Elena Nazarova b8d24912a0 MALINKA-868: When no accounts show welcome screen 2022-12-16 11:06:52 +03:00
Juraldinio 80fbb82695 MALINKA-849 - show unaccepted accounts 2022-12-15 13:58:27 +03:00
Juraldinio fb55b3b5ba MALINKA-897 - save statuses 2022-12-15 13:50:52 +03:00
Juraldinio 296bacdf9a MALINKA-477 - add refresh after return to application 2022-12-15 13:14:52 +03:00
Juraldinio b77b486ab9 MALINKA-889 - remove unnecessary code 2022-12-15 12:54:09 +03:00
Juraldinio ccb332c7b6 MALINKA-477 - request on restore and on refresh 5 times 2022-12-15 00:39:42 +03:00
Juraldinio 87d399b6cf MALINKA-849 - fix flow with accounts 2022-12-15 00:32:57 +03:00
Juraldinio 711d3f474a MALINKA-858 - Correct show account type 2022-12-14 23:28:32 +03:00
Juraldinio 437e21b841 Remove Notifications. Use Combine version. 2022-12-14 23:16:59 +03:00
Juraldinio 822f39d103 Remove didChangeAccount notification from applciation 2022-12-14 22:10:49 +03:00
Juraldinio 8715aa5e0e MALINKA-890 - Rollback 2022-12-14 19:47:56 +03:00
Juraldinio 00677ab22c Remove warning 2022-12-14 18:59:43 +03:00
Juraldinio fdc037ad03 MALINKA-851 - reduce api calling 2022-12-14 18:32:58 +03:00
Juraldinio 9db17e992c MALINKA-890 - do not show empty account view 2022-12-14 18:32:58 +03:00
Jura Shikin 57f58be5aa Merge branch 'MALINKA-869/successful_deal' into 'develop'
MALINKA-869: Changed conditions of succesful deal

See merge request raspberry/mobile/ios!151
2022-12-14 18:32:01 +03:00
Elena Nazarova 18852a91a1 MALINKA-869: Changed conditions of succesful deal 2022-12-14 18:32:01 +03:00
Juraldinio 2ac95d779e MALINKA-352 - disable Delete button 2022-12-14 13:53:43 +03:00
Jura Shikin 36fbf8e9f2 Merge branch 'MALINKA-851/load_resources' into 'develop'
MALINKA-851: Load resources fix

See merge request raspberry/mobile/ios!150
2022-12-14 13:25:51 +03:00
Elena Nazarova a88ff0b35a MALINKA-851: Load resources fix 2022-12-14 13:25:51 +03:00
Jura Shikin 27076b15cd Merge branch 'MALINKA-868/welcome' into 'develop'
MALINKA-868: Added logic when account is being removed

See merge request raspberry/mobile/ios!148
2022-12-14 12:19:38 +03:00
Elena Nazarova e8646781b2 MALINKA-868: Added logic when account is being removed 2022-12-14 12:19:38 +03:00
Juraldinio e104b84db5 Restore copy Package.resolved 2022-12-14 01:02:28 +03:00
Juraldinio 5f4d969618 MALINKA-849 - create account 2022-12-14 00:58:40 +03:00
Juraldinio fe11ccf7d1 Remove warnings 2022-12-13 22:51:28 +03:00
Juraldinio 036f185194 Remove lint warnings 2022-12-13 22:46:16 +03:00
Juraldinio be2c6c1961 Remove warnings 2022-12-13 22:30:08 +03:00
Juraldinio 9abb34a4b4 MALINKA-849 - allow update on drag down 2022-12-13 20:28:07 +03:00
Juraldinio 2f47853a72 Remove copy Package.resolved 2022-12-13 20:10:48 +03:00
Juraldinio 5bce821cf0 Fix build Archive 2022-12-13 19:02:44 +03:00
Juraldinio 3885d5099c Fix calling UI changes on background thread 2022-12-13 16:01:41 +03:00
Juraldinio c317b22a93 Fix build script 2022-12-13 14:38:18 +03:00
Juraldinio 2b6277d3c6 Fix build before leaving Ventura OS 2022-12-13 14:32:16 +03:00
Juraldinio b502d2acd2 Correct scripts and restore Test stage 2022-12-13 09:50:37 +03:00
Juraldinio 55ab759b6d disable all lint rules for NetworkModelGraphQL 2022-12-12 21:42:41 +03:00
Juraldinio ada74ee74d Remove lint errors 2022-12-12 21:35:59 +03:00
Juraldinio e00d8feb4c Fix yaml after remove tests 2022-12-12 21:02:47 +03:00
Juraldinio 3480df6e5e Temporary remove tests 2022-12-12 21:01:25 +03:00
Juraldinio daf706eb36 MALINKA-858 - change private key 2022-12-12 19:19:40 +03:00
Juraldinio aae94ddc49 MALINKA-861 - partial. Remove update token after delete all 2022-12-08 16:45:07 +03:00
Juraldinio 967ec6f9d5 MALINKA-862 - Switch account to new created 2022-12-08 16:17:02 +03:00
Juraldinio 2a2bef0946 MALINKA-856 - fix PIN changes 2022-12-08 15:31:06 +03:00
Jura Shikin 3c082b5a97 Merge branch 'MALINKA-845/tabbar_fix' into 'develop'
MALINKA-827: tabbar fix

See merge request raspberry/mobile/ios!146
2022-12-08 13:34:35 +03:00
Elena Nazarova 6a9a488503 MALINKA-827: tabbar fix 2022-12-08 13:11:27 +03:00
Juraldinio c35b01614c MALINKA-849 - Fix check status for account 2022-12-08 12:55:47 +03:00
Juraldinio b61c219095 MALINKA-856 - fix saving private key 2022-12-07 21:06:43 +03:00
Juraldinio dbc7c4c0f7 MALINKA-850 - decrypt chat 2022-12-07 18:02:53 +03:00
Juraldinio 9f26d225c3 MALINKA-847 - fix auth biometrics 2022-12-07 14:33:40 +03:00
Juraldinio f3bf66d565 MALINKA-840 - fix dropping accounts across versions 2022-12-07 13:25:38 +03:00
Jura Shikin 1eff29e3b5 Merge branch 'feature/MALINKA-663/Incorrect_switch_to_free_transactions' into 'develop'
MALINKA-663: Reorganised logic of switching transactions type while buying RAM...

See merge request raspberry/mobile/ios!140
2022-12-06 23:40:16 +03:00
Андрей Геращенко 351e1f7997 MALINKA-663: Reorganised logic of switching transactions type while buying RAM... 2022-12-06 23:40:16 +03:00
Jura Shikin 671eb28e4f Merge branch 'feature/WalletStatus' into 'develop'
WiP

See merge request raspberry/mobile/ios!143
2022-12-06 23:00:21 +03:00
Jura Shikin ab8702c265 Wallet status 2022-12-06 23:00:21 +03:00
Jura Shikin 5fca5e7bab Merge branch 'MALINKA-732/autolayout_fixes' into 'develop'
MALINKA-732: Chat autolayout fix

See merge request raspberry/mobile/ios!141
2022-12-06 11:46:44 +03:00
Elena Nazarova 6b2444bfb4 MALINKA-732: Chat autolayout fix 2022-12-06 11:46:44 +03:00
Андрей Геращенко 5201889c76 Merge branch 'feature/MALINKA-726/RAM_error_popups' into 'develop'
Resolve MALINKA-726 Popups for errors on "Buy Ram" screen

See merge request raspberry/mobile/ios!138
2022-12-02 12:05:49 +03:00
Андрей Геращенко 2c4c5e858b Resolve MALINKA-726 Popups for errors on "Buy Ram" screen 2022-12-02 12:05:49 +03:00
Jura Shikin f474652e47 Merge branch 'feature/MALINKA-744/error_AutoLayout_creating_account' into 'develop'
MALINKA-744: Error with AutoLayout when creating an account

See merge request raspberry/mobile/ios!133
2022-12-01 11:53:49 +03:00
Никита Чирухин 0fbc8833a4 MALINKA-744: Error with AutoLayout when creating an account 2022-12-01 11:53:49 +03:00
Андрей Геращенко e56bab499f Merge branch 'MALINKA-747/release_cpu_autolayout' into 'develop'
MALINKA-747: Release/return cpu autolayout

See merge request raspberry/mobile/ios!135
2022-12-01 11:20:26 +03:00
Elena Nazarova fe531fb6c5 MALINKA-747: Release/return cpu autolayout 2022-12-01 11:20:26 +03:00
Elena Nazarova d9cc0d43c6 Merge branch 'MALINKA-791/cryptocash-release-button-hide' into 'develop'
MALINKA-791: hid cryptocash release button for list tokens

See merge request raspberry/mobile/ios!134
2022-11-30 18:23:50 +03:00
Elena Nazarova cf3872690b MALINKA-791: hid cryptocash release button for list tokens 2022-11-30 18:23:50 +03:00
Андрей Геращенко b02caf33f2 Merge branch 'feature/MALINKA-737/error_with_AutoLayout_account_resources_screen' into 'develop'
MALINKA-737: Error with AutoLayout on account resources screen

See merge request raspberry/mobile/ios!130
2022-11-29 14:03:13 +03:00
Никита Чирухин eb10cd8481 MALINKA-737: Error with AutoLayout on account resources screen 2022-11-29 14:03:12 +03:00
Никита Чирухин 2d3c32709f Merge branch 'feature/MALINKA-726/RAM_error_popups' into 'develop'
Resolve MALINKA-726 Fixed design for errors on bying ram

See merge request raspberry/mobile/ios!127
2022-11-24 21:36:54 +03:00
Андрей Геращенко f4bd507f08 Resolve MALINKA-726 Fixed design for errors on bying ram 2022-11-24 21:36:54 +03:00
Никита Чирухин 3cde524869 Merge branch 'MALINKA-751/autolayout_add_account' into 'develop'
MALINKA-751: Autolayout add token

See merge request raspberry/mobile/ios!129
2022-11-24 21:36:30 +03:00
Elena Nazarova c16d3bdd26 MALINKA-751: Autolayout add token 2022-11-24 21:36:30 +03:00
Jura Shikin e25df20907 Merge branch 'feature/MALINKA-770/URL_card_withdrawal' into 'develop'
Resolve MALINKA-770 "Feature//url card withdrawal"

See merge request raspberry/mobile/ios!128
2022-11-24 11:21:30 +03:00
Андрей Геращенко e27c464216 Resolve MALINKA-770 "Feature//url card withdrawal" 2022-11-24 11:21:30 +03:00
Андрей Геращенко a675ecfff2 Merge branch 'MALINKA-732/autolayout_sell' into 'develop'
MALINKA-732: Fixed autolayout warnings on sell screen

See merge request raspberry/mobile/ios!126
2022-11-24 10:56:21 +03:00
Elena Nazarova 70b04a31d8 MALINKA-732: Fixed autolayout warnings on sell screen 2022-11-24 10:56:21 +03:00
Jura Shikin 0620258f4f Merge branch 'feature/MALINKA-770/New_card_withdrawal' into 'master'
MALINKA-770: Added handle of wurl and wbtn in app settings

See merge request raspberry/mobile/ios!125
2022-11-24 09:36:08 +03:00
Андрей Геращенко fc465325e6 MALINKA-770: Added handle of wurl and wbtn in app settings 2022-11-24 09:36:08 +03:00
Elena Nazarova da3bcecc94 Merge branch 'feature/MALINKA-725/disable_raspberries_warranty_exchange' into 'develop'
MALINKA-725: Disable raspberries exchange

See merge request raspberry/mobile/ios!124
2022-11-22 11:18:06 +03:00
Никита Чирухин 2a6b269316 MALINKA-725: Disable raspberries exchange 2022-11-22 11:18:06 +03:00
Jura Shikin 8f2458b71c Merge branch 'feature/MALINKA-631/errors_with_AutoLayout' into 'develop'
MALINKA-631: Errors with AutoLayout

See merge request raspberry/mobile/ios!119
2022-11-22 08:49:20 +03:00
Никита Чирухин 2fac50e903 MALINKA-631: Errors with AutoLayout 2022-11-22 08:49:20 +03:00
Jura Shikin 2d7181942f Merge branch 'MALINKA-749/token_to_self' into 'develop'
MALINKA-749: Can't send token to self

See merge request raspberry/mobile/ios!123
2022-11-22 08:42:36 +03:00
Elena Nazarova 539484bf59 MALINKA-749: Can't send token to self 2022-11-22 08:42:35 +03:00
Jura Shikin 0b8685b9f9 Merge branch 'feature/MALINKA-726/RAM_error_popups' into 'develop'
Resolve MALINKA-726 "Feature//ram error popups"

See merge request raspberry/mobile/ios!121
2022-11-18 13:23:30 +03:00
Андрей Геращенко 99efa4cbe5 Resolve MALINKA-726 "Feature//ram error popups" 2022-11-18 13:23:29 +03:00
Никита Чирухин 3cc383b101 Merge branch 'feature/MALINKA-727/Sell_correct_ram_amount' into 'develop'
MALINKA-727: Fixed minimal amount of RAM to sell

See merge request raspberry/mobile/ios!120
2022-11-17 13:08:48 +03:00
Андрей Геращенко 37dc41ed18 MALINKA-727: Fixed minimal amount of RAM to sell 2022-11-17 13:08:47 +03:00
Андрей Геращенко 99701611f5 Merge branch 'MALINKA-664/resources_0' into 'develop'
MALINKA-664: fixed titles, better calculations

See merge request raspberry/mobile/ios!113
2022-11-17 12:16:19 +03:00
Elena Nazarova 4d49b7235b MALINKA-664: fixed titles, better calculations 2022-11-17 12:16:19 +03:00
Juraldinio d922c68639 MALINKA-736 - Headers for backend only for Malinka.life 2022-11-16 21:35:58 +03:00
Андрей Геращенко 1031e2b344 Merge branch 'feature/MALINKA-725/disable_raspberries_warranty_exchange' into 'develop'
MALINKA-725: Disable raspberries exchange

See merge request raspberry/mobile/ios!117
2022-11-16 15:24:36 +03:00
Никита Чирухин 6d06f8d928 MALINKA-725: Disable raspberries exchange 2022-11-16 15:24:36 +03:00
Juraldinio 14fb9a7fd5 Pass headers to backend 2022-11-16 13:58:39 +03:00
Jura Shikin 5d5804357e Merge branch 'feature/MALINKA-736/Settings' into 'develop'
MALINKA-736 - Upddate settings in application

See merge request raspberry/mobile/ios!118
2022-11-16 13:11:25 +03:00
Juraldinio e925d8045a MALINKA-736 - Upddate settings in application 2022-11-16 12:57:42 +03:00
Андрей Геращенко 4dc3315f4e Merge branch 'MALINKA-706/locale' into 'develop'
MALINKA-706: localized resources capacity title

See merge request raspberry/mobile/ios!115
2022-11-14 15:06:16 +03:00
Андрей Геращенко 26718f1619 Merge branch 'feature/MALINKA-631/errors_with_AutoLAyout' into 'develop'
MALINKA-631: Errors with AutoLayout

See merge request raspberry/mobile/ios!114
2022-11-14 13:53:17 +03:00
Никита Чирухин 6588a26a27 MALINKA-631: Errors with AutoLayout 2022-11-14 13:53:17 +03:00
Андрей Геращенко 3bde096265 Merge branch 'feature/MALINKA-709/tabbar_background_fix' into 'develop'
MALINKA-709: Tabbar background fix

See merge request raspberry/mobile/ios!116
2022-11-14 11:22:53 +03:00
Никита Чирухин 8d2c261e3b MALINKA-709: Tabbar background fix 2022-11-14 11:22:53 +03:00
Jura Shikin 34b695ed2d Merge branch 'MALINKA-697/resources_no_network_crash' into 'develop'
MALINKA-697: fix crash when no network

See merge request raspberry/mobile/ios!110
2022-11-11 12:24:17 +03:00
Elena Nazarova 812456d2a9 MALINKA-697: fix crash when no network 2022-11-11 12:24:17 +03:00
Elena Nazarova 541b97179e MALINKA-706: localized resources capacity title 2022-11-11 12:14:43 +03:00
Jura Shikin bd5c0f3add Merge branch 'feature/MALINKA-702/Forbid_zero_buy' into 'develop'
MALINKA-702: Fixed opportunity to buy zero RAM

See merge request raspberry/mobile/ios!111
2022-11-10 12:04:40 +03:00
Андрей Геращенко 38cd5a9e19 MALINKA-702: Fixed opportunity to buy zero RAM 2022-11-10 12:04:40 +03:00
Андрей Геращенко 10c65f0bc6 Merge branch 'MALINKA-688/resources_settings' into 'develop'
MALINKA-688: Back to resources setup

See merge request raspberry/mobile/ios!108
2022-11-09 17:50:16 +03:00
Elena Nazarova 5070a64f40 MALINKA-688: Back to resources setup 2022-11-09 17:50:16 +03:00
Никита Чирухин 6b7737895a Merge branch 'MALINKA-689/resources_error_title' into 'develop'
MALINKA-689: fix error title

See merge request raspberry/mobile/ios!109
2022-11-09 16:59:12 +03:00
Никита Чирухин ec04220aa0 Merge branch 'MALINKA-599/tabbar-2' into 'develop'
MALINKA-599: Tabbar fixes

See merge request raspberry/mobile/ios!107
2022-11-09 16:58:20 +03:00
Elena Nazarova 5d10b7b1f3 MALINKA-599: Tabbar fixes 2022-11-09 16:58:20 +03:00
Elena Nazarova bdf31e10f2 MALINKA-689: fix error title 2022-11-09 14:24:18 +03:00
Juraldinio 5345b2884b Improvements in optioal template 2022-11-08 12:28:22 +03:00
Juraldinio 28703c85c2 MALINKA-622 - fix crash
When not selected environment
2022-11-08 12:17:25 +03:00
Jura Shikin f417c23ffb Merge branch 'MALINKA-654/remove_unused_constants' into 'develop'
MALINKA-654: Remove unused constants

See merge request raspberry/mobile/ios!104
2022-11-08 11:58:52 +03:00
Никита Чирухин 00f8b69799 MALINKA-654: Remove unused constants 2022-11-08 11:58:52 +03:00
Juraldinio be6b3cd276 Rename enmironment to environment 2022-11-07 15:16:15 +03:00
Jura Shikin 1d2532b138 Merge branch 'MALINKA-601/chat-notifications' into 'develop'
MALINKA-601: unread messages update fix

See merge request raspberry/mobile/ios!100
2022-11-07 14:56:23 +03:00
Elena Nazarova ad18dcb1a1 MALINKA-601: unread messages update fix 2022-11-07 14:56:23 +03:00
Jura Shikin 58de513bc1 Merge branch 'MALINKA-664/resources' into 'develop'
MALINKA-664: Resources

See merge request raspberry/mobile/ios!103
2022-11-07 14:55:53 +03:00
Elena Nazarova 0e30d0da79 MALINKA-664: Resources 2022-11-07 14:55:53 +03:00
Jura Shikin 307e486e1f Merge branch 'feature/MALINKA-596/bug_with_screen_update' into 'develop'
MALINKA-596: Bug with screen update

See merge request raspberry/mobile/ios!102
2022-11-03 11:27:08 +03:00
Никита Чирухин b35b352abe MALINKA-596: Bug with screen update 2022-11-03 11:27:08 +03:00
Juraldinio ca504cb08b Up version to 1.3.0 2022-11-03 11:22:20 +03:00
Jura Shikin e4a7f8aa0c Merge branch 'feature/MALINKA-646/Memory_leaks' into 'develop'
MALINKA-646: Fixed navigation-affected memory leaks

See merge request raspberry/mobile/ios!97
2022-11-03 10:57:30 +03:00
Андрей Геращенко d782addf9f MALINKA-646: Fixed navigation-affected memory leaks 2022-11-03 10:57:30 +03:00
Jura Shikin b03c63bd9f Merge branch 'develop' into 'master'
Release 1.2.3

See merge request raspberry/mobile/ios!101
2022-11-02 19:27:30 +03:00
Juraldinio 85052c55f1 MALINKA-662 - fix “you deposited” description message 2022-11-02 15:15:09 +03:00
Juraldinio 464f1cc182 Version 1.2.3 2022-11-02 14:03:51 +03:00
Juraldinio cd9be1dbec MALINKA-595 - fix showswitcher for transaction kind 2022-11-02 14:03:01 +03:00
Jura Shikin 2f4c2edbd3 Merge branch 'MALINKA-590/chat-extra-back-arrow' into 'develop'
MALINKA-590: hide navigationItem if there is one already

See merge request raspberry/mobile/ios!96
2022-11-02 12:08:36 +03:00
Elena Nazarova c932f29f2a MALINKA-590: hide navigationItem if there is one already 2022-11-02 12:08:35 +03:00
Juraldinio 5cf6f2190e MALINKA-596 - fix UIRefreshControl behaviour 2022-11-01 22:46:52 +03:00
Juraldinio d4fa122f5d Update versino to 1.3.0 2022-11-01 15:56:38 +03:00
Jura Shikin cc428dcfcc Merge branch 'feature/MALINKA-653' into 'develop'
MALINKA-653 - Unit tests in project

See merge request raspberry/mobile/ios!98
2022-11-01 15:42:20 +03:00
Jura Shikin e3a6643eb0 MALINKA-653 - Unit tests in project 2022-11-01 15:42:20 +03:00
Jura Shikin 1b7a254106 Merge branch 'MALINKA-619/Bug_with_environment' into 'develop'
MALINKA-619: Bug with environment when installing the release version on top of the beta

See merge request raspberry/mobile/ios!95
2022-11-01 13:54:05 +03:00
Никита Чирухин 5a43fb1b79 MALINKA-619: Bug with environment when installing the release version on top of the beta 2022-11-01 13:54:05 +03:00
Jura Shikin 963cf15149 Merge branch 'MALINKA-588/chat-artifacts-update' into 'develop'
MALINKA-588: Chat and wallet cards artifacts

See merge request raspberry/mobile/ios!93
2022-10-31 15:15:01 +03:00
Elena Nazarova 5743654658 MALINKA-588: Chat and wallet cards artifacts 2022-10-31 15:15:01 +03:00
Juraldinio 8a072e714d Fix misstyped in builscript 2022-10-27 19:43:56 +03:00
Jura Shikin a132647daa Merge branch 'develop' into 'master'
Release 1.2.2

See merge request raspberry/mobile/ios!94
2022-10-27 19:26:54 +03:00
Juraldinio dc50893c99 Vestion 1.2.2 2022-10-27 17:46:16 +03:00
Juraldinio 0ee67de6aa MALINKA-626 - fix show tokens icons 2022-10-27 16:48:13 +03:00
Juraldinio 57a1d29dfc Update Realm to 10.32.1 2022-10-27 15:39:22 +03:00
Juraldinio 6b536d86da MALINKA-627 - remove comission 2022-10-27 13:59:26 +03:00
Juraldinio 4c50c2e30e MALINKA-626 - fix show tokens icons 2022-10-26 23:39:28 +03:00
Juraldinio b393626df9 MALINKA-626 - fix show tokens icons 2022-10-26 19:29:04 +03:00
Juraldinio cdf7aa27f6 Update to 1.3.0 version 2022-10-26 18:23:29 +03:00
Juraldinio 411eb9510f Correct builds. 2022-10-26 18:22:50 +03:00
Jura Shikin 55a36f48b7 Merge branch 'develop' into 'master'
Release 1.2.1

See merge request raspberry/mobile/ios!91
2022-10-26 18:07:09 +03:00
Juraldinio 00a8cfe1e5 Fix 1.2.1 correcting request table 2022-10-26 16:42:18 +03:00
Juraldinio 7fea607a40 MALINKA-613 - Correct builds for tag 2022-10-25 22:54:29 +03:00
Juraldinio 001c85357e Add show version in alert 2022-10-25 21:40:11 +03:00
Juraldinio 2db21f4632 Increase version to 1.3.0 2022-10-25 21:31:38 +03:00
Juraldinio 2c94a109c9 MALINKA-608 - When account locked we can’t change it 2022-10-25 21:29:50 +03:00
Jura Shikin dadb0ab265 Merge branch 'develop' into 'master'
Release 1.2.0

See merge request raspberry/mobile/ios!90
2022-10-25 19:22:19 +03:00
Juraldinio 3ef732f1f4 Small improvements on ExchangeCryptocashViewController 2022-10-24 21:17:17 +03:00
Juraldinio 4e22b4dcbf Merge branch 'feature/MALINKA-591' into develop 2022-10-24 19:02:36 +03:00
Juraldinio dc64504e60 MALINKA-591 - fix update info about resources 2022-10-24 19:02:23 +03:00
Juraldinio 98ea0ff49c Fix for build. Local version for Branch.io package. 2022-10-24 11:53:12 +03:00
Juraldinio bfcafdee43 Update Branch.io version dependency 2022-10-23 23:38:25 +03:00
Juraldinio e4e899d4d6 MALINKA-554 - Switch environments by Settings 2022-10-23 20:55:42 +03:00
Juraldinio 4b1062cc49 Forgotten changes in table names 2022-10-19 11:50:12 +03:00
Juraldinio a98e25177d Revert "Revert after check"
This reverts commit b14f23c52a.
2022-10-19 11:03:18 +03:00
Juraldinio b14f23c52a Revert after check 2022-10-19 09:44:03 +03:00
Juraldinio c20ad486c6 Contracts for preprod and small fixes in logic 2022-10-18 23:49:59 +03:00
Juraldinio 76a73b1841 Temporary Build 2022-10-17 18:55:04 +03:00
y.shikin 56f64b2226 Remove double quotes 2022-10-17 17:27:02 +03:00
Igor Lunev 333211f4d6 Update .gitlab-ci.yml file 2022-10-13 22:39:34 +03:00
Jura Shikin 41a2ca49d8 Merge branch 'feature/MALINKA-552/UI_bag_on_sell_buy_screen' into 'develop'
MALINKA-552: UI bug on the Sell/Buy screen

See merge request raspberry/mobile/ios!83
2022-10-13 12:36:27 +03:00
Никита Чирухин 432d814d88 MALINKA-552: UI bug on the Sell/Buy screen 2022-10-13 12:36:26 +03:00
Jura Shikin 6628cf639f Merge branch 'feature/MALINKA-524/error_not_enough_RAM_translat' into 'develop'
MALINKA-524: Error "Not enough RAM" in the Russian version of the application

See merge request raspberry/mobile/ios!81
2022-10-12 11:46:26 +03:00
Никита Чирухин f47a81fa89 MALINKA-524: Error "Not enough RAM" in the Russian version of the application 2022-10-12 11:46:26 +03:00
Juraldinio aeefedeafd Upload debug symbols to Crashlytics 2022-10-12 00:04:28 +03:00
Juraldinio 6211ffd317 MALINKA-548 - fix crashed on send 2022-10-11 15:44:04 +03:00
Juraldinio 76c283263e Change swap1 to swap 2022-10-11 15:09:28 +03:00
Jura Shikin 399c9225a8 Merge branch 'feature/MALINKA-550/empty_view_bag_on_buy_sell_screen' into 'develop'
MALINKA-550: The background on the "Sell" and "Buy" screens does not shift when updating

See merge request raspberry/mobile/ios!82
2022-10-10 15:29:42 +03:00
Никита Чирухин d9e970e0b8 MALINKA-550: The background on the "Sell" and "Buy" screens does not shift when updating 2022-10-10 15:29:42 +03:00
Juraldinio 42d672c84b MALINKA-513 - remove redirection to wockjabber.xyz 2022-10-09 22:25:52 +03:00
Juraldinio fae1e8241c MALINKA-513 - fix crash on buy button pressed 2022-10-07 22:17:45 +03:00
Juraldinio 85c1285ea4 MALINKA-528 - add crashlytics 2022-10-07 21:29:23 +03:00
Juraldinio f6706142a9 Fix crash on start after biometrics 2022-10-07 21:26:07 +03:00
Juraldinio 2d19546787 Fix hide banner buttons 2022-10-07 14:37:55 +03:00
Juraldinio 23b9e05807 Hide navigation bar on show history 2022-10-07 14:06:05 +03:00
Juraldinio ac605de375 Fix crash on USDT 2022-10-07 13:40:58 +03:00
Juraldinio e5b03b38de MALINKA-523 - disable progress statuses 2022-10-07 12:16:32 +03:00
Jura Shikin be92d693e0 Merge branch 'feature/PayCashSync' into 'develop'
Feature/pay cash sync

See merge request raspberry/mobile/ios!79
2022-10-06 16:17:16 +03:00
Jura Shikin 253a974f4a Feature/pay cash sync 2022-10-06 16:17:16 +03:00
Андрей Геращенко 34864ebb7b Merge branch 'MALINKA-505/extra_white_header' into 'develop'
MALINKA-505: Extra white header

See merge request raspberry/mobile/ios!75
2022-10-03 16:06:58 +03:00
Elena Nazarova 6fd7bf4b13 MALINKA-505: Extra white header 2022-10-03 16:06:58 +03:00
Elena Nazarova e6b096f6e6 Merge branch 'feature/MALINKA-503/AccountsList_artifacts' into 'develop'
MALINKA-503: Fixed artifacts on accounts screen opening

See merge request raspberry/mobile/ios!72
2022-10-03 15:17:57 +03:00
Jura Shikin 0fcd756add Merge branch 'MALINKA-508/chat_notifications' into 'develop'
MALINKA-508: if no accounts set message badge to zero

See merge request raspberry/mobile/ios!74
2022-09-30 21:43:54 +03:00
Jura Shikin ebd339af25 Merge branch 'MALINKA-509/remove-dash' into 'develop'
MALINKA-509: removed dash

See merge request raspberry/mobile/ios!73
2022-09-30 21:36:34 +03:00
Elena Nazarova a3b5fed569 MALINKA-508: if no accounts set message badge to zero 2022-09-30 19:39:00 +03:00
Elena Nazarova 973065a9ee MALINKA-509: removed dash 2022-09-30 13:50:18 +03:00
user ca0a0cb494 MALINKA-503: Fixed artifacts on accounts screen opening 2022-09-29 19:17:20 +03:00
Jura Shikin 6d5ce05b98 Merge branch 'MALINKA-479/Failed' into 'develop'
MALINKA-479: Wallet statuses UI

See merge request raspberry/mobile/ios!71
2022-09-29 15:09:19 +03:00
Elena Nazarova ee989e4aaa MALINKA-479: Wallet statuses UI 2022-09-29 15:09:19 +03:00
Андрей Геращенко 7c1e70da34 Merge branch 'feature/MALINKA-476/Loader' into 'develop'
MALINKA-476: Add loader before account created

See merge request raspberry/mobile/ios!70
2022-09-26 19:20:54 +03:00
Elena Nazarova 6b87a8fb91 MALINKA-476: Add loader before account created 2022-09-26 19:20:54 +03:00
Jura Shikin e03b2bfb73 Merge branch 'feature/MALINKA-470/artifact_QR_scan_camera' into 'develop'
MALINKA-470: Artifacts when scanning a QR code by the camera

See merge request raspberry/mobile/ios!68
2022-09-26 11:27:45 +03:00
Никита Чирухин 12659f02ba MALINKA-470: Artifacts when scanning a QR code by the camera 2022-09-26 11:27:44 +03:00
Jura Shikin 8af4141ec4 Merge branch 'feature/MALINKA-466/TRX_IN_CHAIN' into 'develop'
Resolve MALINKA-466 "Feature//trx in chain"

See merge request raspberry/mobile/ios!69
2022-09-23 21:35:57 +03:00
Jura Shikin 0ef27447e9 Resolve MALINKA-466 "Feature//trx in chain" 2022-09-23 21:35:57 +03:00
Jura Shikin 47a3c5aac0 Merge branch 'feature/MALINKA-452/searchfield_bug' into 'develop'
MALINKA-452: textfield resigns on changing selected section

See merge request raspberry/mobile/ios!66
2022-09-20 19:00:11 +03:00
Elena Nazarova 5468e093be MALINKA-452: textfield resigns on changing selected section 2022-09-20 19:00:11 +03:00
Jura Shikin 83feaac990 Merge branch 'feature/MALINKA-458/glitch_transfer_of_funds' into 'develop'
MALINKA-458: Artifacts when navigating from the Wallet screen

See merge request raspberry/mobile/ios!65
2022-09-19 19:20:00 +03:00
Никита Чирухин ea7b5f5fbd MALINKA-458: Artifacts when navigating from the Wallet screen 2022-09-19 19:20:00 +03:00
Jura Shikin 7d96161bd7 Merge branch 'feature/MALINKA-457/glitch_add_tokens' into 'develop'
MALINKA-457: Artefacts on the "Add Tokens" screen

See merge request raspberry/mobile/ios!64
2022-09-19 15:03:05 +03:00
Никита Чирухин 09944289f3 MALINKA-457: Artefacts on the "Add Tokens" screen 2022-09-19 15:03:05 +03:00
y.shikin 5129fad5ae Increase app version to 1.2.0 2022-09-15 17:55:49 +03:00
Jura Shikin e1f10f4044 Merge branch 'feature/MALINKA-419/animation_bag_my_applications' into 'develop'
MALINKA-419: Animation bug on My applications screen

See merge request raspberry/mobile/ios!63
2022-09-15 15:13:41 +03:00
Никита Чирухин 29b5f0ad80 MALINKA-419: Animation bug on My applications screen 2022-09-15 15:13:41 +03:00
Jura Shikin 6ec3a8ae1d Merge branch 'feature/MALINKA-366/loader_assemble_malinka' into 'develop'
MALINKA-366: hide loader then collect mlnk

See merge request raspberry/mobile/ios!59
2022-09-15 11:59:37 +03:00
Никита Чирухин bf9d0ec9bc MALINKA-366: hide loader then collect mlnk 2022-09-15 11:59:37 +03:00
Jura Shikin 8f1314c87a Merge branch 'feature/MALINKA-398/glitch_navigation_resources' into 'develop'
MALINKA-398: Artifacts on the "Resources" screen

See merge request raspberry/mobile/ios!57
2022-09-10 10:49:14 +03:00
Никита Чирухин df450b50b4 MALINKA-398: Artifacts on the "Resources" screen 2022-09-10 10:49:14 +03:00
Jura Shikin 18de060a8f Merge branch 'feature/MALINKA-399/glitch_navigation_currency_exchange' into 'develop'
MALINKA-399: Artifacts on the "Currency exchange" screen

See merge request raspberry/mobile/ios!58
2022-09-10 10:48:20 +03:00
Никита Чирухин 7939ab13cc MALINKA-399: Artifacts on the "Currency exchange" screen 2022-09-10 10:48:19 +03:00
Jura Shikin 5ef3124fa3 Merge branch 'feature/MALINKA-397/glitch_navigation_my_account' into 'develop'
MALINKA-397: Artifacts on the "My Accounts" screen

See merge request raspberry/mobile/ios!54
2022-09-09 21:58:23 +03:00
Никита Чирухин f0bdcbd69c MALINKA-397: Artifacts on the "My Accounts" screen 2022-09-09 21:58:23 +03:00
Jura Shikin 5a5477b852 Merge branch 'feature/MALINKA-389/error_text_fix' into 'develop'
MALINKA-389: Notes on the text of the error

See merge request raspberry/mobile/ios!53
2022-09-07 12:27:37 +03:00
Никита Чирухин 885dbe3067 MALINKA-389: Notes on the text of the error 2022-09-07 12:27:37 +03:00
Jura Shikin b73e135aa2 Merge branch 'develop' into 'master'
small fixes in yaml file

See merge request raspberry/mobile/ios!50
2022-08-31 15:29:53 +03:00
y.shikin 66aa113dd4 small fixes in yaml file 2022-08-31 15:28:43 +03:00
Jura Shikin 9dfa80bfb3 Merge branch 'develop' into 'master'
Release 1.1.0

See merge request raspberry/mobile/ios!49
2022-08-31 14:58:39 +03:00
y.shikin fe6656c3ed Fixes yaml 2022-08-31 14:50:10 +03:00
y.shikin c8b1372f6c Add notifications for release 2022-08-31 14:46:17 +03:00
Jura Shikin c748281e5e Merge branch 'develop' into 'master'
Release 1.1.0

See merge request raspberry/mobile/ios!48
2022-08-31 14:42:14 +03:00
y.shikin 13063262cb MALINKA-220 - fix localizations 2022-08-31 14:21:25 +03:00
y.shikin 47e79bc85c MALINKA-220 - correct texts 2022-08-31 14:21:25 +03:00
y.shikin a721bf8416 MALINKA-385 - disable button for prevent double navigate 2022-08-31 14:21:25 +03:00
Juraldinio 4feaba4305 Add device token field 2022-08-31 14:21:25 +03:00
Juraldinio 21a73e8da3 Add change environment in settings 2022-08-31 14:21:25 +03:00
Juraldinio e90cf92749 MALINKA-382 - add call device for check device status 2022-08-31 14:21:25 +03:00
Juraldinio a6dbebf835 MALINKA-380 - preprod and prduction environments 2022-08-31 14:21:25 +03:00
Juraldinio c830ffcf57 MALINKA-354 - add network layer for accountOrder request 2022-08-31 14:21:25 +03:00
Juraldinio e8764b76e2 Statuses for CreateAccount 2022-08-31 14:21:25 +03:00
Juraldinio bb91cdd763 Fix show settings only for beta, not prod 2022-08-31 14:21:25 +03:00
Juraldinio b272f32c55 add property to model 2022-08-31 14:21:25 +03:00
Juraldinio 7c7cd6d0ee Rewrite working with Keychain in application 2022-08-31 14:21:25 +03:00
Juraldinio 63f317dd70 Fix working with token on Simulators 2022-08-31 14:21:25 +03:00
Juraldinio ec51ecaab5 Add helper class for operate with Settings 2022-08-31 14:21:25 +03:00
Juraldinio 38a6d36c91 Some changes 2022-08-31 14:21:25 +03:00
Juraldinio 4a25a43792 Add ignorance while Create account 2022-08-31 14:21:25 +03:00
Juraldinio d03bd5353a Add device ignorance option 2022-08-31 14:21:25 +03:00
Juraldinio 34f67d4c0f Add settings for get device identity 2022-08-31 14:21:25 +03:00
Juraldinio 414c5bcb15 MALINKA-353 - handle order.id response. Prepare for make queries for check status. 2022-08-31 14:21:25 +03:00
Juraldinio e083211591 Draft for key operations 2022-08-31 14:21:25 +03:00
Juraldinio 0aea752053 Remove WalletKeys from project 2022-08-31 14:21:25 +03:00
Juraldinio 58b40ee976 Refactoring Wallet holder to Bank 2022-08-31 14:21:25 +03:00
Juraldinio 0af7900e7b Create wallet flow 2022-08-31 14:21:25 +03:00
Juraldinio 8f05b7ca62 Worked flow for create Account 2022-08-31 14:21:25 +03:00
Juraldinio 13a5d74cae Remove unused classes 2022-08-31 14:21:25 +03:00
Juraldinio cd3e0fa724 WiP with Wallet operations 2022-08-31 14:21:25 +03:00
Juraldinio aecf4c2547 Private and public keys and check wallet name 2022-08-31 14:21:25 +03:00
Juraldinio 89950e3024 Error message and flow for insert private keys 2022-08-31 14:21:25 +03:00
Juraldinio 0ac42cd017 Check device, validate token and show popup 2022-08-31 14:21:25 +03:00
Juraldinio 9181a8dfcb API for create wallet and check device 2022-08-31 14:21:25 +03:00
Juraldinio 70540cd7a2 Build main application 2022-08-31 14:21:25 +03:00
Juraldinio b2a41d5472 Create worked Package 2022-08-31 14:21:25 +03:00
Juraldinio 1672372e19 packages commits 2022-08-31 14:21:25 +03:00
Juraldinio 98598b6af1 Add local eosio-swift with 1.0.0 version 2022-08-31 14:21:24 +03:00
Андрей Геращенко 8ee24471de MALINKA-210: Changes design of wallet creation screen, added wallet creation... 2022-08-31 14:21:24 +03:00
Juraldinio 52a0ee9d01 Autobuild and Wallet API checker and creation 2022-08-31 14:21:24 +03:00
Juraldinio 2305b9cab2 MALINKA-336 - Register device service 2022-08-31 14:21:24 +03:00
Juraldinio 5b0260f004 MALINKA-336 - fix build 2022-08-31 14:21:24 +03:00
Juraldinio 83033ed052 MALINKA-336 - Device creation and validation 2022-08-31 14:21:24 +03:00
Андрей Геращенко 9b54501b19 MALINKA-344: Refactoring for realm 2022-08-31 14:21:24 +03:00
Juraldinio c2afd848bb Remove temporary debug strings 2022-08-31 14:21:24 +03:00
Juraldinio 23bd0102a0 Remove duplications after develop rebase 2022-08-31 14:21:24 +03:00
Juraldinio 940f64eaec Remove OnBoard navigation controller 2022-08-31 14:21:24 +03:00
Juraldinio c2783aff4c MALINKA-341 - wip 2022-08-31 14:21:24 +03:00
Juraldinio 08321a2e3d MALINKA-216 - create wallet 2022-08-31 14:21:24 +03:00
Juraldinio b34585a487 MALINKA-257: Fixed package paths
# Conflicts:
#	iOS/Packages/WalletFoundation/Package.swift
2022-08-31 14:21:24 +03:00
Juraldinio 3a3db75331 MALINKA-257: Added KeyChainAccess package
# Conflicts:
#	iOS/Packages/WalletFoundation/Sources/FCUUID/DeviceUUID.swift
2022-08-31 14:21:24 +03:00
Juraldinio 32d667d55a Change version to 1.1.0
# Conflicts:
#	iOS/project.yml
2022-08-31 14:21:24 +03:00
Juraldinio bba1824a08 MALINKA-340 - fix infinite call load info 2022-08-31 14:21:24 +03:00
Juraldinio 1b8fefdf5a Fixes for working with UUID in cloud
# Conflicts:
#	toolchain/generate.swift

# Conflicts:
#	iOS/project.yml
2022-08-31 14:21:24 +03:00
Juraldinio 85176b43f7 MALINKA-257: Fixed package paths
# Conflicts:
#	iOS/Packages/WalletFoundation/Package.swift
2022-08-31 14:21:24 +03:00
Juraldinio 6c45ec02f8 MALINKA-257: Added KeyChainAccess package
# Conflicts:
#	iOS/Packages/WalletFoundation/Sources/FCUUID/DeviceUUID.swift
2022-08-31 14:21:24 +03:00
Juraldinio 01c2aacc13 Increase marketing version
# Conflicts:
#	iOS/project.yml
2022-08-31 14:21:24 +03:00
Juraldinio 0676bcf49c MALINKA-340 - fix infinite call load info 2022-08-31 14:21:24 +03:00
Elena Nazarova 8f0568d2c6 MALINKA-317: update titles 2022-08-31 14:21:24 +03:00
Juraldinio aa3d10bd6b Try fix deploying 2022-08-31 14:21:24 +03:00
Juraldinio 73eb93c14a Remove warnings 2022-08-31 14:21:24 +03:00
Juraldinio 2b01f7f731 MALINKA-309 - show error for failed registration 2022-08-31 14:21:24 +03:00
Juraldinio 0a74026258 MALINKA-308 - Deeplinks refactoring. Open link after auth if exists. 2022-08-31 14:21:24 +03:00
Juraldinio a030c4d871 Remove temporary debug code 2022-08-31 14:21:24 +03:00
Juraldinio 19b064ae08 Update build yaml file for fix build 2022-08-31 14:21:24 +03:00
Juraldinio f62e58dc66 Fix generate UUID and add organization name 2022-08-31 14:21:24 +03:00
Juraldinio 3a69245723 Close setters to private 2022-08-31 14:21:24 +03:00
Juraldinio 911bfe19e9 REfactoring and commenting 2022-08-31 14:21:24 +03:00
Juraldinio 05e545d8f4 Huge refactoring 2022-08-31 14:21:24 +03:00
y.shikin ff4677166d Fixes for working with UUID in cloud
# Conflicts:
#	toolchain/generate.swift
2022-08-31 14:21:23 +03:00
user d97d742997 MALINKA-257: Added test uuid functionality 2022-08-31 14:20:08 +03:00
user 361b1cc036 MALINKA-257: Fixes for DeviceUUID and added icloud preferences into entitlements 2022-08-31 14:20:08 +03:00
user a7be38c96e MALINKA-257: Minor fixes 2022-08-31 14:20:08 +03:00
user 507f7fdf77 MALINKA-257: Fixed deprecation warnings 2022-08-31 14:20:08 +03:00
user 01b82ef107 MALINKA-257: Fixed dependencies 2022-08-31 14:20:08 +03:00
user 6177c88ff7 MALINKA-257: Fixed package paths 2022-08-31 14:20:08 +03:00
user a722dfb167 MALINKA-257: Added KeyChainAccess package 2022-08-31 14:20:08 +03:00
user 71927e48f5 MALINKA-257: Small refactoring and tiny fixes 2022-08-31 14:20:08 +03:00
user cdb5128eaf MALINKA-257: Added new keychain lib support 2022-08-31 14:20:08 +03:00
user afddc4d85c MALINKA-257: Added FCUUID class 2022-08-31 14:20:08 +03:00
Juraldinio 6cdad7b683 MALINKA-267 - GraphQL call registerDevice 2022-08-31 14:20:08 +03:00
Juraldinio 02bd64b558 Fix GraphQL models 2022-08-31 14:20:08 +03:00
Juraldinio 47beb2abcc Clean up warnings 2022-08-31 14:20:08 +03:00
Juraldinio fe941babc8 Remove unused code and fix DeviceCheckToken struct 2022-08-31 14:20:08 +03:00
Juraldinio 031fe5bf12 WiP 2022-08-31 14:20:08 +03:00
Juraldinio 638c3a072a WiP 2022-08-31 14:20:08 +03:00
Juraldinio 4cb2ed9ac1 WiP 2022-08-31 14:20:08 +03:00
Elena Nazarova db419691eb MALINKA-262: generating device token 2022-08-31 14:20:08 +03:00
Juraldinio 8310a9ff01 DeviceIdentity draft objc version 2022-08-31 14:20:08 +03:00
Juraldinio 5846017b26 Added SPM packages Foundation, Kit and Network 2022-08-31 14:20:08 +03:00
Juraldinio 5f96a45770 Rename to paycash 2022-08-31 14:20:08 +03:00
Juraldinio d39caa511d Add malinka property to servers 2022-08-31 14:20:08 +03:00
Juraldinio e1a1fa6307 Rename server links 2022-08-31 14:20:08 +03:00
Андрей Геращенко 19b95bc1d9 MALINKA-210: Added "Create wallet" screen 2022-08-31 14:20:08 +03:00
Juraldinio d85d7ae03d Apollo attach 2022-08-31 14:20:08 +03:00
Elena Nazarova 4ddd0b47f3 MALINKA-215: bug dismissing popup after successed transfer 2022-08-31 14:20:08 +03:00
Андрей Геращенко c0e6946740 MALINKA-199: Исправлено открытие контроллера с историей операций по криптотокену 2022-08-31 14:20:08 +03:00
Juraldinio b9bf6bcb8c WiP Package separation 2022-08-31 14:20:08 +03:00
y.shikin dcf25e6106 Change version to 1.1.0
# Conflicts:
#	iOS/project.yml
2022-08-31 14:20:07 +03:00
Juraldinio edfb491f3d For make release 2022-08-31 14:19:13 +03:00
Juraldinio 5c09962dbd Remove line for force new build 2022-08-16 12:07:08 +03:00
Juraldinio e2ef60e5fa Remove exit 2022-08-16 10:07:34 +03:00
Juraldinio 8feec718b1 Fix build for beta
# Conflicts:
#	toolchain/generate.swift
2022-08-16 10:02:33 +03:00
Juraldinio 3435a05910 fix echo yml 2022-08-16 10:01:27 +03:00
Juraldinio 13af51a1c1 Echo fix 2022-08-16 10:01:22 +03:00
Juraldinio e31978d529 Fix yml 2022-08-16 10:01:19 +03:00
Juraldinio da7af84c8f Add version for gitlab logs 2022-08-16 10:01:15 +03:00
Juraldinio ebc519cbaf Choose schedule 2022-08-16 10:01:10 +03:00
Juraldinio 0aa9cfae03 Add schedule for iOS build 2022-08-16 10:01:01 +03:00
Juraldinio 623a9df19d Add loader and hide how news 2022-08-16 09:53:13 +03:00
Juraldinio 82348e8f06 MALINKA-340 - fix infinite call load info
# Conflicts:
#	iOS/project.yml
2022-08-15 23:10:51 +03:00
Juraldinio bf7b2997b4 Merge branch 'release102' 2022-08-09 12:16:31 +03:00
Juraldinio 023793926c Develop and master - manual, tag - auto 2022-07-28 10:55:50 +03:00
Juraldinio ef96484dbb Upload to upstore manual 2022-07-28 10:50:44 +03:00
Juraldinio 914c7a2c1f MALINKA-236 - enable spaw and set 1.0.2 version 2022-07-28 10:33:56 +03:00
Juraldinio 6e5f0aacd1 Increase marketing version
# Conflicts:
#	iOS/project.yml
2022-07-28 10:33:54 +03:00
Juraldinio bb588d8e71 Update version to 1.0.1 2022-07-25 15:04:45 +03:00
Juraldinio ca2a6888f7 Remove warnings 2022-07-25 11:26:14 +03:00
Juraldinio 624abcf187 Add malinka.life domain to secure 2022-07-25 11:26:14 +03:00
Jura Shikin 14e5797425 Merge branch 'feature/MALINKA-193/localization_eng' into 'develop'
MALINKA-193: eng localization for working with card recognition

See merge request raspberry/ios!35
2022-07-22 11:06:41 +03:00
Elena Nazarova d07e38b571 MALINKA-193: eng localization for working with card recognition 2022-07-22 11:06:41 +03:00
Jura Shikin e064f72e53 Merge branch 'feature/MALINKA-194/Correct_translation' into 'develop'
MALINKA-194: Поправил локализацию

See merge request raspberry/ios!34
2022-07-22 11:06:12 +03:00
Андрей Геращенко 2e358c49cc MALINKA-194: Поправил локализацию 2022-07-22 11:06:11 +03:00
Juraldinio 4d62bd64f8 Change application ID in AppStore + Linting 2022-07-22 10:47:18 +03:00
Jura Shikin 4c1df2c6ab Merge branch 'feature/MALINKA-179/hide_swap_inheritance' into 'develop'
MALINKA-179: hide SWAP segment in Inheritance

See merge request raspberry/ios!29
2022-07-21 13:47:49 +03:00
Elena Nazarova 518fe9194c MALINKA-179: hide SWAP segment in Inheritance 2022-07-21 13:47:49 +03:00
Juraldinio 7db887675e Update vps working 2022-07-21 11:42:59 +03:00
Juraldinio 6da443dbdc Upload with VPN 2022-07-21 11:04:47 +03:00
Elena Nazarova a4c3f85e9d Merge branch 'feature/MALINKA-176/Hide_swap_buttons' into 'develop'
MALINKA-176: Added functionality for hiding swap button on Wallet screens

See merge request raspberry/ios!33
2022-07-21 10:46:42 +03:00
Андрей Геращенко fde17328ff MALINKA-176: Added functionality for hiding swap button on Wallet screens 2022-07-21 10:46:42 +03:00
Juraldinio 7eec4ed44b MALINKA-181 - notification 2022-07-21 09:41:07 +03:00
Juraldinio de2f4d8e8d MALINKA-181 - Update notification config 2022-07-20 19:53:29 +03:00
Jura Shikin 5b88e2c763 Merge branch 'feature/MALINKA-180/Appsettings_2' into 'develop'
MALINKA-180: Переделал получение параметра is_swap_active

See merge request raspberry/ios!32
2022-07-20 15:46:56 +03:00
Андрей Геращенко feb95933f0 MALINKA-180: Переделал получение параметра is_swap_active 2022-07-20 15:46:55 +03:00
Jura Shikin c4632cd65b Merge branch 'feature/MALINKA-177/hide_currency_exchange' into 'develop'
MALINKA-177: hide currency exchange

See merge request raspberry/ios!30
2022-07-20 15:37:37 +03:00
Elena Nazarova 05bcdfadfc MALINKA-177: hide currency exchange 2022-07-20 15:37:37 +03:00
Jura Shikin 9586acb576 Merge branch 'feature/MALINKA-178/hide_more_options' into 'develop'
MALINKA-178: hide 2 options from 'more'

See merge request raspberry/ios!28
2022-07-20 12:38:14 +03:00
Elena Nazarova 8a8cece7bc MALINKA-178: hide 2 options from 'more' 2022-07-20 12:38:14 +03:00
Elena Nazarova 8b2cb5ea53 Merge branch 'feature/MALINKA-180/Appsettings_isSwapActive' into 'develop'
MALINKA-180: Added swap functionality switching option

See merge request raspberry/ios!31
2022-07-20 12:15:40 +03:00
Андрей Геращенко 7f6df3c484 MALINKA-180: Added swap functionality switching option 2022-07-20 12:15:40 +03:00
Jura Shikin 85da277713 Merge branch 'feature/slack_emoji' into 'develop'
avocado -> raspberry

See merge request raspberry/ios!27
2022-07-19 15:18:17 +03:00
Juraldinio 68f466c6c0 Correct APPLICATION_SCHEME for build 2022-07-19 15:09:34 +03:00
Elena Nazarova d071af0536 avocado -> raspberry 2022-07-19 15:08:43 +03:00
Juraldinio c81e365ab6 Fix sign identity 2022-07-19 14:58:12 +03:00
Juraldinio 83f59087cc MALINKA-172 - Fix application scheme 2022-07-19 14:52:52 +03:00
Jura Shikin 713409aa54 Merge branch 'feature/MALINKA-174/about_app_text' into 'develop'
MALINKA-174: change about app text

See merge request raspberry/ios!26
2022-07-19 14:50:30 +03:00
Elena Nazarova edfa8e16b0 MALINKA-174: change about app text 2022-07-19 14:50:30 +03:00
Juraldinio 753cb4ac60 MALINKA-172 - Fix xcodeproj name 2022-07-19 14:45:20 +03:00
Juraldinio e14baf9589 MALINKA-172 - add BETA build type 2022-07-19 14:41:55 +03:00
Juraldinio a08b9e8eec MALINKA-172 - Change credentials for build app 2022-07-19 13:05:16 +03:00
Juraldinio b6758613b0 MALINKA-102 - fixes 2022-06-19 20:45:28 +03:00
Jura Shikin 97c68b96bf Merge branch 'feature/ios/MALINKA-73/Search_translation' into 'develop'
MALINKA-73: Исправил перевод в поиске по учетным записям

See merge request raspberry/ios!25
2022-06-15 11:01:30 +03:00
Андрей Геращенко b504642824 MALINKA-73: Исправил перевод в поиске по учетным записям 2022-06-15 11:01:30 +03:00
Juraldinio 5d7ebab22c Revert "Kalinka instead Malinka"
This reverts commit a51048aaa6.
2022-06-14 16:14:19 +03:00
Juraldinio a51048aaa6 Kalinka instead Malinka 2022-06-14 14:59:41 +03:00
Jura Shikin 1cc76eb17b Merge branch 'feature/ios/MALINKA-96/dev_14_06' into 'develop'
Resolve MALINKA-96 Правки с дева 14.06

See merge request raspberry/ios!24
2022-06-14 12:56:24 +03:00
Андрей Геращенко 89e7eb651f Resolve MALINKA-96 Правки с дева 14.06 2022-06-14 12:56:24 +03:00
Jura Shikin a78c3a0734 Merge branch 'feature/ios/MALINKA-84/Missed_commit' into 'develop'
MALINKA-84: Утраченные правки

See merge request raspberry/ios!23
2022-06-14 12:18:03 +03:00
Андрей Геращенко 84a3388d7c MALINKA-84: Утраченные правки 2022-06-14 12:18:03 +03:00
Jura Shikin 40e0d840d2 Merge branch 'feature/ios/MALINKA-84/dev_10_06' into 'develop'
MALINKA-84: Синхронизация веток сторонних разработчиков

See merge request raspberry/ios!22
2022-06-10 22:46:45 +03:00
Андрей Геращенко a1fc40f5dd MALINKA-84: Синхронизация веток сторонних разработчиков 2022-06-10 22:46:45 +03:00
Jura Shikin 9aebbdc611 Merge branch 'feature/MALINKA-70/Confirmation_title' into 'develop'
MALINKA-70: Allocating to another account titles

See merge request raspberry/ios!21
2022-06-08 10:59:16 +03:00
Elena Nazarova cac0c6bccd MALINKA-70: Allocating to another account titles 2022-06-08 10:59:16 +03:00
Juraldinio 4383d39d60 Hack for prevent infinite loading 2022-06-07 20:22:34 +03:00
Juraldinio 86a75f9b31 Remove cycle updating balance 2022-06-07 14:19:15 +03:00
Jura Shikin 2c90971314 Merge branch 'feature/MALINKA-72/Fix_add_card_button' into 'develop'
Resolve MALINKA-72 Исправлено поведение кнопки добавления банковской карточки

See merge request raspberry/ios!20
2022-06-03 21:00:57 +03:00
Андрей Геращенко 52fcd915bc Resolve MALINKA-72 Исправлено поведение кнопки добавления банковской карточки 2022-06-03 21:00:57 +03:00
Juraldinio 327d0393ad misstyping 2022-06-03 18:40:08 +03:00
Juraldinio c713149e06 Add defaults 2022-06-03 18:39:12 +03:00
Juraldinio a96e49181e Build with sniffing rights 2022-06-03 18:09:21 +03:00
Jura Shikin eed8f9c719 Merge branch 'feature/MALINKA-62/Remove_Paycash_mentions' into 'develop'
MALINKA-62: Удалил упоминания и логотипы пейкеша

See merge request raspberry/ios!19
2022-06-03 16:55:25 +03:00
Андрей Геращенко cb24ac2b29 MALINKA-62: Удалил упоминания и логотипы пейкеша 2022-06-03 16:55:25 +03:00
Jura Shikin 3dcee588b1 Merge branch 'feature/MALINKA-72/Исправить_перевод' into 'develop'
MALINKA-72: Исправил перевод

See merge request raspberry/ios!17
2022-06-02 14:45:28 +03:00
Андрей Геращенко 15ca62f7eb MALINKA-72: Исправил перевод 2022-06-02 14:45:28 +03:00
Андрей Геращенко 7da567d728 Merge branch 'feature/MALINKA-70/Confirmation_title' into 'develop'
MALINKA-70: Confirmation titles

See merge request raspberry/ios!15
2022-06-02 11:21:29 +03:00
Elena Nazarova 94f657c1e0 MALINKA-70: Confirmation titles 2022-06-02 11:21:29 +03:00
Jura Shikin ba6952712a Merge branch 'fetature/MALINKA-67/updateToDev_2' into 'develop'
MALINKA-67: Перенес коммиты поверх дева

See merge request raspberry/ios!16
2022-06-02 10:36:43 +03:00
Андрей Геращенко 235fadcc4c MALINKA-67: Перенес коммиты поверх дева 2022-06-02 10:36:43 +03:00
Андрей Геращенко 98f4b316fb Merge branch 'feature/MALINKA-68/Welcome_text' into 'develop'
MALINKA-68: Welcome titles

See merge request raspberry/ios!14
2022-06-01 13:58:01 +03:00
Elena Nazarova 75007f8c29 MALINKA-68: Welcome titles 2022-06-01 13:58:01 +03:00
Андрей Геращенко 43c1d95a6b Merge branch 'feature/MALINKA-16/About_app' into 'develop'
MALINKA-16: Paycash -> Malinka

See merge request raspberry/ios!13
2022-06-01 13:23:34 +03:00
Elena Nazarova 523ceff41e MALINKA-16: Paycash -> Malinka 2022-06-01 13:23:34 +03:00
Juraldinio 29086ab27a Change branch key for beta 2022-05-31 17:40:58 +03:00
Jura Shikin 436f284a8d Merge branch 'feature/MALINKA-11/Hide_buying_wallet' into 'develop'
MALINKA-11: Buying functionality is hidden

See merge request raspberry/ios!12
2022-05-31 17:36:11 +03:00
Jura Shikin 4f9328cb9e Merge branch 'feature/MALINKA-3/Цвета' into 'develop'
MALINKA-3: Цвета

See merge request raspberry/ios!11
2022-05-31 17:24:19 +03:00
Elena Nazarova fc9e3f18f2 MALINKA-3: Цвета 2022-05-31 17:24:19 +03:00
Андрей Геращенко d239e1dbf0 MALINKA-11: Buying functionality is hidden 2022-05-31 16:27:48 +03:00
Juraldinio 4bf39173be MALINKA-53 - Change hadrcoded link 2022-05-31 12:09:22 +03:00
Juraldinio 87f68e0ce5 After merge fixes 2022-05-31 12:02:09 +03:00
Juraldinio 73e1fad281 Sync develop at 30052022. 2022-05-30 23:45:32 +03:00
Juraldinio 0a69e0cbc9 Update gitignore 2022-05-30 14:53:56 +03:00
Juraldinio 4ea440f084 Add new Branch.io 2022-05-30 13:24:28 +03:00
Juraldinio 3f5c20dc18 Remove and add ignores 2022-05-30 12:57:35 +03:00
2529 changed files with 416975 additions and 50682 deletions
+5
View File
@@ -93,4 +93,9 @@ iOSInjectionProject/
Info.plist
/PayCash.entitlements
/PayCash.xcodeproj
/ResultIPA/
*.pbxproj
*.xcscheme
*.xcodeproj
*.entitlements
/iOS/Assets/Settings.bundle
+34 -18
View File
@@ -1,5 +1,6 @@
# Execute always
before_script:
- echo $CI_PIPELINE_IID
- echo $GITLAB_USER_ID
- echo ${CI_COMMIT_REF_SLUG}
- id
@@ -9,13 +10,12 @@ variables:
stages:
- lint
# - test
- test
- build
- deploy
- notification
# Linting stage
.linting: &linting
tags:
- malinka
@@ -34,6 +34,25 @@ Lint:
- /^feature/
- merge_requests
# Testing stage
.testing: &testing
tags:
- malinka
stage: test
when: always
allow_failure: false
script:
- chmod +x ./toolchain/testing.sh
- ./toolchain/testing.sh
Test:
<<: *testing
only:
- develop
- /^bugfix/
- /^feature/
- merge_requests
# Build IPA
.BuildIPA: &BuildIPA
tags:
@@ -44,23 +63,20 @@ Lint:
script:
- rm -rf ./ResultIPA
- chmod +x ./toolchain/build_iOS.sh
# - ./toolchain/build_iOS.sh PRODUCTION
- ./toolchain/build_iOS.sh BETA
- ./toolchain/build_iOS.sh ${APPLICATION_DEPLOY_TYPE} ${APPLICATION_NAME} ${APPLICATION_SCHEME_NAME}
# Build stage
build:
stage: build
<<: *BuildIPA
only:
- master
- develop
only:
- tags
- merge_requests
- develop
artifacts:
paths:
- ./ResultIPA
expire_in: 3 days
expire_in: 2 days
buildLeaf:
stage: build
@@ -85,7 +101,7 @@ buildLeaf:
IPA_PATH: ./ResultIPA
script:
- chmod +x ./toolchain/deploy_iOS.sh
- ./toolchain/deploy_iOS.sh ${IPA_DEPLOY_TARGET} ${IPA_PATH}
- ./toolchain/deploy_iOS.sh ${IPA_DEPLOY_TARGET} ${IPA_PATH} ${APPLICATION_SCHEME_NAME}
needs:
- job: build
artifacts: true
@@ -95,9 +111,8 @@ deploy:
<<: *DeployIPA
when: on_success
only:
- master
- develop
- tags
- develop
needs:
- job: build
artifacts: true
@@ -122,28 +137,29 @@ deployLeaf:
script:
- chmod +x ./toolchain/slack_notification.sh
- ./toolchain/slack_notification.sh ${BUILD_RESULT}
needs:
- job: deploy
# Develop
FailureNotification:
variables:
BUILD_RESULT: "FAILURE"
when: on_failure
only:
- master
- develop
- tags
- develop
<<: *slacknotification
needs:
- job: deploy
SuccessNotification:
variables:
BUILD_RESULT: "SUCCESS"
when: on_success
only:
- master
- develop
- tags
- develop
<<: *slacknotification
needs:
- job: deploy
# Merge checks
+32
View File
@@ -0,0 +1,32 @@
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WalletFoundation",
platforms: [.iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "WalletFoundation",
targets: ["WalletFoundation"]),
],
dependencies: [
.package(name: "KeyChainAccess", path: "../../Vendors/spm/KeyChainAccess")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "WalletFoundation",
dependencies: ["KeyChainAccess"],
path: "./Sources"),
.testTarget(
name: "WalletFoundationTests",
dependencies: ["WalletFoundation"],
path: "Tests"//, // Test files
// resources: [.copy("TestData")] // The test data files, copy files without modifying them
)
]
)
@@ -0,0 +1,90 @@
//
// ApplicationSettings.swift
//
//
// Created by Juraldinio on 8/28/22.
//
import Foundation
fileprivate enum ApplicationSettingsKeys {
// TODO: - Need remove after 1.4.0 version
static let ignoreDeviceSatusKeyOld = "Settings.device_ignore"
static let ignoreCreateAccountSatusKeyOld = "Status.createAccount_ignore"
static let isNetworkLogEnabledKeyOld = "Settings.network_copy"
static let isDeviceTokenCopyEnabledKeyOld = "Settings.device_token_copy_enable"
static let apiEnvironmentKeyOld = "Settings.api_environment"
static let apiPaycashEnvironmentKeyOld = "Settings.api_paycash_environment"
static let deviceDescriptionKeyOld = "Settings.device_id"
static let deviceTokenKeyOld = "Settings.device_token"
/// Ignore device status on register flow
static let ignoreDeviceSatusKey = "Settings.application.device_ignore"
/// Ignore create account status on register flow
static let ignoreCreateAccountSatusKey = "Status.application.createAccount_ignore"
/// log all network activity
static let isNetworkLogEnabledKey = "Settings.application.network_copy"
/// Copy device token to Settings fields
static let isDeviceTokenCopyEnabledKey = "Settings.application.device_token_copy_enable"
/// Field in Settings for device description
static let deviceDescriptionKey = "Settings.application.device_id"
/// Field in Settings for device token
static let deviceTokenKey = "Settings.application.device_token"
static let apiUsernameEnvironmentKey = "Settings.application.environment.usernames"
static let apiBackendEnvironmentKey = "Settings.application.environment.backend"
static let otherEnvironmentKey = "Settings.application.environment.other"
static let smartEnvironmentKey = "Settings.application.environment.smart"
/// Field in Settings for Firebase token lifetime
static let firebaseTokenLifetimeKey = "Settings.application.firebase.token.lifetime"
}
public enum ApplicationSettings {
public static var ignoreDeviceSatus: Bool { UserDefaults.standard.bool(forKey: ApplicationSettingsKeys.ignoreDeviceSatusKey) }
public static var ignoreCreateAccountSatus: Bool { UserDefaults.standard.bool(forKey: ApplicationSettingsKeys.ignoreCreateAccountSatusKey) }
public static var isNetworkLogEnabled: Bool { UserDefaults.standard.bool(forKey: ApplicationSettingsKeys.isNetworkLogEnabledKey) }
public static var isDeviceTokenCopyEnabled: Bool { UserDefaults.standard.bool(forKey: ApplicationSettingsKeys.isDeviceTokenCopyEnabledKey) }
public static func device(description: String) { UserDefaults.standard.set(description, forKey: ApplicationSettingsKeys.deviceDescriptionKey) }
public static func device(token: String) { UserDefaults.standard.set(token, forKey: ApplicationSettingsKeys.deviceTokenKey) }
public static var apiUsernameEnvironment: String? { UserDefaults.standard.string(forKey: ApplicationSettingsKeys.apiUsernameEnvironmentKey) }
public static var apiBackendEnvironment: String? { UserDefaults.standard.string(forKey: ApplicationSettingsKeys.apiBackendEnvironmentKey) }
public static var otherEnvironment: String? { UserDefaults.standard.string(forKey: ApplicationSettingsKeys.otherEnvironmentKey) }
public static var smartsEnvironment: String? { UserDefaults.standard.string(forKey: ApplicationSettingsKeys.smartEnvironmentKey) }
public static var firebaseTokenLifetime: Int? { UserDefaults.standard.integer(forKey: ApplicationSettingsKeys.firebaseTokenLifetimeKey) }
public static func clearApiEnvironment() {
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreDeviceSatusKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreCreateAccountSatusKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.isNetworkLogEnabledKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.isDeviceTokenCopyEnabledKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.deviceDescriptionKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.deviceTokenKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.apiUsernameEnvironmentKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.apiBackendEnvironmentKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.otherEnvironmentKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.smartEnvironmentKey)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.firebaseTokenLifetimeKey)
// TODO: - Need remove after 1.4.0 version
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreDeviceSatusKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.ignoreCreateAccountSatusKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.isNetworkLogEnabledKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.isDeviceTokenCopyEnabledKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.apiEnvironmentKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.apiPaycashEnvironmentKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.deviceDescriptionKeyOld)
UserDefaults.standard.removeObject(forKey: ApplicationSettingsKeys.deviceTokenKeyOld)
}
}
@@ -0,0 +1,26 @@
//
// CommonKey.swift
//
//
// Created by Juraldinio on 11/25/22.
//
import Foundation
public struct CommonKey: RawRepresentable, Hashable {
public var rawValue: String
public init?(rawValue: String) {
self.rawValue = rawValue
}
public init(_ rawValue: String) {
self.rawValue = rawValue
}
public func with(_ suffix: String) -> Self { .init(rawValue + "." + suffix) }
public static func key(_ key: String) -> Self { .init(key) }
public static func key(_ key: String, suffix: String) -> Self { Self.key(key).with(suffix) }
}
@@ -0,0 +1,26 @@
//
// KeychainProtocol.swift
//
//
// Created by Nut.Tech on 16.02.2023.
//
import Foundation
public typealias Key = CommonKey
public protocol KeychainProtocol {
/// Checks if a given common key have a password-saved value
/// - Parameter key: Common Key
/// - Returns: is value exists
func exist(_ key: Key) -> Bool
/// Checks if a given common key have a biometric-saved value
/// - Parameter key: Common Key
/// - Returns: is value exists
func bioExist(_ key: Key) -> Bool
/// Access to common key value by biometric authenfication
subscript(biometric key: Key) -> String? { get }
/// Access to common key value by password authenfication
subscript(_ key: Key, password password: String) -> String? { get set }
}
@@ -0,0 +1,189 @@
//
// WalletKeychain.swift
//
//
// Created by Juraldinio on 11/27/22.
//
import LocalAuthentication
final public class WalletKeychain: KeychainProtocol {
private enum KeychainLocals {
public static let password = "PWD"
public static let biometric = "BIO"
}
public static let instance = WalletKeychain()
// MARK: - Init
private init() { }
// MARK: - Interface
public func exist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(KeychainLocals.password)) }
public func bioExist(_ key: Key) -> Bool { self.checkProtectedExist(key: key.with(KeychainLocals.biometric) ) }
public subscript(biometric key: Key) -> String? {
self.loadBiometricProtected(key: key.with(KeychainLocals.biometric))
.map({ String(data: $0, encoding: .utf8) }) ?? nil
}
public subscript(_ key: Key, password password: String) -> String? {
get { loadPassProtected(key: key.with(KeychainLocals.password), password: password).map { String(data: $0, encoding: .utf8) } ?? nil }
set { update(key, password: password, newValue: newValue) }
}
// MARK: - Private
private func getPwdSecAccessControl() -> SecAccessControl {
var access: SecAccessControl?
var error: Unmanaged<CFError>?
access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .applicationPassword, &error)
precondition(access != nil, "SecAccessControlCreateWithFlags failed")
return access! // swiftlint:disable:this force_unwrapping
}
@discardableResult
private func setPassProtected(key: Key, data: String, password: String) -> Bool {
let context = LAContext()
context.setCredential(password.data(using: .utf8), type: .applicationPassword)
let query = [
kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: key.rawValue,
kSecAttrAccessControl as String: getPwdSecAccessControl(),
kSecValueData as String: (data.data(using: .utf8) ?? Data()) as NSData,
kSecUseAuthenticationContext: context
] as CFDictionary
let status: OSStatus = SecItemAdd(query, nil)
if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
if removeProtected(key: key) {
return setPassProtected(key: key, data: data, password: password)
} else {
return false
}
} else {
return false
}
}
private func loadPassProtected(key: Key, password: String) -> Data? {
let context = LAContext()
context.setCredential(password.data(using: .utf8), type: .applicationPassword)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecReturnData as String: kCFBooleanTrue!,
kSecAttrAccessControl as String: getPwdSecAccessControl(),
kSecMatchLimit as String: kSecMatchLimitOne,
kSecUseAuthenticationContext as String: context,
kSecUseAuthenticationUI as String: kSecUseAuthenticationUIFail
]
var dataTypeRef: AnyObject?
let result = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if result == noErr,
let value = dataTypeRef as? Data {
return value
}
return nil
}
// MARK: - Biometric entries
private func getBiometricSecAccessControl() -> SecAccessControl {
var access: SecAccessControl?
var error: Unmanaged<CFError>?
access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .biometryCurrentSet, &error)
precondition(access != nil, "SecAccessControlCreateWithFlags failed")
return access! // swiftlint:disable:this force_unwrapping
}
@discardableResult
private func setBiometricEntry(key: Key, data: String) -> Bool {
let query = [
kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: key.rawValue,
kSecAttrAccessControl as String: getBiometricSecAccessControl(),
kSecValueData as String: (data.data(using: .utf8) ?? Data()) as NSData,
] as CFDictionary
let status: OSStatus = SecItemAdd(query, nil)
if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
if removeProtected(key: key) {
return setBiometricEntry(key: key, data: data)
} else {
return false
}
} else {
return false
}
}
@discardableResult
private func loadBiometricProtected(key: Key) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecReturnData as String: kCFBooleanTrue as Any,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecUseOperationPrompt as String: "Access your data"
]
var dataTypeRef: AnyObject?
return SecItemCopyMatching(query as CFDictionary, &dataTypeRef) == noErr ? dataTypeRef as? Data : nil
}
// MARK: -
private func update(_ key: Key, password: String, newValue: String?) {
//TODO: Refactor later - updation of biometric entry only when it is available and needed
if let value = newValue {
setPassProtected(key: key.with(KeychainLocals.password), data: value, password: password)
setBiometricEntry(key: key.with(KeychainLocals.biometric), data: value)
} else {
removeProtected(key: key.with(KeychainLocals.password))
removeProtected(key: key.with(KeychainLocals.biometric))
}
}
@discardableResult
private func removeProtected(key: Key) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue
]
return SecItemDelete(query as CFDictionary) == noErr
}
private func checkProtectedExist(key: Key) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.rawValue,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecUseAuthenticationUI as String: kSecUseAuthenticationUIFail
]
let status = SecItemCopyMatching(query as CFDictionary, nil)
switch status {
case errSecSuccess, errSecInteractionNotAllowed: return true
case errSecItemNotFound: return false
default: return false
}
}
}
@@ -0,0 +1,54 @@
//
// Settings.swift
//
//
// Created by Juraldinio on 11/29/22.
//
import Foundation
final public class Settings {
public static let shared = Settings()
private init() {}
public func contains(_ key: CommonKey) -> Bool { defaults.value(forKey: key.rawValue) != nil }
public subscript<T>(_ key: CommonKey) -> T? {
get { defaults.value(forKey: key.rawValue) as? T }
set { set(value: newValue, for: key) }
}
public subscript<T: Codable>(_ key: CommonKey) -> [T]? {
get {
let decoder = JSONDecoder()
guard let data = defaults.value(forKey: key.rawValue) as? Data,
let value = try? decoder.decode([T].self, from: data) else { return nil }
return value
}
set {
let encoder = JSONEncoder()
guard let value = newValue,
let data = try? encoder.encode(value) else { return }
set(value: data, for: key)
}
}
public subscript(_ key: CommonKey) -> Bool {
get { defaults.bool(forKey: key.rawValue) }
set { set(value: newValue, for: key) }
}
public subscript(_ key: CommonKey) -> Int {
get { (defaults.value(forKey: key.rawValue) as? Int) ?? 0 }
set { set(value: newValue, for: key) }
}
private func set(value: Any?, for key: CommonKey) {
defaults.setValue(value, forKey: key.rawValue)
defaults.synchronize()
}
private var defaults: UserDefaults { UserDefaults.standard }
}
@@ -0,0 +1,55 @@
//
// DeviceToken.swift
//
//
// Created by NUT.Tech on 02.08.2022.
//
import Foundation
import DeviceCheck
import UIKit
public enum DeviceCheckTokenError: Error {
case notSupported
case generation
}
public struct DeviceCheckToken {
public let token: String
public let isSimulator: Bool
public static func generate() async -> Result<DeviceCheckToken, DeviceCheckTokenError> {
let token: String
let isSimulator: Bool
#if targetEnvironment(simulator)
isSimulator = true
token = tokenConstant
#else
let device = DCDevice.current
guard device.isSupported else { return .failure(.notSupported) }
guard let data = try? await device.generateToken() else { return .failure(.generation) }
isSimulator = false
token = data.base64EncodedString()
#endif
if ApplicationSettings.isDeviceTokenCopyEnabled {
ApplicationSettings.device(token: token)
}
return .success(DeviceCheckToken(token: token, isSimulator: isSimulator))
}
}
#if targetEnvironment(simulator)
fileprivate let tokenConstant = """
AgAAAIArgRs5gVfPAEHSra0ejd0EUNk0+me89vLfv5ZingpyOOkgXXXyjPzYTzWmWSu+BYqcD47byirLZ++3dJccpF99hWppT7G5xAuU+y56WpSYsATWySfuxbSSMT9JSoOWz4QtiDmmVmUHbzCfHbTP3Tr3hsG+86KBBaoSqHtDRy+dtKlV32kDRbuuniBy3nseZsZoCggAAKTXDFKWHDZ55Ya1Cp+8s+dGOyi4C08v0P/8rbQHcjjkOphpLUqKaBZCykAaf5Ue1c1ul57OKeoyaDy9ShXGvwIKIcZrvZBBds3wwEFQuZBNPTG1ZvpIZ3npXscHWaKRd228V/bboEKarukYi3+lsxafZj+R8laD0Ex3nP3WZaIU4990oerdiu6wbdELlaNyrVF6SRJcZQhhflfnnyXHMqw6c41Hk3toMHsUd9wzdzcYmZB1PI5rsgSfnWrxAbBy5rYpH+ZkGkhcHFJqQIO4TB8ZKU0KSjmS8mBnVDT/1VPgiNDz/qI+KWiZ1xcuEwwvaEmD+Fk6Pt9GMRsfxI+SvldTgk8REqr9dBXt69xwM2FHBHP9k4okkaMbsU1qpZPKCwsnTBrkbvLn0zVn2+tfTukLx0O+uCPRMluXn3EXDrQ0aRHFbUtgoMqOx0JP+7tj/BaOAKkc3C2GaCRfEes2YmqZkM4pMve4Qh1jAsIwYtGcDz7AeGQ61kVrc4CFj3xj2OgC+IbBi7naOZqNvr1Be1YPKt8Vpig9YF5GueY9p4DRGlOG6UX2RY4wsZTt38Lxw5uBBkWuINjRyqsiN5obPZ3xLagfzfNsFDBYgBsGrib8nURVfSgAqIgrozOl+cppRB7xoN9QRti+HAJqKNd6sqpsPkXzqnDWPBD2Jdg2WCJE2bjJhTqyJ6L3lHFguOdIPc2P6F1CGx4bn1GtegOLlFxexOjMzfU86gJOhYjkVGHt8GD96ohRl75/fsv1reCI16pWt2x8p+Bbh4kko8wP3FtXiun+i6gPDMBhE30Ye5ATsIUIHFZjHOA8UfntaEyCSAngQebQ0X4UERcue+GKY4RqVfPhuVqJa3RHt35Ci/2g7VWllNs1NKYsPofAgTNO2n/kwGrnnIL+gQPNsO6XjnqDOjjT/eDfZX88eK4k6+8+MnW3+l8IAZOIh5JT++JZvBadrNZPV7G/2ME6G1FCIAZ4icCidbMzpj4sGc8dlJOg/B3457/Vt8CHLulhajQIsXQuqGDotgzirELTVQ+Z2eh3a8W/Pu+g8iNIUL1MCEzHg7RefM6tUescNDH6uPEuEeXKmrsoVbHdUvhuLIVbQGHMCjuGbmxmczcfIcRAPWkjwuVQOKBpoaD/Zep2gM71inpz6056bmvUuTMM9MV6sM87Fqrv6TvIRT/ch/i7Flmv56ERn/NGryafdcvDIu+JMs5U3yvb3STTSZmbh8RXVGIJjOZ1FJYLREUxxK7eEGM2JLCD+CxR7LuLRuN4AtpI4GIhFrbdVdDkywqNpvEY6aGEOFnD9NP6neBuHRhK/AzqpDE83uFf+1JiPPY7aHYVoQhCxkPs8ex0qJnjHaveKiWfSmaZ6JfY/vVByzJNr5XD6ZSQlJQJ3+xjRb+blTR0XcZ5BHI9ovQQAmGQljWpGPnD5CZQ7ah5kVoK1SbPqtxY6J5zQUTjtTpSe3l6By/nopXH6HSQXJGotOzb+eMOOHFhDC9ypq3urHY+Q1jXB18eR/xkXEIlZQsPBmwCLhoNltD57faLzlqgiwinHjqslntnvfsMkoIpQnWFwLYKh0biW9KM5ZWv7CMIxcc+Wjl1FVnyUrMzhz/IIW6WshcNmZWFSMpaKzozxIyFQZ/IjZHPrE1SI33PwGn/Tro8ZRSf9mpKJbA3uidrygVz6WQlJJwR3ujdgZ9aJ9WZQWVehVYj5mMg5P5MB/BG1G/TQNTLn72Root26hSB+8WMCAo/EEY3L5Qox+JabfDV+kBsHjFtDfyo7ghp/AOvkVzudzzk+F3ruc1bIDJlSfOQ5WRTkjmQGhKDIfDvLIi4Mt8bQTzZ3KPkLaU+hwuFE7m9c/MgzmLWK24oDsZ5nZ4oBeUNc+lThUuz7qnWENE1sIXij3zFljsoH+HOPb+zSt8m7iwszxMZ6T84LvqxqWVGEkI4Il2s41ti5QH74nOzTaci+5dYRVWOnY/hAwI51HE1sb3Pe+NsSHcfgtDW0E4Xx+Wx0uLdqbTlriXRnUIOoi9PVNR6XOdJ4nDOxyiOMMhnUooQ7lRqm0hCxw9nAEw5+PvYWWFXxPaupUfeVsjH+9dXW6bzosCGzTbVpHcDjPWie70r+Nma+oOwA4ARKHmGsbKcoO43xos6sqfbZTCCP9BIPQnZ8XbUen9G7eMs9ESigKoynKKVGNmsBXK4lU8xM/qLXXudViMdSPOZ6mghjJCNK0yA1v9l/ipZRHiTPFOttELH6Ip+fKDtfqdeEqCiPrSnVtWzehUKOUhlNJtexkOZcb2Dtq4L7JlJ0GkJg80vCvEYvArM2JpPqKDVr8hCBNC87u6zk9T3E+L2dfL30aiNVAGTl44Qw0pPerIr1a6m9Jkj690Pi3OI7UAgWaoQjxYm2my7DZMqtkL6CrT0NW9KnihXw701ngJysdKcZ0JkMDT2LzP+2Nj10WIOwkLxASexSQgSoyGk7yTYLUAyvwN1rhRtspXaiyOcfyzDwgTIU9Sn/jMbC6fv7GPReCsiFR8Xa6VCj37eFPXgBiOpAYtj/zMz/3S/io3LTqs7QG1M14CX31xSxu21tASOzaRhbd2RB2QCHXgpqv4593psE5EPjbRZt5DN2toQ4XJJ1A1/EcyDkEJ8+1gu34aVrqC6ejm/07/MQ7ISmUuPrJyCaPIW+PbkxF0VpYU5lJ9HP+LD7WggPwi8NVz8zFWNtyTM6aUuTNL69sBHpWlYeqCwpkJ+EcJFuaTnT27N4pFvwA==
"""
#endif
@@ -0,0 +1,346 @@
//
// DeviceUUID.swift
//
//
// Created Nut.Tech on 02.08.2022.
//
#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif
import KeyChainAccess
/// Class allow retrive UUID with different life cycle.
final public class DeviceUUID {
private enum Constants {
enum Keys {
static let installationUUIDKey = "installationUUIDKey"
static let deviceUUIDKey = "deviceUUIDKey"
static let devicesUUIDsKey = "devicesUUIDsKey"
static let devicesUUIDsToggleKey = "devicesUUIDsToggleKey"
static let devicesUUIDs = "devicesUUIDs"
}
enum Locals {
static let DevicesUUIDsDidChangeNotification = NSNotification.Name(rawValue: "DevicesUUIDsDidChangeNotification")
}
}
/// Shared instance for class
static public var shared = DeviceUUID()
/// Changes each time the app gets launched (persistent to session).
lazy private(set) public var session: String = {
self.generateUuid()
}()
/// Changes each time the app gets installed (persistent to installation).
lazy private(set) public var installation: String = {
self.getValue(forKey: Constants.Keys.installationUUIDKey,
defaultValue: nil,
keychain: false,
synchronizable: false)
}()
/// Changes each time all the apps of the same vendor are uninstalled (this works exactly as identifierForVendor).
lazy private(set) public var vendor: String? = {
#if os(iOS)
UIDevice.current
.identifierForVendor?
.uuidString
.lowercased()
.replacingOccurrences(of: "-", with: "")
#elseif os(macOS)
return nil
#endif
}()
/// Changes only on system reset, this is the best replacement to the good old udid (persistent to device)
lazy private(set) public var device: String = {
self.getValue(forKey: Constants.Keys.deviceUUIDKey,
defaultValue: nil,
synchronizable: false)
}()
/// List of all uuidForDevice of the same user.
/// In this way it's possible manage guest accounts across multiple devices easily
lazy private(set) public var devices: [String] = {
let devicesString = self.getValue(forKey: Constants.Keys.devicesUUIDsKey,
defaultValue: self.device)
return devicesString.components(separatedBy: "|")
}()
/// Changes each time (no persistent), but allows to keep in memory more temporary uuids.
public func uuid(forKey key: String) -> String {
guard let uuid = self.uuids[key] else {
let value = self.generateUuid()
self.uuids[key] = value
return value
}
return uuid
}
/// Changes each time (no persistent)
public func generateUuid() -> String {
let uuidRef = CFUUIDCreate(nil)
let uuidStringRef = CFUUIDCreateString(nil, uuidRef)
return ((uuidStringRef as? String) ?? "")
.lowercased()
.replacingOccurrences(of: "-", with: "")
}
// MARK: - Private
private var uuids = [String: String]()
private var isCloudAvailable = false
// MARK: - Init
init() {
self.initCloudUUIDsDevices()
}
// MARK: - Internal
func getValue(forKey key: String,
defaultValue: String? = nil,
userDefaults: Bool = true,
keychain: Bool = true,
service: String? = nil,
accessGroup: String? = nil,
synchronizable: Bool = true) -> String {
if let newValue = Self.getValue(forKey: key,
userDefaults:
userDefaults,
keychain: keychain,
service: service,
accessGroup: accessGroup) {
return newValue
} else {
let value = defaultValue ?? self.generateUuid()
Self.setValue(value,
forKey: key,
userDefaults: userDefaults,
keychain: keychain,
service: service,
accessGroup: accessGroup,
synchronizable: synchronizable)
return value
}
}
public func uuidForDeviceMigratingValue(forKey key: String,
service: String? = nil,
accessGroup: String? = nil,
commitMigration: Bool) -> String? {
if let uuidToMigrate = Self.getValue(forKey: key,
service: service,
accessGroup: accessGroup) {
return self.uuid(forDeviceMigratingValue: uuidToMigrate, commitMigration: commitMigration)
}
return nil
}
func updateUUIDDevice(value: String) {
self.device = value
Self.setValue(value,
forKey: Constants.Keys.deviceUUIDKey,
synchronizable: false)
}
func uuid(forDeviceMigratingValue value: String, commitMigration: Bool) -> String? {
if self.isValidUUID(value) {
let oldValue = self.device
let newValue = value
guard oldValue != newValue else { return oldValue }
if commitMigration {
self.updateUUIDDevice(value: newValue)
let deviceSet = NSMutableOrderedSet(array: self.devices)
deviceSet.add(newValue)
deviceSet.remove(oldValue)
if let uuidsArray = deviceSet.array as? [String] {
self.updateUUIDsDevices(with: uuidsArray)
}
self.syncCloudUUIDsDevices()
return self.device
} else {
return oldValue
}
} else {
let exception = NSException(name: NSExceptionName(rawValue: "Invalid uuid to migrate"),
reason: "uuid value should be a string of 32 or 36 characters.")
exception.raise()
return nil
}
}
private func initCloudUUIDsDevices() {
self.isCloudAvailable = false
guard FileManager.default.ubiquityIdentityToken != nil else { return }
self.isCloudAvailable = true
NotificationCenter.default.addObserver(self,
selector: #selector(self.changesCloudUUIDsDevicesNotification),
name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
object: nil)
self.syncCloudUUIDsDevices()
}
private func syncCloudUUIDsDevices() {
if self.isCloudAvailable {
let iCloud = NSUbiquitousKeyValueStore.default
//if keychain contains more device identifiers than icloud, maybe that icloud has been empty, so re-write these identifiers to iCloud
for uuidOfUserDevice in self.devices {
let uuidOfUserDeviceAsKey = "\(Constants.Keys.deviceUUIDKey)_\(uuidOfUserDevice)"
if iCloud.string(forKey: uuidOfUserDeviceAsKey) != uuidOfUserDevice {
iCloud.set(uuidOfUserDevice, forKey: uuidOfUserDeviceAsKey)
}
}
//toggle a boolean value to force notification on other devices, useful for debug
let uuidsOfUserDevicesToggler = !iCloud.bool(forKey: Constants.Keys.devicesUUIDsToggleKey)
iCloud.set(uuidsOfUserDevicesToggler, forKey: Constants.Keys.devicesUUIDsToggleKey)
iCloud.synchronize()
}
}
private func updateUUIDsDevices(with value: [String]) {
self.devices = value
Self.setValue(value.joined(separator: "|"),
forKey: Constants.Keys.devicesUUIDsKey)
}
private func isValidUUID(_ value: String) -> Bool {
let pattern = "^[0-9a-f]{32}|[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$"
guard let regExp = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else {
return false
}
let uuidValueRange = NSRange(location: 0, length: value.count)
let matchRange = regExp.rangeOfFirstMatch(in: value, options: [], range: uuidValueRange)
var matchValue: String?
if !NSEqualRanges(matchRange, NSRange(location: NSNotFound, length: 0)) {
matchValue = (value as NSString).substring(with: matchRange)
return matchValue == value ? true : false
} else {
return false
}
}
@objc
private func changesCloudUUIDsDevicesNotification(_ notification: Notification?) {
if self.isCloudAvailable {
let uuidsSet = NSMutableOrderedSet(array: self.devices)
let uuidsCount = uuidsSet.count
let iCloud = NSUbiquitousKeyValueStore.default
let iCloudDict = iCloud.dictionaryRepresentation as NSDictionary
iCloudDict.enumerateKeysAndObjects { key, obj, stop in
let uuidKey = key as? NSString
if uuidKey?.range(of: Constants.Keys.deviceUUIDKey).location == 0 {
if let uuidValue = obj as? String {
if uuidKey?.range(of: uuidValue).location != NSNotFound,
self.isValidUUID(uuidValue) {
uuidsSet.add(uuidValue)
} else {
print("invalid uuid")
}
}
}
}
if uuidsSet.count > uuidsCount,
let uuidsArray = uuidsSet.array as? [String] {
self.updateUUIDsDevices(with: uuidsArray)
let userInfo = [Constants.Keys.devicesUUIDs: self.devices]
NotificationCenter.default.post(name: Constants.Locals.DevicesUUIDsDidChangeNotification,
object: self,
userInfo: userInfo)
}
}
}
// MARK: - Static
static private func getValue(forKey key: String,
userDefaults: Bool = true,
keychain: Bool = true,
service: String? = nil,
accessGroup: String? = nil) -> String? {
let keychainStore = Self.makeKeychain(service: service, accessGroup: accessGroup)
var value = try? keychainStore.getString(key)
if userDefaults,
!value.isExist {
value = UserDefaults.standard.string(forKey: key)
}
return value
}
@discardableResult
static private func setValue(_ value: String?,
forKey key: String,
userDefaults: Bool = true,
keychain: Bool = true,
service: String? = nil,
accessGroup: String? = nil,
synchronizable: Bool = true) -> Error? {
if let value = value,
userDefaults {
UserDefaults.standard.set(value, forKey: key)
UserDefaults.standard.synchronize()
}
if let value = value, keychain {
let keychainStore = Self.makeKeychain(service: service, accessGroup: accessGroup).synchronizable(synchronizable)
do {
try keychainStore.set(value, key: key)
} catch {
return error
}
}
return nil
}
static private func makeKeychain(service: String? = nil, accessGroup: String? = nil) -> Keychain {
if let service = service {
if let accessGroup = accessGroup {
return Keychain(service: service, accessGroup: accessGroup)
} else {
return Keychain(service: service)
}
} else if let accessGroup = accessGroup {
return Keychain(accessGroup: accessGroup)
}
return Keychain()
}
}
@@ -0,0 +1,23 @@
//
// Array+Extension.swift
//
//
// Created by Juraldinio on 9/15/22.
//
import Foundation
public extension Array {
subscript(safe index: Index) -> Element? {
return (self.startIndex..<self.endIndex) ~= index ? self[index] : nil
}
}
public extension Array where Element: Hashable {
func distinct() -> Array {
let set = Set(self)
return Array(set)
}
}
@@ -0,0 +1,25 @@
//
// Data+Extension.swift
//
//
// Created by Juraldinio on 12/6/22.
//
import Foundation
public extension Data {
func jsonDecoded<T: Decodable>(type: T.Type, userInfo: [CodingUserInfoKey: Any]? = nil) -> T? {
try? self.makeDecoder(userInfo: userInfo).decode(type, from: self)
}
func jsonDecoded<T: Decodable>(type: T.Type, userInfo: [CodingUserInfoKey: Any]? = nil) -> [T]? {
try? self.makeDecoder(userInfo: userInfo).decode([T].self, from: self)
}
private func makeDecoder(userInfo: [CodingUserInfoKey: Any]?) -> JSONDecoder {
let jsonDecoder = JSONDecoder()
userInfo >>- { jsonDecoder.userInfo = $0 }
return jsonDecoder
}
}
@@ -0,0 +1,15 @@
//
// Dictionary+Extension.swift
//
//
// Created by NUT.Tech on 27.01.2023.
//
import Foundation
public extension Dictionary where Key == String, Value == Any {
func jsonSerialized(options: JSONSerialization.WritingOptions = []) -> Data? {
try? JSONSerialization.data(withJSONObject: self, options: options)
}
}
@@ -0,0 +1,16 @@
//
// Encodable+Extension.swift
//
//
// Created by Juraldinio on 12/6/22.
//
import Foundation
public extension Encodable {
func jsonData() -> Data? {
let encoder = JSONEncoder()
return try? encoder.encode(self)
}
}
@@ -0,0 +1,32 @@
//
// Optional+Extension.swift
//
//
// Created by Juraldinio on 08.08.2022.
//
import Foundation
public extension Optional {
var isExist: Bool {
if case .some = self {
return true
}
return false
}
func orCreate(_ creation: @autoclosure () -> Wrapped) -> Wrapped {
switch self {
case let .some(value): return value
case .none: return creation()
}
}
func orTypedCreate<Element: RawRepresentable>(_ creation: @autoclosure () -> Element) -> Element where Element.RawValue == Wrapped {
switch self {
case let .some(value): return Element(rawValue: value) ?? creation()
case .none: return creation()
}
}
}
@@ -0,0 +1,43 @@
//
// Publisher+Extension.swift
//
//
// Created by Juraldinio on 12/20/22.
//
import Foundation
import Combine
public extension Publisher {
/// Includes the current element as well as the previous element from the upstream publisher in a tuple where the previous element is optional.
/// The first time the upstream publisher emits an element, the previous element will be `nil`.
///
/// let range = (1...5)
/// cancellable = range.publisher
/// .withPrevious()
/// .sink { print ("(\($0.previous), \($0.current))", terminator: " ") }
/// // Prints: "(nil, 1) (Optional(1), 2) (Optional(2), 3) (Optional(3), 4) (Optional(4), 5) ".
///
/// - Returns: A publisher of a tuple of the previous and current elements from the upstream publisher.
func withPrevious() -> AnyPublisher<(previous: Output?, current: Output), Failure> {
scan(Optional<(Output?, Output)>.none) { ($0?.1, $1) }
.compactMap { $0 }
.eraseToAnyPublisher()
}
/// Includes the current element as well as the previous element from the upstream publisher in a tuple where the previous element is not optional.
/// The first time the upstream publisher emits an element, the previous element will be the `initialPreviousValue`.
///
/// let range = (1...5)
/// cancellable = range.publisher
/// .withPrevious(0)
/// .sink { print ("(\($0.previous), \($0.current))", terminator: " ") }
/// // Prints: "(0, 1) (1, 2) (2, 3) (3, 4) (4, 5) ".
///
/// - Parameter initialPreviousValue: The initial value to use as the "previous" value when the upstream publisher emits for the first time.
/// - Returns: A publisher of a tuple of the previous and current elements from the upstream publisher.
func withPrevious(_ initialPreviousValue: Output) -> AnyPublisher<(previous: Output, current: Output), Failure> {
scan((initialPreviousValue, initialPreviousValue)) { ($0.1, $1) }.eraseToAnyPublisher()
}
}
@@ -0,0 +1,24 @@
//
// File.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
public extension String {
func trimCompact() -> String {
let value = self
.replacingOccurrences(of: "\n", with: "")
.replacingOccurrences(of: "\r", with: "")
let regex = try! NSRegularExpression(pattern: "[ ]{2,}", options: .caseInsensitive)
return regex.stringByReplacingMatches(in: value,
options: [],
range: NSRange(0..<value.utf16.count),
withTemplate: " ")
}
}
@@ -0,0 +1,43 @@
//
// Either.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
public enum Either<T: Decodable, U: Decodable>: Decodable {
case firstType(T)
case secondType(U)
public func unwrap() -> Any {
switch self {
case .firstType(let objectOfTypeT): return objectOfTypeT
case .secondType(let objectOfTypeU): return objectOfTypeU
}
}
public func map<V>(firstTypeTransform: (T) -> V, secondTypeTransform: (U) -> V) -> V {
switch self {
case .firstType(let value):
return firstTypeTransform(value)
case .secondType(let value):
return secondTypeTransform(value)
}
}
public init(from decoder: Decoder) throws {
if let value = try? T(from: decoder) {
self = .firstType(value)
} else if let value = try? U(from: decoder) {
self = .secondType(value)
} else {
let context = DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription:
"Cannot decode \(T.self) or \(U.self)")
throw DecodingError.dataCorrupted(context)
}
}
}
@@ -0,0 +1,53 @@
//
// Operations.swift
// Jura
//
// Created by Jura on 8/14/19.
// Copyright © 2019 Jura. All rights reserved.
//
import Foundation
precedencegroup MonadicPrecedence {
associativity: left
higherThan: BitwiseShiftPrecedence
}
infix operator >>- : MonadicPrecedence
@inline(__always)
@discardableResult
public func >>-<T, U>(a: T?, f: (T) throws -> U?) rethrows -> U? {
switch a {
case .some(let x):
return try f(x)
case .none:
return nil
}
}
// MARK: <<< / >>>
precedencegroup FunctionApplicationPrecedenceLeft {
lowerThan: AssignmentPrecedence
associativity: left
}
infix operator >>> : FunctionApplicationPrecedenceLeft
@inline(__always)
public func >>><T, U>(x: T, f: (T) throws -> U) rethrows -> U {
return try f(x)
}
precedencegroup FunctionApplicationPrecedenceRight {
lowerThan: AssignmentPrecedence
associativity: right
}
infix operator <<< : FunctionApplicationPrecedenceRight
@inline(__always)
public func <<<<T, U>(f: (T) throws -> U, x: T) rethrows -> U {
return try f(x)
}
@@ -0,0 +1,44 @@
//
// ArrayTests.swift
//
//
// Created by Juraldinio on 31.10.2022.
//
import Foundation
import XCTest
@testable
import WalletFoundation
final class ArrayTests: XCTestCase {
func testOuter() {
let test = [0, 1, 2]
XCTAssertNil(test[safe: 5])
XCTAssertNil(test[safe: -1])
}
func testInner() {
let test = [1, 3, 5]
var value = test[safe: 1]
XCTAssertNotNil(value)
XCTAssertEqual(value!, 3)
value = test[safe: 2]
XCTAssertNotNil(value)
XCTAssertEqual(value!, 5)
value = test[safe: 0]
XCTAssertNotNil(value)
XCTAssertEqual(value!, 1)
}
func testDistinct() {
let array = [1, 2, 5, 6, 5, 8, 9, 1, 2, 2, 5]
let arrayWithUniqueElements = array.distinct()
XCTAssertEqual(arrayWithUniqueElements.sorted(), [1, 2, 5, 6, 8, 9])
}
}
@@ -0,0 +1,58 @@
//
// OptionalsTests.swift
//
//
// Created by Juraldinio on 11/8/22.
//
import Foundation
import XCTest
@testable
import WalletFoundation
final class OptionalsTests: XCTestCase {
func testIsExists() {
var value: Int?
XCTAssertFalse(value.isExist)
value = 42
XCTAssertTrue(value.isExist)
}
func testOrCreate() {
var value: String?
XCTAssertEqual(value.orCreate("Hello"), "Hello")
value = "world"
XCTAssertEqual(value.orCreate("Hello"), "world")
}
func testOrTypedCreate() {
enum TestCases: String {
case first
case second
}
var value: String?
// Create new value because nil
XCTAssertEqual(value.orTypedCreate(TestCases.first), .first)
XCTAssertNotEqual(value.orTypedCreate(TestCases.first), .second)
value = "Hello"
// Create new value because not matched
XCTAssertEqual(value.orTypedCreate(TestCases.second), .second)
XCTAssertNotEqual(value.orTypedCreate(TestCases.second), .first)
value = "first"
// Not create new value and just match
XCTAssertEqual(value.orTypedCreate(TestCases.second), .first)
XCTAssertNotEqual(value.orTypedCreate(TestCases.second), .second)
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+36
View File
@@ -0,0 +1,36 @@
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WalletKit",
platforms: [.iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "WalletKit",
targets: ["WalletKit"]),
],
dependencies: [
.package(name: "eosswift", path: "../../Vendors/spm/eos-swift"),
.package(name: "KeyChainAccess", path: "../../Vendors/spm/KeyChainAccess"),
.package(name: "WalletFoundation", path: "../WalletFoundation"),
.package(name: "WalletNetwork", path: "../WalletNetwork"),
.package(name: "CryptoSwift", path: "../../Vendors/spm/CryptoSwift-1.5.1")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "WalletKit",
dependencies: ["CryptoSwift", "eosswift", "KeyChainAccess", "WalletFoundation", "WalletNetwork"],
path: "./Sources"),
.testTarget(
name: "WalletKitTests",
dependencies: ["WalletKit"],
path: "Tests"//, // Test files
// resources: [.copy("TestData")] // The test data files, copy files without modifying them
)
]
)
@@ -0,0 +1,53 @@
//
// DeepLink.swift
//
//
// Created by Juraldinio on 8/9/22.
//
import Foundation
public struct DeepLink {
public typealias DataParams = [AnyHashable: Any]
public typealias InfoParams = [String: Any]
public let action: DeepLinkAction
let data: Data?
public let info: [String: Any]?
public let url: URL?
public static func create(params: DataParams?) -> DeepLink? {
guard let params = params,
let rawAction = params["action"] as? String,
let action = DeepLinkAction(rawValue: rawAction),
let data = (params["params"] as? String)?.data(using: .utf8)
?? (try? JSONSerialization.data(withJSONObject: params["params"] as? [String: Any], options: [])) else {
return nil
}
return DeepLink(action: action, data: data, info: nil, url: URL(string: params["r"] as? String ?? ""))
}
public static func create(info: DataParams?) -> DeepLink? {
guard let info = info as? [String: Any],
let rawAction = info["action"] as? String,
let action = DeepLinkAction(rawValue: rawAction) else {
return nil
}
return DeepLink(action: action, data: nil, info: info, url: nil)
}
public func get<T: Decodable>(_ model: T.Type) -> T? {
guard let data = self.data else { return nil }
return try? JSONDecoder().decode(T.self, from: data)
}
public func dictionary() -> [String: Any] {
guard let data = self.data else { return [:] }
return (try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]) ?? [:]
}
}
@@ -1,14 +1,13 @@
//
// CommonModelDeepLink.swift
// PayCash
// DeepLinkAction.swift
//
//
// Created by Igor on 15.10.2020.
// Copyright © 2020 List. All rights reserved.
// Created by Juraldinio on 8/9/22.
//
import Foundation
enum DeepLinkAction: String {
public enum DeepLinkAction: String {
case connect = "connect_accounts"
case chat = "chat"
case transfer = "transfer"
@@ -17,17 +16,17 @@ enum DeepLinkAction: String {
case approveBuy = "approve_buy"
case emission = "emission"
case chatMessage = "chat.message"
case transactionTransfer = "transactions.incoming_transfer"
case transactionTransfer = "transaction.incoming_transfer"
case transactionInheritance = "transactions.incoming_inheritance"
case transactionEmission = "transactions.emission"
case p2pDealNew = "orders.new_deal"
case p2pDealComplete = "orders.completed_deal"
case p2pDealCancel = "orders.cancelled_deal"
case p2pDealDispute = "orders.dispute_deal"
case cashDealNew = "guarantee_orders.new_deal"
case cashDealConfirm = "guarantee_orders.confirmation_deal"
case cashDealCancel = "guarantee_orders.cancelled_deal"
case cashDealComplete = "guarantee_orders.completed_deal"
// case cashDealNew = "guarantee_orders.new_deal"
// case cashDealConfirm = "guarantee_orders.confirmation_deal"
// case cashDealCancel = "guarantee_orders.cancelled_deal"
// case cashDealComplete = "guarantee_orders.completed_deal"
case news = "news"
case competitivePrice = "competitive_price"
}
@@ -0,0 +1,136 @@
//
// Village.swift
//
//
// Created by Juraldinio on 15.08.2022.
//
import Foundation
import WalletFoundation
import WalletNetwork
final public class Village {
private static var keychainKeeper = PrivacyKeeper(keychain: WalletKeychain.instance)
private static var villages = [Village]()
private let environment: NetworkEnvironment
private var houses = [VillageHouse]()
private var foreigner: Foreigner? {
didSet {
guard let foreigner = self.foreigner else { return }
ApplicationSettings.device(description: "\(foreigner)")
}
}
// MARK: - Init
private init(environment: NetworkEnvironment) {
self.environment = environment
}
// MARK: - Public
/// Create instance of Village.
public static func get(with environment: NetworkEnvironment) -> Village {
if let village = Self.villages.first(where: { $0.environment.isEquals(other: environment) }) {
return village
}
let village = Village(environment: environment)
Self.villages.append(village)
return village
}
/// Get device instance.
public func device(force: Bool = false) async throws -> Device {
if force { self.foreigner = nil }
if let foreigner = self.foreigner { return foreigner }
if !force,
let foreigner = Foreigner.restore() {
do {
try await foreigner.retrievStatus(using: self.environment)
self.foreigner = foreigner
return foreigner
} catch { }
}
do {
let foreigner = try await Foreigner.create(for: DeviceUUID.shared.device, using: self.environment)
foreigner.save()
self.foreigner = foreigner
return foreigner
} catch {
throw DeviceError.create
}
}
public func attestate(device: Device) async throws -> CertifiedDevice {
try await Resident.permit(for: device, using: self.environment)
}
/// Create wallet key.
public func createWalletKey() throws -> WalletKey {
try WalletKey.create()
}
/// Create wallet case for hold wallet.
public func createWalletCase(with name: String, key: WalletKey? = nil) async throws -> WalletCase {
let walletKey: WalletKey
if let key = key {
walletKey = key
} else {
walletKey = try WalletKey.create()
}
return try await VillagerCoat.create(name: name, using: self.environment, key: walletKey)
}
/// Create bank that contain Wallets.
public func getBank(with password: String, on device: Device) throws -> Bank {
guard Self.keychainKeeper.accept(password: password) else {
throw WalletError.passwordNotMatch
}
if let bank = self.houses.first(where: { $0.isEquals(device: device, environment: self.environment, password: password) }) {
return bank
}
let house = VillageHouse.create(password: password,
keeper: Self.keychainKeeper,
on: device,
environment: self.environment)
self.houses.append(house)
return house
}
public static func reset() {
VillageHouse.removeAllVillagers()
Self.keychainKeeper.reset()
}
// Keychain static methods
public static func accept(password: String) -> Bool {
Self.keychainKeeper.accept(password: password)
}
public static func passwordViaBiometrics() -> String? {
Self.keychainKeeper.passwordByBiometrics()
}
public static var isPasswordExists: Bool {
Self.keychainKeeper.isPasswordExists
}
public static func set(password: String, old: String? = nil) throws {
try Self.keychainKeeper.update(password: password, old: old)
}
}
@@ -0,0 +1,93 @@
//
// Tractor.swift
//
//
// Created by Juraldinio on 15.08.2022.
//
import Foundation
import Combine
import WalletFoundation
import WalletNetwork
import KeyChainAccess
final class Foreigner: Device, Codable {
private enum Constant {
static let saveKey = "village.tractor.instance"
static let service = CodingUserInfoKey(rawValue: "service")!
}
// MARK: - Codable
private enum CodingKeys: String, CodingKey {
case uuid
case id
}
// MARK: - Init
private init(uuid: String, id: String, isTrusted: Bool) {
self.uuid = uuid
self.id = id
self.isTrusted = isTrusted
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(String.self, forKey: .uuid)
self.id = try container.decode(String.self, forKey: .id)
}
// MARK: - Device
let uuid: String
let id: String
private(set) var isTrusted: Bool = false
private(set) var availableAccounts: Int?
// MARK: - CustomStringConvertible
var description: String { "uuid: \(self.uuid), id: \(self.id)" }
// MARK: - Methods
func save() {
guard let rawData = try? JSONEncoder().encode(self) else { return }
let keychain = Keychain()
try? keychain.set(rawData, key: Constant.saveKey)
}
func retrievStatus(using environment: NetworkEnvironment) async throws {
let service = DeviceService(environment: environment)
do {
let status = try await service.stateDevice(id: self.id)
self.availableAccounts = status.availableAccounts
self.isTrusted = status.isTrusted
return
} catch {
throw DeviceError.status
}
}
// MARK: - Static
static func create(for cid: String, using environment: NetworkEnvironment) async throws -> Foreigner {
let service = DeviceService(environment: environment)
do {
let device = try await service.createDevice(uuid: cid)
return Foreigner(uuid: device.uuid, id: device.id, isTrusted: device.isTrusted)
} catch {
throw DeviceError.create
}
}
static func restore() -> Foreigner? {
let keychain = Keychain()
guard let rawData = try? keychain.getData(Constant.saveKey) else { return nil }
let decoder = JSONDecoder()
return try? decoder.decode(Foreigner.self, from: rawData)
}
}
@@ -0,0 +1,230 @@
//
// PrivacyKeeper.swift
//
//
// Created by Nut.Tech on 07.02.2023.
//
import WalletFoundation
import Foundation
import CryptoSwift
final class PrivacyKeeper {
private typealias CipherData = (key: String, iv: String)
private enum Constants {
static let oldPasswordKey = CommonKey("PrivacyKeeper.password")
static let passwordKey = "PrivacyKeeper.password"
static let cipherKey = "PrivacyKeeper.ckey"
static let ivKey = "PrivacyKeeper.ivkey"
}
var isPasswordExists: Bool {
if let encryptedPasswordKey {
return self.keychain.exist(encryptedPasswordKey)
}
return false
}
private var encryptedPasswordKey: CommonKey?
private var cipherData: CipherData
private var keychain: KeychainProtocol
init(keychain: KeychainProtocol) {
self.keychain = keychain
self.cipherData = Self.initCipherData()
self.encryptedPasswordKey = Self.makeEncryptedPasswordCommonKey(cipherData: self.cipherData)
}
/// Cheks if password correct
func accept(password: String) -> Bool {
self.getPassword(password) == password
}
/// Get password via biometry
func passwordByBiometrics() -> String? {
guard let encryptedPasswordKey else { return nil }
return self.keychain.bioExist(encryptedPasswordKey) ?
self.keychain[biometric: encryptedPasswordKey] : nil
}
/// Get private key from keychain
/// - Parameters:
/// - key: common key in keychain
/// - password: current password
/// - Returns: private key (if exists)
func privateKey(for key: String, password: String?) -> String? {
let pwd = password ?? self.passwordByBiometrics()
if let pwd {
return self.keychain[self.encryptPK(commonKey: key), password: pwd]
} else {
return nil
}
}
/// Update password to new one.
func update(password: String, old: String? = nil) throws {
guard self.isPasswordExists else {
try self.setPassword(password)
return
}
guard let old, self.accept(password: old) else {
throw WalletError.passwordNotMatch
}
try self.setPassword(password)
}
/// Update private key for current account. If you are using biometrics, pass nil in password.
/// - Parameters:
/// - privateKey: new private key
/// - key: common key in keychain
/// - password: current password
func update(privateKey: String?, for key: String, password: String?) {
let pwd = password ?? self.passwordByBiometrics()
guard let pwd, self.accept(password: pwd) else { return }
self.keychain[self.encryptPK(commonKey: key), password: pwd] = privateKey
}
/// Migrates pasword and private key storage in keychain to new style
/// - Parameters:
/// - oldKey: old private key common key for keychain
/// - newKey: new private key common key for keychain
/// - password: current password
func migrate(oldKey: CommonKey, newKey: CommonKey, password: String) {
self.migratePassword(password: password)
guard self.accept(password: password) else { return }
self.migratePrivateKey(oldKey: oldKey, newKey: newKey, password: password)
self.migrateToEncrypted(commonKey: newKey, password: password)
}
/// Resets all data by making all old data inaccessible
func reset() {
self.cipherData = Self.createCipherData()
self.encryptedPasswordKey = Self.makeEncryptedPasswordCommonKey(cipherData: self.cipherData)
}
/// Gets password if exists. Pass nil to get biometrics password
/// - Parameter password: current password
/// - Returns: password string (if exists)
private func getPassword(_ password: String? = nil) -> String? {
if let password {
guard let encryptedPasswordKey else { return nil }
return self.keychain[encryptedPasswordKey, password: password]
} else {
return self.passwordByBiometrics()
}
}
/// Set password
/// - Parameter password: new password
private func setPassword(_ password: String) throws {
guard let encryptedPasswordKey else {
throw WalletError.passwordKeyNotExist
}
self.keychain[encryptedPasswordKey, password: password] = password
}
/// Migrate private key in keychain from old to new for old versions
/// - Parameters:
/// - oldKey: old-style key
/// - newKey: new-style key
/// - password: current password
private func migratePrivateKey(oldKey: CommonKey, newKey: CommonKey, password: String) {
guard let privateKey = self.keychain[oldKey, password: password] else { return }
self.keychain[oldKey, password: password] = nil
self.keychain[newKey, password: password] = privateKey
}
/// Migrate to encrypted keys for keychain
/// - Parameters:
/// - commonKey: old-style common key, without encryption
/// - password: current password
private func migrateToEncrypted(commonKey: CommonKey, password: String) {
guard let privateKey = self.keychain[commonKey, password: password] else { return }
self.keychain[commonKey, password: password] = nil
self.keychain[self.encryptPK(commonKey: commonKey.rawValue), password: password] = privateKey
}
/// Migrating from an old keychain password key to a new one
/// - Parameter password: current password
private func migratePassword(password: String) {
guard self.keychain[Constants.oldPasswordKey, password: password] != nil,
let encryptedPasswordKey else { return }
self.keychain[Constants.oldPasswordKey, password: password] = nil
self.keychain[encryptedPasswordKey, password: password] = password
}
/// Makes encrypted password key for keychain
private static func makeEncryptedPasswordCommonKey(cipherData: CipherData) -> CommonKey? {
guard let encryptedCKeyString = try? Self.aesEncrypt(string: Constants.passwordKey,
key: cipherData.key,
iv: cipherData.iv)
else { return nil }
return CommonKey(encryptedCKeyString)
}
/// Encrypt key for use in keychain
/// - Parameter commonKey: common key, e.g. "user@wallet.privatekey"
/// - Returns: encrypted CommonKey object
private func encryptPK(commonKey: String) -> CommonKey {
let encryptedCKeyString = try? Self.aesEncrypt(string: commonKey,
key: self.cipherData.key,
iv: self.cipherData.iv)
return CommonKey(encryptedCKeyString ?? commonKey)
}
/// Generates data for encoding keys
private static func makeCipherData() -> CipherData {
let ckey = UUID().uuidString.replacingOccurrences(of:"-",
with: "",
options: .literal)
let iv = String(UUID().uuidString.replacingOccurrences(of:"-",
with: "",
options: .literal)
.dropLast(16))
return CipherData(key: ckey, iv: iv)
}
/// Tries to load cipher data and creates it, if doesn't exist
private static func initCipherData() -> CipherData {
if let ckey = UserDefaults.standard.string(forKey: Constants.cipherKey),
let iv = UserDefaults.standard.string(forKey: Constants.ivKey) {
return CipherData(key: ckey, iv: iv)
} else {
return Self.createCipherData()
}
}
/// Creates new data for encoding keychain keys and saves it to defaults
private static func createCipherData() -> CipherData {
let cipherData = Self.makeCipherData()
UserDefaults.standard.set(cipherData.key, forKey: Constants.cipherKey)
UserDefaults.standard.set(cipherData.iv, forKey: Constants.ivKey)
return cipherData
}
static func aesEncrypt(string: String, key: String, iv: String) throws -> String {
guard let data = string.data(using: .utf8) else {
throw WalletError.cannotEncryptNonUTF8Data
}
let encrypted = try AES(key: Array(key.utf8),
blockMode: CBC(iv: Array(iv.utf8)))
.encrypt([UInt8](data))
let encryptedData = Data(encrypted)
return encryptedData.base64EncodedString()
}
static func aesDecrypt(string: String, key: String, iv: String) throws -> String {
guard let data = Data(base64Encoded: string) else {
throw WalletError.cannotDecryptNonUTF8Data
}
let decrypted = try AES(key: Array(key.utf8),
blockMode: CBC(iv: Array(iv.utf8)))
.decrypt([UInt8](data))
let decryptedData = Data(decrypted)
guard let decryptedString = String(bytes: decryptedData.bytes, encoding: .utf8) else {
throw WalletError.cannotDecryptData
}
return decryptedString
}
}
@@ -0,0 +1,47 @@
//
// Resident.swift
//
//
// Created by Juraldinio on 25.08.2022.
//
import Foundation
import WalletFoundation
import WalletNetwork
struct Resident: CertifiedDevice {
let device: Device
let status: DeviceStatus
static func permit(for device: Device, using environment: NetworkEnvironment) async throws -> CertifiedDevice {
if device.isTrusted { return Resident(device: device, status: .valid) }
let result = await DeviceCheckToken.generate()
guard let deviceToken = try? result.get() else {
throw CertifiedDeviceError.token
}
let service = DeviceService(environment: environment)
do {
let status = try await service.checkDevice(id: device.id, token: deviceToken.token)
return Resident(device: device, status: status == .valid ? .valid : .invalid )
} catch let NetworkServiceError.gqlApplication(error) {
let deviceError: CertifiedDeviceError
switch error {
case "Invalid": deviceError = .invalid
case "DeviceNotFoundError": deviceError = .notFound
case "DecryptError": deviceError = .decrypt
case "UnexpectedDeviceKind": deviceError = .kind
case "UnexpectedError": deviceError = .unknown
default: deviceError = .unknown
}
throw deviceError
} catch {
throw CertifiedDeviceError.unknown
}
}
}
@@ -0,0 +1,300 @@
//
// File.swift
//
//
// Created by Juraldinio on 8/27/22.
//
import Foundation
import Combine
import WalletFoundation
import WalletNetwork
final class VillageHouse: Bank {
private enum Constants {
static let oldKey = "Account.Service.collection"
static let key = "Wallet.bank.service"
enum Keys {
static let current = CommonKey("Account.Service.current")
static let collection = CommonKey("Account.Service.collection")
}
}
// MARK: - Properties
private let device: Device
private let environment: NetworkEnvironment
private let keychainKeeper: PrivacyKeeper
private let activeSubject: CurrentValueSubject<Villager?, Never>
private let villagersSubject: CurrentValueSubject<[Villager], Never>
private var cancellables = Set<AnyCancellable>()
// MARK: - Init
private init(password: String, keychain: PrivacyKeeper, device: Device, environment: NetworkEnvironment) {
self.device = device
self.environment = environment
self.keychainKeeper = keychain
let collection = Self.restore(keychain: keychain)
Self.migrateIfNeeded(collection: collection, password: password)
self.villagersSubject = CurrentValueSubject(collection)
let active = Self.active(in: collection, keychain: keychain)
self.activeSubject = CurrentValueSubject(active)
self.villagersSubject
.sink { [weak self] villagers in
self?.save(villagers: villagers)
}
.store(in: &self.cancellables)
}
// MARK: - Bank
var active: Wallet? { self.activeSubject.value }
var wallets: [Wallet] { self.villagersSubject.value }
lazy var activePublisher: AnyPublisher<Wallet?, Never> = self.activeSubject.map { $0 }.eraseToAnyPublisher()
lazy var walletsPublisher: AnyPublisher<[Wallet], Never> = self.villagersSubject.map { $0 }.eraseToAnyPublisher()
func remove(wallet: Wallet) throws {
var villagers = self.villagersSubject.value
guard let villager = self.villager(by: wallet),
let index = villagers.firstIndex(where: { $0 == villager }) else {
throw BankError.notOwned
}
villagers.remove(at: index)
villager.clear()
self.villagersSubject.value = villagers
if let active = self.active,
villager.isEquals(other: active) {
try self.activate(wallet: nil)
}
}
func activate(wallet: Wallet?) throws {
guard let wallet else {
Settings.shared[Constants.Keys.current] = Data()
self.activeSubject.value = nil
return
}
guard let villager = self.villager(by: wallet) else {
throw BankError.notOwned
}
// We can save only wallet in accepted state.
if case .accepted = villager.state {
Settings.shared[Constants.Keys.current] = villager.jsonData()
}
self.activeSubject.value = villager
}
func add(using walletCase: WalletCase, password: String) async throws -> Wallet {
let villager = try await Villager.create(walletCase: walletCase,
on: self.device,
using: self.environment,
keychain: self.keychainKeeper)
self.add(wallets: [villager], password: password)
return villager
}
func add(using purses: [Purse], password: String) throws {
guard self.keychainKeeper.accept(password: password) else {
throw WalletError.passwordNotMatch
}
let wallets = purses
.filter { $0.bank?.isEquals(other: self, password: password) ?? false }
.filter { purse in
!self.wallets.contains(where: { $0.name == purse.name && $0.keyType.rawValue == purse.permission.permName })
}
.map { Villager.create(purse: $0,
keeper: self.keychainKeeper) }
self.add(wallets: wallets, password: password)
}
func restore(using keys: WalletKey) async throws -> PurseHolder {
let holder = PurseHolder()
let service = AccountHyperionService(environment: self.environment)
let eosService = EOSService(environment: self.environment)
return try await holder.load(using: service, eosService: eosService, keys: keys, in: self)
}
func refreshStatus(wallet: Wallet?) async throws {
let villagers: [Villager]
if let wallet,
let villager = self.villager(by: wallet) {
villagers = [villager]
} else {
villagers = self.villagersSubject.value
}
_ = await withTaskGroup(of: Void.self) { group in
villagers.forEach { villager in
switch villager.state {
case .pending,
.creating:
group.addTask {
try? await villager.refresh(using: self.environment)
}
case .accepted,
.declined:
return
}
}
await group.waitForAll()
self.save(villagers: villagers)
}
}
func accept(password: String) -> Bool {
self.keychainKeeper.accept(password: password)
}
func isEquals(other: Bank, password: String) -> Bool {
guard let house = other as? VillageHouse else { return false }
return self.device.isEquals(other: house.device) &&
self.environment.isEquals(other: house.environment) &&
self.keychainKeeper.accept(password: password) &&
other.accept(password: password)
}
func switchPassword(_ new: String, old: String) throws {
try self.keychainKeeper.update(password: new, old: old)
self.wallets.forEach { $0.updatePrivateKeyEncryption(password: new, old: old) }
}
func update(_ keyUpdates: [WalletKeyUpdate], using password: String) async throws -> [WalletKeyUpdateResult] {
// TODO: - After move EOS to frameworks!
/*guard password == self.password else { throw BankError.passwordNotMatch }
let villagers = keyUpdates.compactMap({ self.villager(by: $0.wallet) })
guard villagers.count != keyUpdates.count else { throw BankError.notOwned }
guard let privateKey = self.privateKey(password) else {
throw WalletError.privateKeyNotExists
}
return WalletKeyUpdate(wallet: self, oldPrivateKey: "", transitionId: "")
return try await villager.update(key: key, password: self.password, using: self.environment)*/
return []
}
// MARK: - Internal
func isEquals(device: Device, environment: NetworkEnvironment, password: String) -> Bool {
return self.device.isEquals(other: device) &&
self.environment.isEquals(other: environment) &&
self.accept(password: password)
}
// MARK: - Private
private func add(wallets: [Villager], password: String) {
wallets
.filter { $0.key.privateKey.isExist }
.forEach { $0.updateKeychain(key: $0.key, using: password) }
let villagers = self.villagersSubject.value
self.villagersSubject.value = villagers + wallets
}
private func save(villagers: [Villager]) {
Settings.shared[Constants.Keys.collection] = villagers.jsonData()
}
private static func restore(keychain: PrivacyKeeper) -> [Villager] {
if let data: Data = Settings.shared[Constants.Keys.collection],
let userInfoKey = Villager.keychainUserInfoKey,
let villagers: [Villager] = data.jsonDecoded(type: Villager.self,
userInfo: [userInfoKey: keychain]) {
return villagers
}
return []
}
private static func migrateIfNeeded(collection: [Villager], password: String) {
guard collection.count > 0 else { return }
collection.forEach { $0.migrate(password: password) }
}
private func migrate() {
let migrateWallets: [Villager]
if let data = UserDefaults.standard.value(forKey: Constants.oldKey) as? Data,
let collection = try? JSONDecoder().decode([Villager.OldVillager].self, from: data) {
migrateWallets = collection.compactMap { $0.covert(keeper: self.keychainKeeper) }
} else {
migrateWallets = []
}
print(migrateWallets)
// Delete previous key
// UserDefaults.standard.set(nil, forKey: Constants.oldKey)
}
private func villager(by wallet: Wallet) -> Villager? {
guard let villager = wallet as? Villager,
self.villagersSubject.value.contains(where: { $0 == villager }) else {
return nil
}
return villager
}
// MARK: - Static
public static func create(password: String, keeper: PrivacyKeeper, on device: Device, environment: NetworkEnvironment) -> VillageHouse {
VillageHouse(password: password, keychain: keeper, device: device, environment: environment)
}
public static func removeAllVillagers() {
Settings.shared[Constants.Keys.collection] = Data()
}
private static func active(in collection: [Villager], keychain: PrivacyKeeper) -> Villager? {
if let data: Data = Settings.shared[Constants.Keys.current],
let userInfoKey = Villager.keychainUserInfoKey,
let villager: Villager = data.jsonDecoded(type: Villager.self,
userInfo: [userInfoKey: keychain]),
collection.contains(where: { $0 == villager }) {
return villager
}
guard let rawValue: String = Settings.shared[Constants.Keys.current] else { return nil }
let name = rawValue.components(separatedBy: "@").first
let keyTypeString = rawValue.components(separatedBy: "@").last ?? ""
let keyType = WalletKeyType(rawValue: keyTypeString) ?? WalletKeyType.active
return collection.first(where: { $0.name == name && $0.keyType == keyType })
}
}
@@ -0,0 +1,283 @@
//
// Villager.swift
//
//
// Created by Juraldinio on 16.08.2022.
//
import Foundation
import Combine
import WalletFoundation
import WalletNetwork
extension String {
fileprivate static let privateKey = "privateKey"
}
final class Villager: Wallet, Codable, CustomStringConvertible {
struct OldVillager: Codable {
var username: String
var publicKey: String
var keyType: String
func covert(keeper: PrivacyKeeper) -> Villager? {
guard let keyType = WalletKeyType(rawValue: self.keyType),
let key = try? WalletKey.restore(using: self.username, type: keyType, publicKey: self.publicKey) else {
return nil
}
return Villager(name: self.username,
key: key,
keyType: keyType,
state: .accepted,
keychain: keeper)
}
}
private let stateSubject: CurrentValueSubject<WalletState, Never>
// MARK: - Init
private init(walletCase: WalletCase,
keyType: WalletKeyType,
state: WalletState,
keychain: PrivacyKeeper) {
self.name = walletCase.name
self.key = walletCase.key
self.keyType = keyType
self.keychain = keychain
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
}
private init(name: String,
key: WalletKey,
keyType: WalletKeyType,
state: WalletState,
keychain: PrivacyKeeper) {
self.name = name
self.key = key
self.keyType = keyType
self.keychain = keychain
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
}
// MARK: - Codable
private enum CodingKeys: String, CodingKey {
// Old values
case username
case publicKey
//
case name
case key
case keyType
case state
}
static var keychainUserInfoKey: CodingUserInfoKey? {
return CodingUserInfoKey(rawValue: "keychain")
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// For save old format with username field!
let username = try container.decodeIfPresent(String.self, forKey: .username)
if let username {
self.name = username
} else {
self.name = try container.decode(String.self, forKey: .name)
}
self.keyType = try container.decode(WalletKeyType.self, forKey: .keyType)
let state = try container.decode(WalletState.self, forKey: .state)
self.stateSubject = CurrentValueSubject<WalletState, Never>(state)
guard let keychainKey = Self.keychainUserInfoKey,
let keeper = decoder.userInfo[keychainKey] as? PrivacyKeeper else {
throw WalletError.decodingWithoutKeychainContext
}
self.keychain = keeper
// If first version we hold key on flat structure!
if let key = try? WalletKey(from: decoder) {
self.key = key
} else {
self.key = try container.decode(WalletKey.self, forKey: .key)
}
// TODO: - NEED COMPLETETASK
// self.key = try WalletKey.restore(using: self.name, type: self.keyType, publicKey: publicKey, password: "")
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.name, forKey: .name)
try container.encode(self.key, forKey: .key)
try container.encode(self.keyType, forKey: .keyType)
try container.encode(self.stateSubject.value, forKey: .state)
}
// MARK: - Wallet
let name: String
private(set) var key: WalletKey
let keyType: WalletKeyType
var state: WalletState { self.stateSubject.value }
private let keychain: PrivacyKeeper
lazy var statePublisher: AnyPublisher<WalletState, Never> = self.stateSubject.eraseToAnyPublisher()
// MARK: - Equatable
static func == (lhs: Villager, rhs: Villager) -> Bool {
return lhs.name == rhs.name
&& lhs.key == rhs.key
&& lhs.keyType == rhs.keyType
&& lhs.state == rhs.state
&& lhs.name == rhs.name
}
// MARK: - CustomStringConvertible
var description: String { "(name: \(name), keyType: \(keyType), state: \(state), public: \(key.publicKey)" }
// MARK: - Internal
func refresh(using environment: NetworkEnvironment) async throws {
let service = AccountService(environment: environment)
let state: WalletState
switch self.state {
case .creating(let orderId),
.pending(let orderId):
do {
let order = try await service.order(with: orderId)
state = WalletState(order: order)
} catch {
throw WalletError.network(error)
}
default:
state = self.state
}
self.stateSubject.value = state
}
@discardableResult
func updateKeychain(key: WalletKey, using password: String) -> Wallet {
if self.keychain.accept(password: password) {
self.key = key
self.updatePrivateKey(password: password)
}
return self
}
func privateKey(password: String?) -> String? {
guard let password, self.keychain.accept(password: password) else { return nil }
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
return self.keychain.privateKey(for: key.rawValue, password: password)
}
private func updatePrivateKey(password: String) {
guard let privateKey = self.key.privateKey else { return }
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
self.keychain.update(privateKey: privateKey, for: key.rawValue, password: password)
}
// TODO: - Need review
func migrate(password: String) {
let oldKey = CommonKey.key(self.name, suffix: .privateKey)
let newKey = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
self.keychain.migrate(oldKey: oldKey, newKey: newKey, password: password)
}
/// Updates storage of private key in keychain for new password
/// - Parameters:
/// - password: new password
/// - old: old password (which was stored with private key earlier)
func updatePrivateKeyEncryption(password: String, old: String) {
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
let privateKey = self.privateKey(password: old)
self.keychain.update(privateKey: nil, for: key.rawValue, password: old)
self.keychain.update(privateKey: privateKey, for: key.rawValue, password: password)
}
func clear() {
let key = CommonKey.key("\(self.name)@\(self.keyType.rawValue)", suffix: .privateKey)
self.keychain.update(privateKey: nil, for: key.rawValue, password: "")
}
// MARK: - Static
static func create(purse: Purse, keeper: PrivacyKeeper) -> Villager {
Villager(name: purse.name,
key: purse.key,
keyType: purse.keyType,
state: .accepted,
keychain: keeper)
}
static func create(walletCase: WalletCase,
on device: Device,
using environment: NetworkEnvironment,
keychain: PrivacyKeeper) async throws -> Villager {
let service = AccountService(environment: environment)
do {
// return Villager(walletCase: walletCase, state: .pending)
let order = try await service.create(username: walletCase.name,
pubKey: walletCase.key.publicKey,
deviceId: device.id)
let state: WalletState
switch order.status {
case .creating: state = .creating(order.id)
case .active, .executed: state = .pending(order.id)
case .expired, .failed: state = .declined
case .completed: state = .accepted
}
return Villager(walletCase: walletCase,
keyType: .owner,
state: state,
keychain: keychain)
} catch let NetworkServiceError.gqlApplication(error) {
if ApplicationSettings.ignoreCreateAccountSatus {
return Villager(walletCase: walletCase,
keyType: .owner,
state: .creating("HELLO"),
keychain: keychain)
}
let walletError: WalletError
switch error {
case "invalid EOS public key": walletError = .invalidKey
case "INVALID DEVICE ID": walletError = .invalidDevice
case "UNKNOWN DEVICE": walletError = .device
case "UNCHECKED": walletError = .unchecked
case "UNTRUSTED": walletError = .untrusted
case "DEVICE LIMIT REACHED": walletError = .deviceLimit
case "DAILY LIMIT REACHED": walletError = .dailyLimit
case "UNAVAILABLE": walletError = .unavailable
case "ACCOUNT ALREADY EXISTS": walletError = .exists
default: walletError = .unavailable
}
throw walletError
} catch {
throw WalletError.unavailable
}
}
static func create(walletKey: WalletKey, using environment: NetworkEnvironment) async throws -> Villager {
// TODO: - Add logic
throw WalletError.unavailable
}
}
@@ -0,0 +1,52 @@
//
// VillagerCoat.swift
//
//
// Created by Juraldinio on 16.08.2022.
//
import Foundation
import WalletNetwork
final class VillagerCoat: WalletCase {
// MARK: - Init
private init(name: String, key: WalletKey) {
self.name = name
self.key = key
}
// MARK: - WalletCase
let name: String
private(set) var key: WalletKey
@discardableResult
func update(key: WalletKey) -> WalletKey {
self.key = key
return self.key
}
@discardableResult
func generateKey() -> WalletKey {
if let key = try? WalletKey.create() {
self.key = key
}
return self.key
}
// MARK: - Static
static func create(name: String, using environment: NetworkEnvironment, key: WalletKey) async throws -> VillagerCoat {
let walletName = name.lowercased()
let service = AccountService(environment: environment)
let isAvailable = await service.isAvailable(username: walletName)
switch isAvailable {
case .available: return VillagerCoat(name: walletName, key: key)
case .alreadyTaken: throw WalletCaseError.exists(name: walletName)
case .system: throw WalletCaseError.system
}
}
}
@@ -0,0 +1,47 @@
//
// Bank.swift
//
//
// Created by Juraldinio on 8/27/22.
//
import Foundation
import Combine
import WalletNetwork
public enum BankError: Error {
case empty
case notOwned
}
/// Bank that contain wallets.
public protocol Bank: AnyObject {
/// Current active wallet.
var active: Wallet? { get }
/// Publisher for observe changes.
var activePublisher: AnyPublisher<Wallet?, Never> { get }
/// Wallets in bank.
var wallets: [Wallet] { get }
/// Publisher for observe wallet changes.
var walletsPublisher: AnyPublisher<[Wallet], Never> { get }
/// Activate wallet
func activate(wallet: Wallet?) throws
/// Create new wallet in bank.
func add(using walletCase: WalletCase, password: String) async throws -> Wallet
/// Add Purse to bank with converting to Wallet
func add(using purses: [Purse], password: String) throws
/// Restore wallet by key.
func restore(using keys: WalletKey) async throws -> PurseHolder
/// Remove wallet from bank.
func remove(wallet: Wallet) throws
/// Check wallet status
func refreshStatus(wallet: Wallet?) async throws
/// Update WalletKey for Wallet
func update(_ keyUpdates: [WalletKeyUpdate], using password: String) async throws -> [WalletKeyUpdateResult]
/// Compare two banks
func isEquals(other: Bank, password: String) -> Bool
/// Switch password
func switchPassword(_ password: String, old: String) throws
/// Check if password valid for this bank
func accept(password: String) -> Bool
}
@@ -0,0 +1,40 @@
//
// CertifiedDevice.swift
//
//
// Created by Juraldinio on 25.08.2022.
//
import Foundation
public enum CertifiedDeviceError: Error {
/// Unavailable generate token for check
case token
/// Device not fount on server
case notFound
/// Invalid token
case invalid
/// Token decription error
case decrypt
/// Unexpected device kind
case kind
/// Unknown error
case unknown
/// Already in progress
case progress
}
/// Device status
public enum DeviceStatus {
/// Valid
case valid
/// Invalid
case invalid
}
public protocol CertifiedDevice {
/// Device
var device: Device { get }
/// Device status
var status: DeviceStatus { get }
}
@@ -0,0 +1,40 @@
//
// Device.swift
//
//
// Created by Juraldinio on 15.08.2022.
//
import Foundation
import Combine
/// Errors for Device
public enum DeviceError: Error {
/// Failed while restore
case restore
/// Failed create device on server side
case create
/// Failed retrieve status
case status
}
public protocol Device: CustomStringConvertible {
/// Device generated Identity UUID
var uuid: String { get }
/// Server generated Identity
var id: String { get }
/// Is device trusted
var isTrusted: Bool { get }
func isEquals(other: Device) -> Bool
}
// MARK: - Equatable
extension Device {
func isEquals(other: Device) -> Bool {
return self.uuid == other.uuid &&
self.id == other.id &&
self.isTrusted == other.isTrusted
}
}
@@ -0,0 +1,12 @@
//
// ErrorDomain.swift
//
//
// Created by user on 31.10.2022.
//
import Foundation
enum ErrorDomain: Error {
case noEnvironment
}
@@ -0,0 +1,101 @@
//
// PurseHolder.swift
//
//
// Created by Juraldinio on 12/4/22.
//
import Foundation
import WalletFoundation
import WalletNetwork
import EosioSwift
import Combine
public struct Purse {
public let name: String
public let key: WalletKey
public let keyType: WalletKeyType
let permission: Permission
var bank: Bank?
}
public final class PurseHolder {
public enum PurseError: Error {
case empty
}
public private(set) var purses: [Purse] = []
private var count: Int = 0
func load(using service: AccountHyperionService, eosService: EOSService, keys: WalletKey, in bank: Bank) async throws -> PurseHolder {
let publicKey = keys.publicKey
let collection = await service.fetchNamesHyperion(publicKey: publicKey)
guard !collection.isEmpty else {
throw PurseError.empty
}
let _ = await withCheckedContinuation { continuation in
self.count = collection.count
collection.forEach { name in
DispatchQueue.global().async {
eosService.getAccount(name) { response in
self.count -= 1
switch response {
case let .success(accountInfo):
let permissions = accountInfo.permissions
.filter { permission in permission.requiredAuth.keys.contains(where: { $0.key == publicKey }) }
.sorted { $0.permName > $1.permName }
permissions.forEach { permission in
if (permission.permName == WalletKeyType.owner.rawValue) {
self.purses.append(Purse(name: name,
key: keys,
keyType: .owner,
permission: permission,
bank: bank))
}
let hasAddedOwnerKey = self.purses.contains(where: { account in
account.name == name &&
account.permission.requiredAuth.keys.first?.key == permission.requiredAuth.keys.first?.key
})
if let type = WalletKeyType(rawValue: permission.permName),
type != WalletKeyType.owner,
!hasAddedOwnerKey {
self.purses.append(Purse(name: name,
key: keys,
keyType: type,
permission: permission,
bank: bank))
}
}
default: break
}
if self.count == 0 {
continuation.resume(returning: self.purses)
}
}
}
}
}
return self
}
}
@@ -0,0 +1,184 @@
//
// Wallet.swift
//
//
// Created by Juraldinio on 15.08.2022.
//
import Foundation
import WalletNetwork
import Combine
/// Errors for wallet creation
public enum WalletError: Error {
/// Invalid EOS public key
case invalidKey
/// Device not found
case invalidDevice
/// Device not found
case device
/// Device does not do check phase
case unchecked
/// Device not trusted
case untrusted
/// Device limit reached
case deviceLimit
/// Daily limit reached
case dailyLimit
/// Unknown error
case unavailable
/// Network error
case network(Error)
/// Private key does not exists
case privateKeyNotExists
/// Wallet with such name already exists.
case exists
/// Input password doesn't match keychain password
case passwordNotMatch
/// Error creating access key for password in keychain
case passwordKeyNotExist
/// Failed encryption access key for keychain
case cannotEncryptNonUTF8Data
/// Failed decryption access key for keychain
case cannotDecryptNonUTF8Data
/// Common decrypt error
case cannotDecryptData
/// Trying to decode Wallet-protocol object without keychain data
case decodingWithoutKeychainContext
}
/// Wallet state
public enum WalletState: RawRepresentable {
/// Transaction sent to EOS.
case creating(String)
/// Creation in progress
case pending(String)
/// Created
case accepted
/// Creation failed
case declined
private enum Constants {
static let preffixCreate = "C"
static let preffixPending = "P"
}
// TODO:- remove public
public init(order: WalletOrder) {
switch order.status {
case .completed:
self = .accepted
case .creating:
self = .creating(order.id)
case .executed, .active:
self = .pending(order.id)
case .failed, .expired:
self = .declined
}
}
// MARK: - RawRepresentable
public init?(rawValue: String) {
switch rawValue {
case "accepted": self = .accepted
case "declined": self = .declined
default:
let value = String(rawValue.dropFirst())
let preffix = rawValue.first?.uppercased()
if preffix == Constants.preffixPending {
self = .pending(value)
} else {
self = .creating(value)
}
}
}
public var rawValue: String {
switch self {
case .accepted: return "accepted"
case .declined: return "declined"
case let .pending(value): return "\(Constants.preffixPending)\(value)"
case let .creating(value): return "\(Constants.preffixCreate)\(value)"
}
}
}
extension WalletState: Codable { }
public enum WalletKeyType: String, Codable, CustomStringConvertible {
case owner
case active
public var description: String { self.rawValue }
}
public struct WalletKeyUpdateResult {
public let wallet: Wallet
public let oldPrivateKey: String
public let transitionId: String
}
public struct WalletKeyUpdate {
public let wallet: Wallet
public let key: WalletKey
public init(wallet: Wallet, key: WalletKey) {
self.wallet = wallet
self.key = key
}
}
/// Wallet
public protocol Wallet: AnyObject {
/// Name
var name: String { get }
/// Keys
var key: WalletKey { get }
/// Type
var keyType: WalletKeyType { get }
/// State
var state: WalletState { get }
var statePublisher: AnyPublisher<WalletState, Never> { get }
@discardableResult
func updateKeychain(key: WalletKey, using password: String) -> Wallet
func privateKey(password: String?) -> String?
func updatePrivateKeyEncryption(password: String, old: String)
/// Compare two Wallets
func isEquals(other: Wallet) -> Bool
}
public extension Wallet {
var isOnCreationState: Bool {
switch self.state {
case .declined,
.accepted:
return false
case .creating,
.pending:
return true
}
}
var isOnActiveState: Bool {
switch self.state {
case .creating,
.accepted:
return true
case .declined,
.pending:
return false
}
}
func isEquals(other: Wallet) -> Bool {
self.name == other.name &&
self.key == other.key &&
self.keyType == other.keyType &&
self.state == other.state
}
}
@@ -0,0 +1,30 @@
//
// WalletCase.swift
//
//
// Created by Juraldinio on 17.08.2022.
//
import Foundation
/// Wallet Case errors
public enum WalletCaseError: Error {
/// System error
case system
/// Wallet with name already exists
case exists(name: String)
}
/// Wallet case. This mean case that can contain wallet.
public protocol WalletCase: AnyObject {
/// Name
var name: String { get }
/// Key
var key: WalletKey { get }
/// Update key
@discardableResult
func update(key: WalletKey) -> WalletKey
/// Generate new key
@discardableResult
func generateKey() -> WalletKey
}
@@ -0,0 +1,142 @@
//
// WalletKey.swift
//
//
// Created by Juraldinio on 17.08.2022.
//
import Foundation
import eosswift
/// Errors for WalletKey
public enum WalletKeyError: Error {
/// System error
case system
/// Invalid public key
case invalidPublic
/// Invalid private key
case invalidPrivate
/// Invalid pair public and private keys
case invalidPair
///
case unlock
}
/// Represents Wallet key.
/// This keys must pass checks.
public enum WalletKey: Codable, Equatable {
/// Public key
case publicKey(String)
/// Public and Private key
case bunch(publicKey: String, privateKey: String)
public var publicKey: String {
switch self {
case let .publicKey(value): return value
case let .bunch(publicKey: value, privateKey: _): return value
}
}
public var privateKey: String? {
switch self {
case let .bunch(publicKey: _, privateKey: value): return value
default: return nil
}
}
func save() {
}
// MARK: - Codable
private enum CodingKeys: String, CodingKey {
case publicKey
case privateKey
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let publicKey = try container.decode(String.self, forKey: .publicKey)
let privateKey = try container.decodeIfPresent(String.self, forKey: .privateKey)
if let privateKey {
self = .bunch(publicKey: publicKey, privateKey: privateKey)
} else {
self = .publicKey(publicKey)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .publicKey(value):
try container.encode(value, forKey: .publicKey)
case let .bunch(publicKey: publicKey, privateKey: privateKey):
try container.encode(publicKey, forKey: .publicKey)
try container.encode(privateKey, forKey: .privateKey)
}
}
// MARK: - Public static
public static func create(public: String, private: String) throws -> WalletKey {
let privateKey: EOSPrivateKey
do {
privateKey = try EOSPrivateKey(base58: `private`)
} catch {
throw WalletKeyError.invalidPrivate
}
let publicKey: EOSPublicKey
do {
publicKey = try EOSPublicKey(base58: `public`)
} catch {
throw WalletKeyError.invalidPublic
}
guard privateKey.publicKey.base58 == publicKey.base58 else {
throw WalletKeyError.invalidPair
}
return .bunch(publicKey: publicKey.base58, privateKey: privateKey.base58)
}
public static func create(public: String) throws -> WalletKey {
let publicKey: EOSPublicKey
do {
publicKey = try EOSPublicKey(base58: `public`)
} catch {
throw WalletKeyError.invalidPublic
}
return .publicKey(publicKey.base58)
}
public static func create(private: String) throws -> WalletKey {
let privateKey: EOSPrivateKey
do {
privateKey = try EOSPrivateKey(base58: `private`)
} catch {
throw WalletKeyError.invalidPrivate
}
return .bunch(publicKey: privateKey.publicKey.base58, privateKey: privateKey.base58)
}
/// Create key for wallet
public static func create() throws -> WalletKey {
guard let key = try? EOSPrivateKey() else { throw WalletKeyError.system }
return .bunch(publicKey: key.publicKey.base58, privateKey: key.base58)
}
// MARK: - Internal static
static func restore(using name: String, type: WalletKeyType, publicKey: String) throws -> WalletKey {
throw WalletKeyError.unlock
}
}
@@ -0,0 +1,58 @@
//
// DeepLinkActionTests.swift
//
//
// Created by Juraldinio on 31.10.2022.
//
import Foundation
import XCTest
import WalletKit
final class DeepLinkActionTests: XCTestCase {
func testConvertFromString() {
XCTAssertEqual(DeepLinkAction.connect.rawValue, "connect_accounts")
XCTAssertEqual(DeepLinkAction.chat.rawValue, "chat")
XCTAssertEqual(DeepLinkAction.transfer.rawValue, "transfer")
XCTAssertEqual(DeepLinkAction.walletAuth.rawValue, "wallet_auth")
XCTAssertEqual(DeepLinkAction.tokenization.rawValue, "accept_tokenization")
XCTAssertEqual(DeepLinkAction.approveBuy.rawValue, "approve_buy")
XCTAssertEqual(DeepLinkAction.emission.rawValue, "emission")
XCTAssertEqual(DeepLinkAction.chatMessage.rawValue, "chat.message")
XCTAssertEqual(DeepLinkAction.transactionTransfer.rawValue, "transaction.incoming_transfer")
XCTAssertEqual(DeepLinkAction.transactionInheritance.rawValue, "transactions.incoming_inheritance")
XCTAssertEqual(DeepLinkAction.transactionEmission.rawValue, "transactions.emission")
XCTAssertEqual(DeepLinkAction.p2pDealNew.rawValue, "orders.new_deal")
XCTAssertEqual(DeepLinkAction.p2pDealComplete.rawValue, "orders.completed_deal")
XCTAssertEqual(DeepLinkAction.p2pDealCancel.rawValue, "orders.cancelled_deal")
XCTAssertEqual(DeepLinkAction.p2pDealDispute.rawValue, "orders.dispute_deal")
XCTAssertEqual(DeepLinkAction.news.rawValue, "news")
XCTAssertEqual(DeepLinkAction.competitivePrice.rawValue, "competitive_price")
}
func testConvertToString() {
XCTAssertEqual(DeepLinkAction(rawValue: "connect_accounts"), .connect)
XCTAssertEqual(DeepLinkAction(rawValue: "chat"), .chat)
XCTAssertEqual(DeepLinkAction(rawValue: "transfer"), .transfer)
XCTAssertEqual(DeepLinkAction(rawValue: "wallet_auth"), .walletAuth)
XCTAssertEqual(DeepLinkAction(rawValue: "accept_tokenization"), .tokenization)
XCTAssertEqual(DeepLinkAction(rawValue: "approve_buy"), .approveBuy)
XCTAssertEqual(DeepLinkAction(rawValue: "emission"), .emission)
XCTAssertEqual(DeepLinkAction(rawValue: "chat.message"), .chatMessage)
XCTAssertEqual(DeepLinkAction(rawValue: "transaction.incoming_transfer"), .transactionTransfer)
XCTAssertEqual(DeepLinkAction(rawValue: "transactions.incoming_inheritance"), .transactionInheritance)
XCTAssertEqual(DeepLinkAction(rawValue: "transactions.emission"), .transactionEmission)
XCTAssertEqual(DeepLinkAction(rawValue: "orders.new_deal"), .p2pDealNew)
XCTAssertEqual(DeepLinkAction(rawValue: "orders.completed_deal"), .p2pDealComplete)
XCTAssertEqual(DeepLinkAction(rawValue: "orders.cancelled_deal"), .p2pDealCancel)
XCTAssertEqual(DeepLinkAction(rawValue: "orders.dispute_deal"), .p2pDealDispute)
XCTAssertEqual(DeepLinkAction(rawValue: "news"), .news)
XCTAssertEqual(DeepLinkAction(rawValue: "competitive_price"), .competitivePrice)
}
func testConvertFailed() {
XCTAssertNil(DeepLinkAction(rawValue: "news1"))
}
}
@@ -0,0 +1,21 @@
//
// DeepLinkTests.swift
//
//
// Created by Juraldinio on 31.10.2022.
//
import Foundation
import XCTest
@testable
import WalletKit
final class DeepLinkTests: XCTestCase {
func testCreateWithParamsFailed() {
XCTAssertNil(DeepLink.create(params: nil))
XCTAssertNil(DeepLink.create(params: [:]))
}
}
@@ -0,0 +1,99 @@
//
// JSONParsingTests.swift
//
//
// Created by Nut.Tech on 17.02.2023.
//
import XCTest
@testable import WalletKit
fileprivate struct User: Decodable {
let id: Int
let name: String
var employer: String?
static var employerUserInfoKey: CodingUserInfoKey? {
return CodingUserInfoKey(rawValue: "employer")
}
private enum CodingKeys: String, CodingKey {
case id
case name
}
init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
self.id = try keyedContainer.decode(Int.self, forKey: .id)
self.name = try keyedContainer.decode(String.self, forKey: .name)
guard let employerKey = Self.employerUserInfoKey,
let employer = decoder.userInfo[employerKey] as? String else {
return
}
self.employer = employer
}
}
final class JSONParsingTests: XCTestCase {
private enum Locals {
static let username1 = "Username1"
static let username2 = "Username2"
static let userId1 = 1
static let userId2 = 2
static let companyName = "Company Name"
}
func testJsonDecoded() {
let jsonData = """
{"id": \(Locals.userId1), "name": "\(Locals.username1)"}
""".data(using: .utf8)!
guard let user: User = jsonData.jsonDecoded(type: User.self) else {
XCTFail("Error decoding data")
return
}
XCTAssertEqual(user.id, Locals.userId1)
XCTAssertEqual(user.name, Locals.username1)
}
func testJsonDecodedArray() {
let jsonData = """
[{"id": \(Locals.userId1), "name": "\(Locals.username1)"}, {"id": \(Locals.userId2), "name": "\(Locals.username2)"}]
""".data(using: .utf8)!
guard let users: [User] = jsonData.jsonDecoded(type: User.self) else {
XCTFail("Error decoding data")
return
}
XCTAssertEqual(users.count, 2)
XCTAssertEqual(users[0].id, Locals.userId1)
XCTAssertEqual(users[0].name, Locals.username1)
XCTAssertEqual(users[1].id, Locals.userId2)
XCTAssertEqual(users[1].name, Locals.username2)
}
func testJsonDecodedWithUserInfo() {
let jsonData = """
{"id": \(Locals.userId1), "name": "\(Locals.username1)"}
""".data(using: .utf8)!
guard let key = CodingUserInfoKey(rawValue: "employer") else {
XCTFail("Error creating user key")
return
}
let userInfo = [key: Locals.companyName]
guard let user: User = jsonData.jsonDecoded(type: User.self, userInfo: userInfo) else {
XCTFail("Error decoding data")
return
}
XCTAssertEqual(user.id, Locals.userId1)
XCTAssertEqual(user.name, Locals.username1)
XCTAssertEqual(user.employer, Locals.companyName)
}
}
@@ -0,0 +1,75 @@
//
// KeychainMock.swift
//
//
// Created by NUT.TECH on 16.02.2023.
//
import Foundation
import WalletFoundation
final class KeychainMock: KeychainProtocol {
private enum KeychainMockLocals {
static let password = "PWD"
static let biometric = "BIO"
}
typealias Value = (value: String, password: String)
private var storage: [String: Value]
init(storage: [String: Value]? = nil) {
self.storage = storage ?? [:]
}
func exist(_ key: Key) -> Bool {
let commonKey = key.with(KeychainMockLocals.password)
return self.storage[commonKey.rawValue].isExist
}
func bioExist(_ key: Key) -> Bool {
let commonKey = key.with(KeychainMockLocals.biometric)
return self.storage[commonKey.rawValue] != nil
}
subscript(biometric key: Key) -> String? {
let commonKey = key.with(KeychainMockLocals.biometric).rawValue
let object = self.storage[commonKey]
return object?.value
}
subscript(key: Key, password password: String) -> String? {
get {
let commonKey = key.with(KeychainMockLocals.password).rawValue
let object = self.storage[commonKey]
return object?.password == password ? object?.value : nil
}
set {
self.update(key, password: password, newValue: newValue)
}
}
private func update(_ key: Key, password: String, newValue: String?) {
if let value = newValue {
self.updateStorage(key: key.with(KeychainMockLocals.password), password: password, value: value)
self.updateStorage(key: key.with(KeychainMockLocals.biometric), password: password, value: value)
} else {
self.updateStorage(key: key.with(KeychainMockLocals.password), password: password, value: nil)
self.updateStorage(key: key.with(KeychainMockLocals.biometric), password: password, value: nil)
}
}
private func updateStorage(key: Key, password: String, value: String?) {
let stringKey = key.rawValue
if self.storage[stringKey] != nil, self.storage[stringKey]?.password == password {
if let value {
self.storage[stringKey]?.value = value
} else {
self.storage.removeValue(forKey: stringKey)
}
} else if let value {
self.storage[stringKey] = Value(value: value, password: password)
}
}
}
@@ -0,0 +1,103 @@
//
// PrivacyKeeperTests.swift
//
//
// Created by user on 16.02.2023.
//
import XCTest
@testable import WalletKit
@testable import WalletFoundation
final class PrivacyKeeperTests: XCTestCase {
func testAES() throws {
let commonKey = "PrivacyKeeper.password"
let encryptedKey = "mdHBVh0lxLT8VBJnL9MYuM/pxWi/QmTCXmW8YZQO8Cs="
let key = "96080AE5042241D9BFF30330C9625201"
let iv = "7C515B5233314B42"
do {
let encryptedCommonKey = try PrivacyKeeper.aesEncrypt(string: commonKey, key: key, iv: iv)
let decryptedCommonKey = try PrivacyKeeper.aesDecrypt(string: encryptedCommonKey, key: key, iv: iv)
XCTAssertEqual(encryptedCommonKey, encryptedKey, "Incorrect encryption result")
XCTAssertEqual(decryptedCommonKey, commonKey, "Incorrect decryption result")
} catch {
XCTFail(error.localizedDescription)
}
}
func testPassword() {
let password = "0000"
let keychainMock = KeychainMock()
let keeper = PrivacyKeeper(keychain: keychainMock)
do {
XCTAssertFalse(keeper.isPasswordExists, "Incorrect isPasswordExists result")
try keeper.update(password: password)
XCTAssertTrue(keeper.isPasswordExists, "Incorrect isPasswordExists result")
XCTAssertTrue(keeper.accept(password: password), "Incorrect password")
let newPassword = "1111"
try keeper.update(password: newPassword, old: password)
XCTAssertTrue(keeper.accept(password: newPassword), "Incorrect password")
let incorrectPassword = "2222"
XCTAssertFalse(keeper.accept(password: incorrectPassword), "Incorrect password check")
keeper.reset()
XCTAssertFalse(keeper.accept(password: newPassword), "Incorrect password reset")
XCTAssertFalse(keeper.isPasswordExists, "Incorrect isPasswordExists result")
} catch {
XCTFail(error.localizedDescription)
}
}
func testPrivateKey() {
let password = "0000"
let privateKey = "privateKey1111"
let commonKey = "accountKey@malinka.privateKey"
let keychainMock = KeychainMock()
let keeper = PrivacyKeeper(keychain: keychainMock)
do {
try keeper.update(password: password)
XCTAssertTrue(keeper.accept(password: password), "Incorrect password")
keeper.update(privateKey: privateKey,
for: commonKey,
password: password)
let readPrivateKey = keeper.privateKey(for: commonKey, password: password)
XCTAssertNotNil(readPrivateKey, "Error setting private key to keychain")
XCTAssertEqual(readPrivateKey, privateKey, "Incorrect private key stored in keychain")
keeper.reset()
let nilPrivateKey = keeper.privateKey(for: commonKey, password: password)
XCTAssertNil(nilPrivateKey, "Incorrect private key reset")
} catch {
XCTFail(error.localizedDescription)
}
}
func testMigration() {
let password = "0000"
let privateKey = "privateKey1111"
let privateKey2 = "privateKey2222"
let migratedCommonKey = "accountName@owner.privateKey"
let keyToMigrate = CommonKey("accountKey2@malinka.privateKey")
let oldKey = CommonKey.key("accountName", suffix: "privateKey")
let newKey = CommonKey.key("accountName@owner", suffix: "privateKey")
let storage = ["accountName.privateKey.PWD": KeychainMock.Value(value: privateKey, password: password),
"PrivacyKeeper.password.PWD": KeychainMock.Value(value: password, password: password),
"accountKey2@malinka.privateKey.PWD": KeychainMock.Value(value: privateKey2, password: password)]
let keychainMock = KeychainMock(storage: storage)
let keeper = PrivacyKeeper(keychain: keychainMock)
keeper.migrate(oldKey: oldKey, newKey: newKey, password: password)
XCTAssertNil(keeper.privateKey(for: oldKey.rawValue, password: password), "Incorrect private key")
XCTAssertEqual(keeper.privateKey(for: migratedCommonKey, password: password), privateKey, "Incorrect private key")
keeper.migrate(oldKey: keyToMigrate, newKey: keyToMigrate, password: password)
XCTAssertEqual(keeper.privateKey(for: keyToMigrate.rawValue, password: password), privateKey2, "Incorrect private key")
}
}
+36
View File
@@ -0,0 +1,36 @@
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WalletNetwork",
platforms: [.iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "WalletNetwork",
targets: ["WalletNetwork"]),
],
dependencies: [
.package(name: "eosswift", path: "../../Vendors/spm/eos-swift"),
.package(name: "EosioSwift", path: "../Vendors/spm/eosio-swift-1.0.0"),
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.6.2")),
.package(name: "WalletFoundation", path: "../WalletFoundation"),
.package(name: "Mocker", path: "../../Vendors/spm/Mocker-3.0.1")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "WalletNetwork",
dependencies: ["EosioSwift", "eosswift", "Alamofire", "WalletFoundation"],
path: "./Sources"),
.testTarget(
name: "WalletNetworkTests",
dependencies: ["WalletNetwork", "Mocker"],
resources: [
.process("Resources")
])
]
)
@@ -0,0 +1,52 @@
//
// AccountHyperionService.swift
//
//
// Created by Juraldinio on 12/4/22.
//
import Foundation
import Alamofire
import WalletFoundation
public struct AccountHyperionService: NetworkService {
private struct Response: Codable {
let accountNames: [String]
enum CodingKeys: String, CodingKey {
case accountNames = "account_names"
}
}
let environment: NetworkEnvironment
public init(environment: NetworkEnvironment) {
self.environment = environment
}
// Methods
public func fetchNamesHyperion(publicKey: String) async -> [String] {
guard var components = URLComponents(url: self.environment.hyperion.url(), resolvingAgainstBaseURL: false) else {
return []
}
components.path = "/v2/state/get_key_accounts"
components.queryItems = [URLQueryItem(name: "public_key", value: publicKey),
URLQueryItem(name: "skip", value: "0"),
URLQueryItem(name: "limit", value: "5000")]
guard let url = components.url else { return [] }
let response = try? await AF.request(
url,
method: .get,
headers: self.environment.usernames.httpHeaders)
.serializingDecodable(Response.self)
.value
guard let response = response else { return [] }
return response.accountNames
}
}
@@ -0,0 +1,156 @@
//
// AccountService.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
import Alamofire
import WalletFoundation
public struct AccountService: NetworkService {
public enum ServiceError: Error {
case notificationEmptyAccount
case notificationEmptyToken
case notificationEmptyLanguage
}
let environment: NetworkEnvironment
let session: Session
public init(environment: NetworkEnvironment) {
self.environment = environment
if let configuration = environment.configuration {
configuration.headers = HTTPHeaders.default
self.session = Session(configuration: configuration)
} else {
self.session = AF
}
}
// Methods
public func create(username: String, pubKey: String, deviceId: String) async throws -> WalletOrder {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let variables = AccountCreateRequest.Variables(username: username, pubKey: pubKey, deviceId: deviceId)
let result = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: AccountCreateRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders
)
.responseString { print(">>>>>> \($0)") }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<AccountCreateResponse>.self, decoder: decoder)
.value
switch result?.result {
case let .firstType(response): return response.order
case let .secondType(error): throw error.networkServiceError
default: throw NetworkServiceError.uncatched
}
}
public func order(with id: String) async throws -> WalletOrder {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let variables = AccountOrderRequest.Variables(uid: id)
let result = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: AccountOrderRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders
)
.responseString { print(">>>>>> \($0)") }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<AccountOrderResponse>.self, decoder: decoder)
.value
switch result?.result {
case let .firstType(response): return response.order
case let .secondType(error): throw error.networkServiceError
default: throw NetworkServiceError.uncatched
}
}
public enum AvailableType {
case available
case alreadyTaken
case system
}
public func isAvailable(username: String) async -> AvailableType {
guard !username.isEmpty else { return .system }
let variables = AccountCheckNameRequest.Variables(username: username)
let response = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: AccountCheckNameRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders)
.responseString { print("checkWalletName: \($0)") }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<AccountCheckNameResponse>.self)
.value
guard let response = response else { return .system }
switch response.result {
case .firstType: return .available
case let .secondType(type):
switch type {
case let .application(error: errorType) where errorType == "ALREADY_TAKEN":
return .alreadyTaken
default: return .system
}
}
}
public func updateNotification(token: String, accounts: [String], language: String) async throws {
if accounts.isEmpty { throw ServiceError.notificationEmptyAccount }
if token.isEmpty { throw ServiceError.notificationEmptyToken }
if language.isEmpty { throw ServiceError.notificationEmptyLanguage }
let variables = AccountNotificationTokenRequest.Variables(
deviceToken: token,
deviceType: "IOS",
eosAccounts: accounts,
langCode: language,
application: "MALINKA"
)
let result = try? await self.session.request(
self.environment.backend.url,
method: .post,
parameters: AccountNotificationTokenRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.backend.httpHeaders)
// .responseString { print("updateNotification: \($0)") }
.serializingDecodable(GraphQLResult<AccountNotificationTokenResponse>.self)
.value
print(result ?? "")
}
}
@@ -0,0 +1,50 @@
//
// AccountNodeService.swift
//
//
// Created by Nut.Tech on 16.01.2023.
//
import Alamofire
import Foundation
public struct NodeService: NetworkService {
let environment: NetworkEnvironment
let session: Session
public init(environment: NetworkEnvironment) {
self.environment = environment
if let configuration = environment.configuration {
configuration.headers = HTTPHeaders.default
self.session = Session(configuration: configuration)
} else {
self.session = AF
}
}
public func fetchActions(account: String,
limit: Int,
skip: Int? = nil) async throws -> [NodeAction] {
guard let url = URL(string: "https://eos.greymass.com") else { return [] } //self.environment.hyperion.url()
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return [] }
components.path = "/v1/history/get_actions"
components.queryItems = [URLQueryItem(name: "account_name", value: account),
URLQueryItem(name: "offset", value: "\(-limit)")]
if let skip {
components.queryItems?.append(URLQueryItem(name: "pos", value: "\(skip)"))
}
do {
let result = try await self.session.request(
components,
method: .get
).serializingDecodable(NodeResponse.self)
.value
return result.actions
} catch {
throw error
}
}
}
@@ -0,0 +1,31 @@
//
// AccountCheckNameRequest.swift
//
//
// Created by Juraldinio on 11.08.2022.
//
import Foundation
struct AccountCheckNameRequest: Encodable {
struct Variables: Encodable {
let username: String
}
// MARK: - GraphQL
let variables: Variables
let operationName = "checkWalletName"
let query: String = #"""
query checkWalletName(
$username: String!
) {
checkWalletName(username: $username) { errors }
}
"""#.trimCompact()
}
struct AccountCheckNameResponse: GraphQLResponse {
static let node = "checkWalletName"
}
@@ -0,0 +1,37 @@
//
// AccountCreateRequest.swift
//
//
// Created by Juraldinio on 24.08.2022.
//
import Foundation
struct AccountCreateRequest: Encodable {
struct Variables: Encodable {
let username: String
let pubKey: String
let deviceId: String
}
// MARK: - GraphQL
let variables: Variables
let operationName = "CreateAccount"
let query: String = #"""
mutation CreateAccount($username: String!, $pubKey: String!, $deviceId: String!) {
createAccount(username: $username, pubKey: $pubKey, deviceId: $deviceId) {
errors, order { timeout, id, modified, status }, id
}
}
"""#.trimCompact()
}
struct AccountCreateResponse: GraphQLResponse {
static let node = "createAccount"
let id: Int
let order: WalletOrder
}
@@ -1,24 +1,25 @@
//
// AddPushNotificationDeviceTokenGraphQLRequest.swift
// PayCash
// AccountNotificationTokenRequest.swift
//
//
// Created by Saveliy Stavitsky on 7/30/21.
// Copyright © 2021 AM. All rights reserved.
// Created by Juraldinio on 01.08.2022.
//
import Foundation
struct UpdatePushNotificationDeviceTokenGraphQLRequest: Encodable {
struct ResponseData: Decodable { }
struct AccountNotificationTokenRequest: Encodable {
struct Variables: Encodable {
let deviceToken: String
let deviceType: String
let eosAccounts: [String]
let langCode: String
let application: String
}
// MARK: - GraphQL
let variables: Variables
let operationName = "updatePushNotificationDeviceToken"
let query: String = #"""
@@ -27,15 +28,22 @@ struct UpdatePushNotificationDeviceTokenGraphQLRequest: Encodable {
$deviceType: DeviceTypeEnum,
$eosAccounts: [String]!,
$langCode: Lang!
$application: ApplicationEnum
) {
updatePushNotificationDeviceToken(
deviceToken: $deviceToken,
deviceType: $deviceType,
eosAccounts: $eosAccounts,
langCode: $langCode
langCode: $langCode,
application: $application
) {
errors
}
}
"""#
"""#.trimCompact()
}
struct AccountNotificationTokenResponse: GraphQLResponse {
static let node = "updatePushNotificationDeviceToken"
}
@@ -0,0 +1,45 @@
//
// AccountOrderRequest.swift
//
//
// Created by Juraldinio on 29.08.2022.
//
import Foundation
struct AccountOrderRequest: Encodable {
struct Variables: Encodable {
let uid: String
}
// MARK: - GraphQL
let variables: Variables
let operationName = "accountOrder"
let query: String = #"""
query accountOrder(
$uid: String!
) {
accountOrder(uid: $uid) { timeout, id, modified, status, created }
}
"""#.trimCompact()
}
struct AccountOrderResponse: GraphQLResponse {
private enum Common: String, CodingKey {
case order = "accountOrder"
}
static let node = ""
let order: WalletOrder
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Common.self)
self.order = try container.decode(WalletOrder.self, forKey: .order)
}
}
@@ -0,0 +1,31 @@
//
// File.swift
//
//
// Created by Juraldinio on 8/28/22.
//
import Foundation
public struct WalletOrder: Codable {
public enum Status: String, Codable {
/// Order created.
case active = "ACTIVE"
/// Order expired.
case expired = "EXPIRED"
/// Sent to blockchain.
case executed = "EXECUTED"
/// Accepting EOS transaction. Already sent, but not all blocks created.
case creating = "TRX_IN_CHAIN"
/// Failed.
case failed = "FAILED"
/// Created and completed.
case completed = "COMPLETED"
}
public let timeout: Date
public let modified: Date
public let status: Status
public let id: String
}
@@ -0,0 +1,15 @@
//
// NodeAct.swift
//
//
// Created by Nut.Tech on 10.01.2023.
//
import Foundation
public struct NodeAct: Decodable {
public let data: NodeData
public let name: String
public let authorization: [NodeActAuthorization]
public let account: String
}
@@ -0,0 +1,13 @@
//
// NodeActAuthorization.swift
//
//
// Created by Nut.Tech on 10.01.2023.
//
import Foundation
public struct NodeActAuthorization: Codable {
public let actor: String
public let permission: String
}
@@ -0,0 +1,22 @@
//
// NodeAction.swift
//
//
// Created by Nut.Tech on 10.01.2023.
//
import Foundation
public struct NodeAction: Decodable {
public let accountActionSeq: Int
public let actionTrace: NodeActionTrace
public let globalActionSeq: Int
public let irreversible: Bool
enum CodingKeys: String, CodingKey {
case accountActionSeq = "account_action_seq"
case actionTrace = "action_trace"
case globalActionSeq = "global_action_seq"
case irreversible
}
}
@@ -0,0 +1,24 @@
//
// NodeActionTrace.swift
//
//
// Created by Nut.Tech on 11.01.2023.
//
import Foundation
public struct NodeActionTrace: Decodable {
public let act: NodeAct
public let blockNum: Int
public let blockTime: String
public let receiver: String?
public let trxId: String
enum CodingKeys: String, CodingKey {
case blockNum = "block_num"
case blockTime = "block_time"
case trxId = "trx_id"
case receiver
case act
}
}
@@ -0,0 +1,47 @@
//
// NodeData.swift
//
// Created by NUT.TECH on 10.01.2023.
//
import Foundation
public struct NodeData: Decodable {
public let data: [String: Any]
public init(from decoder: Decoder) throws {
// Create a decoding container using DynamicCodingKeys
// The container will contain all the JSON first level key
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
var tempData = [String: Any]()
// Loop through each key
for key in container.allKeys {
if let value = try? container.decode(NodeData.self, forKey: key) {
tempData[key.stringValue] = value.data
} else if let value = try? container.decode(String.self, forKey: key) {
tempData[key.stringValue] = value
} else if let value = try? container.decode(Int.self, forKey: key) {
tempData[key.stringValue] = value
}
}
self.data = tempData
}
private struct DynamicCodingKeys: CodingKey {
// Use for string-keyed dictionary
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
// Use for integer-keyed dictionary
var intValue: Int?
init?(intValue: Int) {
// We are not using this, thus just return nil
return nil
}
}
}
@@ -0,0 +1,12 @@
//
// NodeResponse.swift
//
//
// Created by Nut.Tech on 11.01.2023.
//
import Foundation
public struct NodeResponse: Decodable {
public let actions: [NodeAction]
}
@@ -0,0 +1,41 @@
//
// CommonEnvironment.swift
//
//
// Created by Juraldinio on 04.08.2022.
//
import Foundation
public struct CommonEnvironment: NetworkEnvironment {
public let configuration: URLSessionConfiguration?
public let usernames: NetworkStaticAPIRecord
public let hyperion: NetworkDynamicAPIRecord
public let backend: NetworkStaticAPIRecord
public let node: NetworkDynamicAPIRecord
public init(usernames: NetworkStaticAPIRecord,
backend: NetworkStaticAPIRecord,
hyperion: NetworkDynamicAPIRecord,
node: NetworkDynamicAPIRecord) {
self.configuration = nil
self.usernames = usernames
self.backend = backend
self.hyperion = hyperion
self.node = node
}
public init(usernames: URL,
backend: URL,
hyperion: @escaping NetworkDynamicAPIRecord.Dynamic,
node: @escaping NetworkDynamicAPIRecord.Dynamic,
headers: [String: String] = [:]) {
let userRecord = NetworkStaticAPIRecord(url: usernames, headers: headers)
let backendRecord = NetworkStaticAPIRecord(url: backend, headers: headers)
let hyperionRecord = NetworkDynamicAPIRecord(url: hyperion, headers: headers)
let nodeRecord = NetworkDynamicAPIRecord(url: node, headers: headers)
self.init(usernames: userRecord, backend: backendRecord, hyperion: hyperionRecord, node: nodeRecord)
}
}
@@ -0,0 +1,106 @@
//
// GraphQLResult.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
import WalletFoundation
protocol GraphQLResponse: Decodable {
static var node: String { get }
}
struct GraphQLError: Decodable {
let message: String
}
enum GraphQLResponseError: Decodable {
enum Place: CustomStringConvertible {
case node
case failed
var description: String {
switch self {
case .node: return "node"
case .failed: return "failed"
}
}
}
case system(errors: [GraphQLError])
case application(error: String)
case decode(Place)
init(from decoder: Decoder) throws {
self = .system(errors: [])
}
// MARK: -
var networkServiceError: NetworkServiceError {
switch self {
case let .system(errors: errors): return .gqlSystem(errors.map { $0.message })
case let .application(error: error): return .gqlApplication(error)
case let .decode(place): return .gqlDecode(place.description)
}
}
}
let KEY_ERRORS = "errors"
struct GraphQLResult<Resp: GraphQLResponse>: Decodable {
let result: Either<Resp, GraphQLResponseError>
private enum Common: String, CodingKey {
case data
case errors
}
private struct DynamicCodingKeys: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil }
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Common.self)
// System error catch
if let errors = try? container.decode([GraphQLError].self, forKey: .errors) {
self.result = .secondType(.system(errors: errors))
return
}
// If node is empty
guard !Resp.node.isEmpty else {
if let response = try? container.decode(Resp.self, forKey: .data) {
self.result = .firstType(response)
} else {
self.result = .secondType(.decode(.failed))
}
return
}
let dataContainer = try container.nestedContainer(keyedBy: DynamicCodingKeys.self, forKey: .data)
guard let key = DynamicCodingKeys(stringValue: Resp.node) else {
self.result = .secondType(.decode(.node))
return
}
if let responseContainer = try? dataContainer.nestedContainer(keyedBy: DynamicCodingKeys.self, forKey: key),
let errorKey = DynamicCodingKeys(stringValue: KEY_ERRORS),
let error = try? responseContainer.decode(String.self, forKey: errorKey) {
self.result = .secondType(.application(error: error))
} else if let response = try? dataContainer.decode(Resp.self, forKey: key) {
self.result = .firstType(response)
} else {
self.result = .secondType(.decode(.failed))
}
}
}
@@ -0,0 +1,84 @@
//
// NetworkService.swift
//
//
// Created by Juraldinio on 04.08.2022.
//
import Foundation
import Alamofire
public struct NetworkStaticAPIRecord {
public typealias Headers = [String: String]
public let url: URL
public let headers: Headers?
public init(url: URL, headers: Headers?) {
self.url = url
self.headers = headers
}
var httpHeaders: HTTPHeaders? {
guard let headers = self.headers else { return nil }
return HTTPHeaders(headers.compactMap { key, value in HTTPHeader(name: key, value: value) })
}
}
public struct NetworkDynamicAPIRecord {
public typealias Headers = [String: String]
public typealias Dynamic = () -> URL
public let url: Dynamic
public let headers: Headers?
public init(url: @escaping Dynamic, headers: Headers?) {
self.url = url
self.headers = headers
}
var httpHeaders: HTTPHeaders? {
guard let headers = self.headers else { return nil }
return HTTPHeaders(headers.compactMap { key, value in HTTPHeader(name: key, value: value) })
}
}
public protocol NetworkEnvironment {
var configuration: URLSessionConfiguration? { get }
var usernames: NetworkStaticAPIRecord { get }
var backend: NetworkStaticAPIRecord { get }
var hyperion: NetworkDynamicAPIRecord { get }
var node: NetworkDynamicAPIRecord { get }
func isEquals(other: NetworkEnvironment) -> Bool
}
public protocol NetworkService {
init(environment: NetworkEnvironment)
}
public enum NetworkServiceError: Error {
case invalidUrl
case gqlSystem([String])
case gqlApplication(String)
case gqlDecode(String)
case uncatched
}
// MARK: - Equatable
extension NetworkEnvironment {
public func isEquals(other: NetworkEnvironment) -> Bool {
return self.configuration == other.configuration &&
self.usernames.url == other.usernames.url &&
self.usernames.headers == other.usernames.headers &&
self.backend.url == other.backend.url &&
self.backend.headers == other.backend.headers &&
self.hyperion.url() == other.hyperion.url() &&
self.hyperion.headers == other.hyperion.headers &&
self.node.url() == other.node.url() &&
self.node.headers == other.node.headers
}
}
@@ -0,0 +1,97 @@
//
// DeviceService.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
import Alamofire
public struct DeviceService: NetworkService {
let environment: NetworkEnvironment
let session: Session
public init(environment: NetworkEnvironment) {
self.environment = environment
if let configuration = environment.configuration {
configuration.headers = HTTPHeaders.default
self.session = Session(configuration: configuration)
} else {
self.session = AF
}
}
public func createDevice(uuid: String) async throws -> Device {
let variables = RegisterDeviceRequest.Variables(cid: uuid)
let result = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: RegisterDeviceRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders
)
// .responseString { print($0) }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<RegisterDeviceResponse>.self)
.value
switch result?.result {
case let .firstType(device): return Device(uuid: uuid, id: device.uid, isTrusted: device.isTrustedNow)
case let .secondType(error): throw error.networkServiceError
default: throw NetworkServiceError.uncatched
}
}
public func stateDevice(id: String) async throws -> StateDevice {
let variables = StateDeviceRequest.Variables(uid: id)
let result = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: StateDeviceRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders
)
// .responseString { print(">>>>>> \($0)") }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<StateDeviceResponse>.self)
.value
switch result?.result {
case let .firstType(state): return StateDevice(availableAccounts: state.availableAccounts,
isTrusted: state.isTrustedNow)
case let .secondType(error): throw error.networkServiceError
default: throw NetworkServiceError.uncatched
}
}
public func checkDevice(id: String, token: String) async throws -> DeviceStatus {
let variables = CheckDeviceRequest.Variables(uid: id, token: token)
let result = try? await self.session.request(
self.environment.usernames.url,
method: .post,
parameters: CheckDeviceRequest(variables: variables),
encoder: JSONParameterEncoder.default,
headers: self.environment.usernames.httpHeaders
)
// .responseString { print($0) }
// .cURLDescription { print($0) }
.serializingDecodable(GraphQLResult<CheckDeviceResponse>.self)
.value
switch result?.result {
case let .firstType(status): return status.status
case let .secondType(error): throw error.networkServiceError
default: throw NetworkServiceError.uncatched
}
}
}
@@ -0,0 +1,35 @@
//
// CheckDeviceRequest.swift
//
//
// Created by Juraldinio on 24.08.2022.
//
import Foundation
struct CheckDeviceRequest: Encodable {
struct Variables: Encodable {
let uid: String
let token: String
}
// MARK: - GraphQL
let operationName = "CheckDevice"
let query: String = #"""
mutation CheckDevice($uid: String!, $token: String!) {
checkDevice(uid: $uid, token: $token) {
status
}
}
"""#.trimCompact()
let variables: Variables
}
struct CheckDeviceResponse: GraphQLResponse {
static let node = "checkDevice"
let status: DeviceStatus
}
@@ -0,0 +1,36 @@
//
// RegisterDeviceRequest.swift
//
//
// Created by Juraldinio on 04.08.2022.
//
import Foundation
struct RegisterDeviceRequest: Encodable {
struct Variables: Encodable {
let cid: String
}
// MARK: - GraphQL
let operationName = "RegisterDevice"
let query: String = #"""
mutation RegisterDevice($cid: String) {
registerDevice(kind: IOS, cid: $cid) {
isTrustedNow
uid
}
}
"""#.trimCompact()
let variables: Variables
}
struct RegisterDeviceResponse: GraphQLResponse {
static let node = "registerDevice"
let isTrustedNow: Bool
let uid: String
}
@@ -0,0 +1,34 @@
//
// StateDeviceRequest.swift
//
//
// Created by Juraldinio on 29.08.2022.
//
import Foundation
struct StateDeviceRequest: Encodable {
struct Variables: Encodable {
let uid: String
}
// MARK: - GraphQL
let variables: Variables
let operationName = "device"
let query: String = #"""
query device(
$uid: String!
) {
device(uid: $uid) { availableAccounts, isTrustedNow }
}
"""#.trimCompact()
}
struct StateDeviceResponse: GraphQLResponse {
static let node = "device"
let availableAccounts: Int
let isTrustedNow: Bool
}
@@ -0,0 +1,18 @@
//
// Device.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
/// Model represent device
public struct Device {
/// Device generated Identity UUID
public let uuid: String
/// Server generated Identity
public let id: String
/// Is device trusted
public let isTrusted: Bool
}
@@ -0,0 +1,14 @@
//
// DeviceStatus.swift
//
//
// Created by Juraldinio on 23.08.2022.
//
import Foundation
public enum DeviceStatus: String, Decodable {
case valid = "Valid"
case invalid = "Invalid"
case unexpected = "UnexpectedError"
}
@@ -0,0 +1,13 @@
//
// StateDevice.swift
//
//
// Created by Juraldinio on 29.08.2022.
//
import Foundation
public struct StateDevice {
public let availableAccounts: Int
public let isTrusted: Bool
}
@@ -0,0 +1,68 @@
//
// EOSService.swift
//
//
// Created by Juraldinio on 31.07.2022.
//
import Foundation
import Alamofire
import WalletFoundation
import EosioSwift
import eosswift
public struct EOSService: NetworkService {
let environment: NetworkEnvironment
public init(environment: NetworkEnvironment) {
self.environment = environment
}
// Methods
public func requireNodes() async -> [String] {
let result = try? await AF.request(
self.environment.backend.url,
method: .post,
parameters: EOSNodeRequest(),
encoder: JSONParameterEncoder.default,
headers: self.environment.backend.httpHeaders)
// .responseString(completionHandler: { print(">>>~~~>>> \($0)") })
.serializingDecodable(GraphQLResult<EOSNodesResponse>.self)
.value
switch result?.result {
case let .firstType(value):
return value.nodes.map { $0.url }
default:
return []
}
}
public func requireHyperions() async -> [String] {
let result = try? await AF.request(
self.environment.backend.url,
method: .post,
parameters: EOSHyperionsRequest(),
encoder: JSONParameterEncoder.default,
headers: self.environment.backend.httpHeaders)
.serializingDecodable(GraphQLResult<EOSHyperionsResponse>.self)
.value
switch result?.result {
case let .firstType(value):
return value.hyperions.map { $0.url }
default:
return []
}
}
public func getAccount(_ account: String, completion: @escaping (EosioResult<EosioRpcAccountResponse, EosioError>) -> Void) {
EosioRpcProvider(endpoint: self.environment.node.url(), headers: self.environment.node.headers)
.getAccount(requestParameters: EosioRpcAccountRequest(accountName: account), completion: completion)
}
}
@@ -0,0 +1,35 @@
//
// EOSHyperionsRequest.swift
//
//
// Created by NUT.Tech on 01.08.2022.
//
import Foundation
struct EOSHyperionsRequest: Encodable {
// MARK: - GraphQL
let query: String = #"""
query {
getEOSHyperions {
hyperions {
url
}
}
}
"""#.trimCompact()
}
struct EOSHyperionsResponse: GraphQLResponse {
static let node = "getEOSHyperions"
struct Hyperion: Decodable {
let url: String
}
let hyperions: [Hyperion]
}
@@ -0,0 +1,37 @@
//
// EOSNodesQueryRequest.swift
//
//
// Created by Juraldinio on 01.08.2022.
//
import Foundation
struct EOSNodeRequest: Encodable {
// MARK: - GraphQL
let operationName = "getEOSNodes"
let query: String = #"""
query getEOSNodes {
getEOSNodes {
nodes {
url
}
}
}
"""#.trimCompact()
let variables: [String: String]? = nil
}
struct EOSNodesResponse: GraphQLResponse {
static let node = "getEOSNodes"
struct Node: Decodable {
let url: String
}
let nodes: [Node]
}
@@ -0,0 +1,311 @@
//
// DeviceServiceTests.swift
//
//
// Created by Juraldinio on 13.01.2023.
//
import Foundation
import XCTest
import WalletFoundation
@testable
import WalletNetwork
import Mocker
final class DeviceServiceTests: XCTestCase {
override func tearDown() {
Mocker.removeAll()
}
// MARK: - CreateDevice
// When receive incorrect status we must throw uncatched exception.
func testCreateDeviceFailUncatched() async {
let url = URL(string: "https://malinka.life/create")!
let uuid = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 500,
data: [ .post: Data() ]
).register()
do {
_ = try await service.createDevice(uuid: uuid)
XCTFail("Require to fail")
} catch NetworkServiceError.uncatched {
XCTAssertTrue(true)
} catch {
XCTFail("Require '\(NetworkServiceError.uncatched)' exception")
}
}
// When we receive incorrect data that we can't decode.
func testCreateDeviceFailDecode() async {
let url = URL(string: "https://malinka.life/create")!
let uuid = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 200,
data: [ .post: DeviceServiceData.createDeviceDecodeFailed ]
).register()
do {
_ = try await service.createDevice(uuid: uuid)
} catch NetworkServiceError.gqlDecode(let message) {
XCTAssertEqual(message, "failed")
} catch {
XCTFail("Catch exceprion '\(error)' but require NetworkServiceError.gqlDecode")
}
}
// When we successed create device.
func testCreateDeviceSuccessTrusted() async {
let url = URL(string: "https://malinka.life/create")!
let uuid = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 200,
data: [ .post: DeviceServiceData.createDeviceTrusted ]
).register()
do {
let device = try await service.createDevice(uuid: uuid)
XCTAssertTrue(device.isTrusted)
XCTAssertEqual(device.id, "a88c4532a09024c245e9")
XCTAssertEqual(device.uuid, uuid)
} catch {
XCTFail("Catch exceprion \(error)")
}
}
// Test send correct request.
func testCreateDeviceSuccessRequest() async {
let url = URL(string: "https://malinka.life/query")!
let uuid = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
let requestExpectation = XCTestExpectation(description: "Request")
let completionExpectation = XCTestExpectation(description: "Completion")
var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.post: Data()])
mock.onRequestHandler = OnRequestHandler(httpBodyType: [String: Either<String, [String: String]>].self) { request, object in
XCTAssertEqual(url, mock.request.url)
XCTAssertEqual(request.method, .post)
XCTAssertNotNil(object)
let registerDevice = RegisterDeviceRequest(variables: RegisterDeviceRequest.Variables(cid: uuid))
XCTAssertNotNil(object!["query"])
object!["query"]!.map(firstTypeTransform: {
XCTAssertEqual($0, registerDevice.query)
}, secondTypeTransform: {
XCTFail("Require 'String' type but receive \($0)")
})
XCTAssertNotNil(object!["operationName"])
object!["operationName"]!.map(firstTypeTransform: {
XCTAssertEqual($0, registerDevice.operationName)
}, secondTypeTransform: {
XCTFail("Require 'String' type but receive \($0)")
})
XCTAssertNotNil(object!["variables"])
object!["variables"]!.map(firstTypeTransform: {
XCTFail("Require '[String: String]' type but receive \($0)")
}, secondTypeTransform: {
XCTAssertNotNil($0["cid"])
XCTAssertEqual($0["cid"], registerDevice.variables.cid)
})
requestExpectation.fulfill()
}
mock.completion = {
completionExpectation.fulfill()
}
mock.register()
_ = try? await service.createDevice(uuid: uuid)
wait(for: [requestExpectation, completionExpectation], timeout: 2.0)
}
// MARK: - StateDevice
// Receive null values in fields - we must throw exception.
func testQueryStateDeviceException() async {
let url = URL(string: "https://malinka.life/query")!
let id = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 200,
data: [ .post: DeviceServiceData.queryDeviceStateNull ]
).register()
do {
_ = try await service.stateDevice(id: id)
XCTFail("Require catch exceprion NetworkServiceError.gqlDecode")
} catch {
XCTAssertTrue(true)
}
}
// Receive untrusted status.
func testQueryStateDeviceUntrusted() async {
let url = URL(string: "https://malinka.life/query")!
let id = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 200,
data: [ .post: DeviceServiceData.queryDeviceStateUntrusted ]
).register()
do {
let state = try await service.stateDevice(id: id)
XCTAssertFalse(state.isTrusted)
XCTAssertEqual(state.availableAccounts, 0)
} catch {
XCTFail("Catch exceprion \(error)")
}
}
// Receive trusted status.
func testQueryStateDeviceTrusted() async {
let url = URL(string: "https://malinka.life/query")!
let id = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
Mock(url: url,
dataType: .json,
statusCode: 200,
data: [ .post: DeviceServiceData.queryDeviceStateTrusted ]
).register()
do {
let state = try await service.stateDevice(id: id)
XCTAssertTrue(state.isTrusted)
XCTAssertEqual(state.availableAccounts, 2)
} catch {
XCTFail("Catch exceprion \(error)")
}
}
// Check sent correct request.
func testQueryStateDeviceRequest() async {
let url = URL(string: "https://malinka.life/query")!
let id = "1234-0987-4567"
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubUsernames(configuration: configuration, url: url)
let service = DeviceService(environment: env)
let requestExpectation = XCTestExpectation(description: "Request")
let completionExpectation = XCTestExpectation(description: "Completion")
var mock = Mock(url: url, dataType: .json, statusCode: 200, data: [.post: Data()])
mock.onRequestHandler = OnRequestHandler(httpBodyType: [String: Either<String, [String: String]>].self) { request, object in
XCTAssertEqual(url, mock.request.url)
XCTAssertEqual(request.method, .post)
XCTAssertNotNil(object)
let stateRequest = StateDeviceRequest(variables: StateDeviceRequest.Variables(uid: id))
XCTAssertNotNil(object!["query"])
object!["query"]!.map(firstTypeTransform: {
XCTAssertEqual($0, stateRequest.query)
}, secondTypeTransform: {
XCTFail("Require 'String' type but receive \($0)")
})
XCTAssertNotNil(object!["operationName"])
object!["operationName"]!.map(firstTypeTransform: {
XCTAssertEqual($0, stateRequest.operationName)
}, secondTypeTransform: {
XCTFail("Require 'String' type but receive \($0)")
})
XCTAssertNotNil(object!["variables"])
object!["variables"]!.map(firstTypeTransform: {
XCTFail("Require '[String: String]' type but receive \($0)")
}, secondTypeTransform: {
XCTAssertNotNil($0["uid"])
XCTAssertEqual($0["uid"], stateRequest.variables.uid)
})
requestExpectation.fulfill()
}
mock.completion = {
completionExpectation.fulfill()
}
mock.register()
_ = try? await service.stateDevice(id: id)
wait(for: [requestExpectation, completionExpectation], timeout: 2.0)
}
// MARK: - CheckDevice
}
@@ -0,0 +1,28 @@
//
// DeviceServiceData.swift
//
//
// Created by Juraldinio on 13.01.2023.
//
import Foundation
final class DeviceServiceData {
static let createDeviceTrusted = try! Data(contentsOf: Bundle.module.url(forResource: "CreateDeviceSuccessTrusted",
withExtension: "json")!)
static let createDeviceDecodeFailed = try! Data(contentsOf: Bundle.module.url(forResource: "CreateDeviceSuccessNull",
withExtension: "json")!)
static let queryDeviceStateNull = try! Data(contentsOf: Bundle.module.url(forResource: "QueryDeviceSuccessNull",
withExtension: "json")!)
static let queryDeviceStateUntrusted = try! Data(contentsOf: Bundle.module.url(forResource: "QueryDeviceSuccessUntrusted",
withExtension: "json")!)
static let queryDeviceStateTrusted = try! Data(contentsOf: Bundle.module.url(forResource: "QueryDeviceSuccessTrusted",
withExtension: "json")!)
static let nodeActionsExample = try! Data(contentsOf: Bundle.module.url(forResource: "v1historyTestmalinka1",
withExtension: "json")!)
}
@@ -0,0 +1,44 @@
//
// NetworkEnvironmentImpl.swift
//
//
// Created by Juraldinio on 13.01.2023.
//
import Foundation
import WalletNetwork
struct NetworkEnvironmentImpl: NetworkEnvironment {
let configuration: URLSessionConfiguration?
let usernames: NetworkStaticAPIRecord
let backend: NetworkStaticAPIRecord
let hyperion: NetworkDynamicAPIRecord
let node: NetworkDynamicAPIRecord
static func createStubUsernames(configuration: URLSessionConfiguration,
url: URL,
headers: NetworkStaticAPIRecord.Headers? = nil) -> NetworkEnvironment {
let record = NetworkStaticAPIRecord(url: url, headers: headers)
let dumpUrl = URL(string: "Empty")!
return NetworkEnvironmentImpl(configuration: configuration,
usernames: record,
backend: NetworkStaticAPIRecord(url: dumpUrl, headers: nil),
hyperion: NetworkDynamicAPIRecord(url: { dumpUrl }, headers: nil),
node: NetworkDynamicAPIRecord(url: { dumpUrl }, headers: nil))
}
static func createStubNodes(configuration: URLSessionConfiguration,
url: URL,
headers: NetworkStaticAPIRecord.Headers? = nil) -> NetworkEnvironment {
let record = NetworkDynamicAPIRecord(url: { url }, headers: headers)
let dumpUrl = URL(string: "Empty")!
return NetworkEnvironmentImpl(configuration: configuration,
usernames: NetworkStaticAPIRecord(url: dumpUrl, headers: nil),
backend: NetworkStaticAPIRecord(url: dumpUrl, headers: nil),
hyperion: NetworkDynamicAPIRecord(url: { dumpUrl }, headers: nil),
node: record)
}
}
@@ -0,0 +1,120 @@
//
// NodeActionModelsTests.swift
//
//
// Created by Nut.Tech on 13.01.2023.
//
import WalletNetwork
import Foundation
import XCTest
final class NodeActionModelsTests: XCTestCase {
func testNodeActAuthorization() {
let data = """
{
"actor": "testmalinka1",
"permission": "owner"
}
""".data(using: .utf8)!
let nodeAct = try? JSONDecoder().decode(NodeActAuthorization.self, from: data)
XCTAssertNotNil(nodeAct, "Error parsing NodeActAuthorization object")
XCTAssertEqual(nodeAct?.actor, "testmalinka1", "Error parsing NodeActAuthorization.actor value")
XCTAssertEqual(nodeAct?.permission, "owner", "Error parsing NodeActAuthorization.owner value")
}
func testNodeData() {
let data = """
{
"from": "testmalinka1",
"memo": "buyram:testmalinka1",
"quantity": "0.1000 EOS",
"to": "malinkawallt"
}
""".data(using: .utf8)!
let nodeData = try? JSONDecoder().decode(NodeData.self, from: data)
XCTAssertNotNil(nodeData, "Error parsing NodeData object")
guard let parsedData = nodeData?.data else {
XCTFail("Error parsing data value")
return
}
XCTAssertEqual(parsedData["from"] as? String, "testmalinka1", "Error parsing data value")
XCTAssertEqual(parsedData["memo"] as? String, "buyram:testmalinka1", "Error parsing data value")
XCTAssertEqual(parsedData["quantity"] as? String, "0.1000 EOS", "Error parsing data value")
XCTAssertEqual(parsedData["to"] as? String, "malinkawallt", "Error parsing data value")
}
func testNodeAct() {
let data = """
{
"account": "eosio.token",
"authorization": [
{
"actor": "testmalinka1",
"permission": "owner"
}
],
"data": {
"from": "testmalinka1",
"memo": "buyram:testmalinka1",
"quantity": "0.1000 EOS",
"to": "malinkawallt"
},
"hex_data": "100c9c2e1a99b1ca906334dcc0e9a291e80300000000000004454f53000000001362757972616d3a746573746d616c696e6b6131",
"name": "transfer"
}
""".data(using: .utf8)!
let nodeAct = try? JSONDecoder().decode(NodeAct.self, from: data)
XCTAssertNotNil(nodeAct, "Error parsing NodeAct object")
XCTAssertNotNil(nodeAct?.data, "Error parsing NodeAct.data object")
XCTAssertNotNil(nodeAct?.authorization, "buyram:testmalinka1", file: "Error parsing NodeAct.authorization objects")
XCTAssertEqual(nodeAct?.authorization.count, 1, "Error parsing NodeAct.authorization objects")
XCTAssertEqual(nodeAct?.name, "transfer", "Error parsing NodeAct.name value")
XCTAssertEqual(nodeAct?.account, "eosio.token", "Error parsing NodeAct.account value")
}
func testNodeActionTrace() {
guard let url = Bundle.module.url(forResource: "NodeActionTrace", withExtension: "json"),
let data = try? Data(contentsOf: url),
let result = try? JSONDecoder().decode(NodeActionTrace.self, from: data)
else {
XCTFail("Error reading JSON file")
return
}
XCTAssertNotNil(result.act, "Error parsing NodeActionTrace.act object")
XCTAssertEqual(result.blockNum, 288713481, "Error parsing NodeActionTrace.blockNum value")
XCTAssertEqual(result.blockTime, "2023-01-12T15:27:01.500", "Error parsing NodeActionTrace.blockTime value")
XCTAssertEqual(result.receiver, "malinkawallt", "Error parsing NodeActionTrace.receiver value")
XCTAssertEqual(result.trxId, "233cb97fcc9a6c8cc7943e021cd9705dd5ff7b82b1fa3a93afaf240f35f2c31c", "Error parsing NodeActionTrace.trxId value")
}
func testNodeAction() {
guard let url = Bundle.module.url(forResource: "NodeAction", withExtension: "json"),
let data = try? Data(contentsOf: url),
let result = try? JSONDecoder().decode(NodeAction.self, from: data)
else {
XCTFail("Error reading JSON file")
return
}
XCTAssertNotNil(result.actionTrace, "Error parsing NodeAction.actionTrace value")
XCTAssertEqual(result.accountActionSeq, 2858, "Error parsing NodeAction.accountActionSeq value")
XCTAssertEqual(result.globalActionSeq, 357585823156, "Error parsing NodeAction.globalActionSeq value")
XCTAssertTrue(result.irreversible, "Error parsing NodeAction.irreversible value")
}
func testNodeResponse() {
guard let url = Bundle.module.url(forResource: "NodeResponse", withExtension: "json"),
let data = try? Data(contentsOf: url),
let result = try? JSONDecoder().decode(NodeResponse.self, from: data)
else {
XCTFail("Error reading JSON file")
return
}
XCTAssertNotNil(result.actions, "Error parsing NodeResponse")
XCTAssertEqual(result.actions.count, 1, "Error parsing NodeResponse.actions")
}
}
@@ -0,0 +1,68 @@
//
// NodeServiceTests.swift
//
//
// Created by Nut.Tech on 16.01.2023.
//
import XCTest
@testable import WalletNetwork
import Mocker
final class NodeServiceTests: XCTestCase {
override func tearDown() {
Mocker.removeAll()
}
func testFetchNodeActions() async {
let accountName = "testmalinka3"
let offset = -100
let url = URL(string: "https://eos.greymass.com/v1/history/get_actions?account_name=\(accountName)&offset=\(offset)")!
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let env = NetworkEnvironmentImpl.createStubNodes(configuration: configuration, url: url)
let service = NodeService(environment: env)
Mock(url: url,
ignoreQuery: true,
dataType: .json,
statusCode: 200,
data: [ .get: DeviceServiceData.nodeActionsExample ]
).register()
do {
let state = try await service.fetchActions(account: accountName,
limit: offset)
guard let firstAction = state.first else {
XCTFail("Error parsing NodeActions")
return
}
XCTAssertEqual(state.count, 100, "Incorrect number of node actions")
XCTAssertEqual(firstAction.accountActionSeq, 2759, "Error parsing NodeAction.accountActionSeq")
XCTAssertEqual(firstAction.globalActionSeq, 357563493447, "Error parsing NodeAction.globalActionSeq")
XCTAssertTrue(firstAction.irreversible, "Error parsing NodeAction.irreversible")
XCTAssertNotNil(firstAction.actionTrace, "Error parsing NodeAction.actionTrace")
XCTAssertEqual(firstAction.actionTrace.blockTime, "2023-01-09T13:55:06.000", "Error parsing NodeAction.actionTrace.blockTime")
XCTAssertEqual(firstAction.actionTrace.blockNum, 288184258, "Error parsing NodeAction.actionTrace.blockNum")
XCTAssertEqual(firstAction.actionTrace.trxId, "a4dfd44c82cf0a3be1930bf6e4ec6ea51c1b331a4e77864a0d2cae4d06e8683d", "Error parsing NodeAction.actionTrace.trxId")
XCTAssertEqual(firstAction.actionTrace.receiver, "testmalinka1", "Error parsing NodeAction.actionTrace.receiver")
let act = firstAction.actionTrace.act
XCTAssertEqual(act.account, "eosio.token", "Error parsing NodeActionTrace.act.account")
XCTAssertEqual(act.name, "transfer", "Error parsing NodeActionTrace.act.name")
XCTAssertEqual(act.authorization.count, 1, "Error parsing NodeActionTrace.act.authorization")
XCTAssertNotNil(act.data, "Error parsing NodeActionTrace.act.data")
guard let authorization = act.authorization.first else {
XCTFail("Error parsing NodeAction.actionTrace.act.authorization")
return
}
XCTAssertEqual(authorization.permission, "owner", "Error parsing NodeActionTrace.act.authorization.permission")
XCTAssertEqual(authorization.actor, "testmalinka1", "Error parsing NodeActionTrace.act.authorization.actor")
} catch {
XCTFail("Catch exceprion \(error)")
}
}
}
@@ -0,0 +1 @@
{"data": {"registerDevice": {"isTrustedNow": false, "uid": null}}}
@@ -0,0 +1 @@
{"data": {"registerDevice": {"isTrustedNow": true, "uid": "a88c4532a09024c245e9"}}}
@@ -0,0 +1,51 @@
{
"account_action_seq": 2858,
"action_trace": {
"account_ram_deltas": [],
"act": {
"account": "eosio.token",
"authorization": [
{
"actor": "testmalinka1",
"permission": "owner"
}
],
"data": {
"from": "testmalinka1",
"memo": "buyram:testmalinka1",
"quantity": "0.1000 EOS",
"to": "malinkawallt"
},
"hex_data": "100c9c2e1a99b1ca906334dcc0e9a291e80300000000000004454f53000000001362757972616d3a746573746d616c696e6b6131",
"name": "transfer"
},
"action_ordinal": 4,
"block_num": 288713481,
"block_time": "2023-01-12T15:27:01.500",
"closest_unnotified_ancestor_action_ordinal": 2,
"context_free": false,
"creator_action_ordinal": 2,
"elapsed": 2,
"producer_block_id": "11356b09099ac39c311d160b99824c40825a5b65f7ddc75fb1e4a5d4c57cde68",
"receipt": {
"abi_sequence": 4,
"act_digest": "0d8c0d9d769f6b68d2b3b2cc1dd2b219eb1ddd7d52c83d221abd74541f6f687b",
"auth_sequence": [
[
"testmalinka1",
2192
]
],
"code_sequence": 4,
"global_sequence": 357585823156,
"receiver": "malinkawallt",
"recv_sequence": 14905
},
"receiver": "malinkawallt",
"trx_id": "233cb97fcc9a6c8cc7943e021cd9705dd5ff7b82b1fa3a93afaf240f35f2c31c"
},
"block_num": 288713481,
"block_time": "2023-01-12T15:27:01.500",
"global_action_seq": 357585823156,
"irreversible": true
}
@@ -0,0 +1,44 @@
{
"account_ram_deltas": [],
"act": {
"account": "eosio.token",
"authorization": [
{
"actor": "testmalinka1",
"permission": "owner"
}
],
"data": {
"from": "testmalinka1",
"memo": "buyram:testmalinka1",
"quantity": "0.1000 EOS",
"to": "malinkawallt"
},
"hex_data": "100c9c2e1a99b1ca906334dcc0e9a291e80300000000000004454f53000000001362757972616d3a746573746d616c696e6b6131",
"name": "transfer"
},
"action_ordinal": 4,
"block_num": 288713481,
"block_time": "2023-01-12T15:27:01.500",
"closest_unnotified_ancestor_action_ordinal": 2,
"context_free": false,
"creator_action_ordinal": 2,
"elapsed": 2,
"producer_block_id": "11356b09099ac39c311d160b99824c40825a5b65f7ddc75fb1e4a5d4c57cde68",
"receipt": {
"abi_sequence": 4,
"act_digest": "0d8c0d9d769f6b68d2b3b2cc1dd2b219eb1ddd7d52c83d221abd74541f6f687b",
"auth_sequence": [
[
"testmalinka1",
2192
]
],
"code_sequence": 4,
"global_sequence": 357585823156,
"receiver": "malinkawallt",
"recv_sequence": 14905
},
"receiver": "malinkawallt",
"trx_id": "233cb97fcc9a6c8cc7943e021cd9705dd5ff7b82b1fa3a93afaf240f35f2c31c"
}
@@ -0,0 +1,57 @@
{
"actions": [
{
"account_action_seq": 2858,
"action_trace": {
"account_ram_deltas": [],
"act": {
"account": "eosio.token",
"authorization": [
{
"actor": "testmalinka1",
"permission": "owner"
}
],
"data": {
"from": "testmalinka1",
"memo": "buyram:testmalinka1",
"quantity": "0.1000 EOS",
"to": "malinkawallt"
},
"hex_data": "100c9c2e1a99b1ca906334dcc0e9a291e80300000000000004454f53000000001362757972616d3a746573746d616c696e6b6131",
"name": "transfer"
},
"action_ordinal": 4,
"block_num": 288713481,
"block_time": "2023-01-12T15:27:01.500",
"closest_unnotified_ancestor_action_ordinal": 2,
"context_free": false,
"creator_action_ordinal": 2,
"elapsed": 2,
"producer_block_id": "11356b09099ac39c311d160b99824c40825a5b65f7ddc75fb1e4a5d4c57cde68",
"receipt": {
"abi_sequence": 4,
"act_digest": "0d8c0d9d769f6b68d2b3b2cc1dd2b219eb1ddd7d52c83d221abd74541f6f687b",
"auth_sequence": [
[
"testmalinka1",
2192
]
],
"code_sequence": 4,
"global_sequence": 357585823156,
"receiver": "malinkawallt",
"recv_sequence": 14905
},
"receiver": "malinkawallt",
"trx_id": "233cb97fcc9a6c8cc7943e021cd9705dd5ff7b82b1fa3a93afaf240f35f2c31c"
},
"block_num": 288713481,
"block_time": "2023-01-12T15:27:01.500",
"global_action_seq": 357585823156,
"irreversible": true
}
],
"head_block_num": 288838483,
"last_irreversible_block": 288838158
}
@@ -0,0 +1,8 @@
{
"data": {
"device": {
"availableAccounts": null,
"isTrustedNow": null
}
}
}
@@ -0,0 +1,8 @@
{
"data": {
"device": {
"availableAccounts": 2,
"isTrustedNow": true
}
}
}
@@ -0,0 +1,8 @@
{
"data": {
"device": {
"availableAccounts": 0,
"isTrustedNow": false
}
}
}
+30
View File
@@ -0,0 +1,30 @@
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WalletUIComponents",
platforms: [.iOS(.v13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "WalletUIComponents",
targets: ["WalletUIComponents"]),
],
dependencies: [ ],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "WalletUIComponents",
dependencies: [ ],
path: "./Sources"),
.testTarget(
name: "WalletUIComponentsTests",
dependencies: ["WalletUIComponents"],
path: "Tests"//, // Test files
// resources: [.copy("TestData")] // The test data files, copy files without modifying them
)
]
)
@@ -0,0 +1,59 @@
//
// TextField.swift
// Malinka
//
// Created by NUT.Tech on 08.02.2023.
// Copyright © 2023 NUT.Tech. All rights reserved.
//
import SwiftUI
import Combine
/// Struct can be replaced to SwiftUI's native TextField after iOS 15.0.
/// It was created for access to firstResponder logic which has no analogue
/// until iOS 15.0 with @FocusState wrapper type and isFocused property of TextField.
public struct TextField: UIViewRepresentable {
@ObservedObject
public var viewModel: TextFieldViewModel
public let font: UIFont
public let textColor: UIColor
// MARK: - Init
public init(viewModel: TextFieldViewModel, font: UIFont, textColor: UIColor) {
self.viewModel = viewModel
self.font = font
self.textColor = textColor
}
// MARK: - UIViewRepresentable
public func makeCoordinator() -> UITextFieldDelegate {
TextFieldCoordinator(viewModel: self.viewModel)
}
public func makeUIView(context: Context) -> UITextField {
let view = UITextField()
view.autocapitalizationType = .none
view.autocorrectionType = .no
view.clearButtonMode = .never
view.font = font
view.textColor = self.textColor
view.keyboardType = self.viewModel.keyboardType
view.placeholder = self.viewModel.placeholder
view.addTarget(context.coordinator,
action: #selector(TextFieldCoordinator.textViewDidChange),
for: .editingChanged)
view.delegate = context.coordinator
return view
}
public func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = self.viewModel.text
self.viewModel.isFirstResponder
? uiView.becomeFirstResponder()
: uiView.resignFirstResponder()
}
}
@@ -0,0 +1,36 @@
//
// TextFieldCoordinator.swift
// Malinka
//
// Created by NUT.Tech on 14.02.2023.
// Copyright © 2023 NUT.Tech. All rights reserved.
//
import SwiftUI
final class TextFieldCoordinator: NSObject, UITextFieldDelegate {
private let viewModel: TextFieldViewModel
init(viewModel: TextFieldViewModel) {
self.viewModel = viewModel
}
@objc
func textViewDidChange(_ textField: UITextField) {
self.viewModel.text = textField.text ?? ""
}
// MARK: - UITextFieldDelegate
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
guard self.viewModel.isFirstResponder == textField.isFirstResponder else { return false }
self.viewModel.isFirstResponder = true
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
self.viewModel.isFirstResponder = false
return true
}
}
@@ -0,0 +1,51 @@
//
// TextFieldViewModel.swift
//
//
// Created by Juraldinio on 2/19/23.
//
import Foundation
import UIKit
import Combine
public final class TextFieldViewModel: ObservableObject {
@Published
public var text = ""
@Published
public var isFirstResponder = false
@Published
public var shouldClear: Bool = false
public let placeholder: String
public let keyboardType: UIKeyboardType
public var isCloseButtonVisible: Bool { !self.text.isEmpty }
private var canclellables = Set<AnyCancellable>()
// MARK: - Init
public init(placeholder: String = "", keyboardType: UIKeyboardType = .default) {
self.placeholder = placeholder
self.keyboardType = keyboardType
self.$text
.receive(on: DispatchQueue.main)
.sink { [weak self] value in
self?.shouldClear = value == ""
}
.store(in: &self.canclellables)
}
// MARK: - Methods
public func clearButtonAction() {
self.text = ""
self.isFirstResponder = false
self.shouldClear = false
}
}
@@ -0,0 +1,8 @@
//
// File.swift
//
//
// Created by Juraldinio on 2/16/23.
//
import Foundation
-72
View File
@@ -1,72 +0,0 @@
<?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>JMalinkaWallet.ipa</key>
<array>
<dict>
<key>architectures</key>
<array>
<string>arm64</string>
</array>
<key>bitcode</key>
<false/>
<key>buildNumber</key>
<string>12</string>
<key>certificate</key>
<dict>
<key>SHA1</key>
<string>35C003923C51D78DF01DBCBFF8DAC6666C09412D</string>
<key>dateExpires</key>
<string>8/4/22</string>
<key>type</key>
<string>iOS Distribution</string>
</dict>
<key>entitlements</key>
<dict>
<key>application-identifier</key>
<string>GENPCTDS3G.com.juraldinio.wallet</string>
<key>aps-environment</key>
<string>production</string>
<key>beta-reports-active</key>
<true/>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:paycashonline.test-app.link</string>
<string>applinks:paycashonline-alternate.test-app.link</string>
<string>applinks:paycashonline-alternate.app.link</string>
<string>applinks:paycashonline.app.link</string>
</array>
<key>com.apple.developer.devicecheck.appattest-environment</key>
<string>production</string>
<key>com.apple.developer.team-identifier</key>
<string>GENPCTDS3G</string>
<key>get-task-allow</key>
<false/>
</dict>
<key>name</key>
<string>JMalinkaWallet.app</string>
<key>profile</key>
<dict>
<key>UUID</key>
<string>215599e9-99cb-4fbc-832f-12be30a2bcb0</string>
<key>dateExpires</key>
<string>8/4/22</string>
<key>name</key>
<string>MalinkaWallet</string>
</dict>
<key>symbols</key>
<true/>
<key>team</key>
<dict>
<key>id</key>
<string>GENPCTDS3G</string>
<key>name</key>
<string></string>
</dict>
<key>versionNumber</key>
<string>1.0.0</string>
</dict>
</array>
</dict>
</plist>
-29
View File
@@ -1,29 +0,0 @@
<?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>generateAppStoreInformation</key>
<false/>
<key>manageAppVersionAndBuildNumber</key>
<true/>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>com.juraldinio.wallet</key>
<string>MalinkaWallet</string>
</dict>
<key>signingCertificate</key>
<string>Apple Distribution</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>GENPCTDS3G</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
### Xcode ###
.build/
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.xcuserstate
timeline.xctimeline
.swiftpm/xcode
CryptoSwift.xcframework
/Framework
.DS_Store
Carthage/Build
.idea
.vscode
+5
View File
@@ -0,0 +1,5 @@
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <758033+krzyzanowskim@users.noreply.github.com>
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <krzyzanowskim@users.noreply.github.com>
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <marcin@krzyzanowskim.com>
Marcin Krzyzanowski <marcin.krzyzanowski@gmail.com> <marcin.krzyzanowski@gmail.com>
Luis Reisewitz <reisewitz@me.com> <zweigraf@users.noreply.github.com>
@@ -0,0 +1,36 @@
--exclude .build, Carthage, DerivedData, .git, Tests/LinuxMain.swift, Tests/CryptoSwiftTests/XCTestManifests.swift, Tests/TestsPerformance/XCTestManifests.swift
--swiftversion 5.0
--allman false
--commas always
--comments indent
--elseposition same-line
--empty void
--exponentcase lowercase
--exponentgrouping disabled
--fractiongrouping disabled
--header ignore
--octalgrouping 4,8
--decimalgrouping 3,6
--binarygrouping 4,8
--hexgrouping ignore
--hexliteralcase lowercase
--ifdef indent
--indent 2
--indentcase true
--importgrouping testable-bottom
--linebreaks lf
--operatorfunc spaced
--patternlet inline
--ranges no-space
--self insert
--semicolons inline
--stripunusedargs closure-only
--trimwhitespace always
--wraparguments preserve
--wrapcollections before-first
# rules
--rules indent, braces, isEmpty, redundantBreak, blankLinesAroundMark, blankLinesAtEndOfScope, blankLinesBetweenScopes, consecutiveBlankLines, consecutiveSpaces, duplicateImports, elseOnSameLine, leadingDelimiters, redundantBreak, redundantExtensionACL, redundantFileprivate, redundantGet, redundantInit, redundantLet, redundantNilInit, redundantObjc, redundantParens, redundantPattern, redundantRawValues, redundantReturn, redundantSelf, redundantVoidReturnType, semicolons, sortedImports, spaceAroundBraces, spaceAroundBrackets, spaceAroundComments, spaceAroundGenerics, spaceAroundOperators, spaceAroundParens, spaceInsideBraces, spaceInsideBrackets, specifiers, strongOutlets, strongifiedSelf, todos, void, wrapArguments, yodaConditions, trailingSpace
+13
View File
@@ -0,0 +1,13 @@
language: generic
matrix:
include:
# Test Ubuntu Linux 14.04
- os: linux
dist: trusty
sudo: required
- os: osx
osx_image: xcode11.4
install:
- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
script:
- swift test -c release -Xswiftc -enable-testing -Xswiftc -DCI -Xswiftc -Xfrontend -Xswiftc -solver-memory-threshold -Xswiftc -Xfrontend -Xswiftc 999999999
+342
View File
@@ -0,0 +1,342 @@
1.5.1
- Resolve type name clash by renaming BigInt -> BigInteger
1.5.0
- RSA (@NathanFallet)
- Workaround for Xcode 13.3.1
1.4.3
- Fix PCBC mode.
1.4.2
- Update Xcode project to Xcode 13
- Add SHA3 support for HMAC
- Update HMAC.Variant API (deprecate current cases)
1.4.1
- Introduce ISO 10126 padding
- fix various compiler warnings
- Revert Xcode project deployment target
1.4.0
- Customize CFB segment size (cfb8, cfb128).
- Adapt Swift @inlineable for better code optimization
1.3.8
- Revert xcframework revolution. Back to build from sources. (I'm sorry)
1.3.7
- Re-release to workaround Swift Package Manager release
1.3.6
- Fix macOS binary
- Windows support
1.3.5
- Re-release binary framework due to codesign issue
1.3.4
- Distribute optimized binary (xcframework) via SPM for apple platforms
1.3.3
- Adds OCB Authenticated-Encryption Algorithm (RFC7253)
- build-framework.sh output CryptoSwift.xcframework
- Xcode 12.2 maintenance updates
- Removed CryptoSwift.playground (couldn't make it work since Xcode 12 update)
1.3.2
- Swift 5.3 update (Xcode 12)
- Bump target platform (iOS 9, macOS 10.12)
- Allow CMAC with any Cipher
- Remove CMAC key limit
1.3.1
- Fix tests
- Swift 5.2 update
- Address possible timing issue
1.3.0
- Adds ISO-78164 padding
- Performance improvements
- Swift 5.1 update
1.2.0
- Performance improvements
- Workaround Xcode test builds with Xcode 11
1.1.3
- Fix build crash: https://bugs.swift.org/browse/SR-11630
- Fixes Xcode project tests build
- SwiftFormat all the things
- Increase/fix SHA2 data length for big input by use Int64 for calculation
1.1.2
- Fix Swift 5.0 build (for real this time)
1.1.1
- Fix Swift 5.0 build
1.1.0
- Replace RandomBytesSequence with Swift.RandomNumberGenerator
- Fix CBC-MAC
- Update SPM support
- Update for Xcode 11 and Swift 5.1
- Xcode: BUILD_LIBRARY_FOR_DISTRIBUTION = YES
1.0.0
- Swift 5
- Let's
- Celebrate
- This
- Event
- With
- 1.0.0 release
- After
- 4 years
- Thank you
0.15.0
- Adds The scrypt Password-Based Key Derivation Function (https://tools.ietf.org/html/rfc7914)
- Minor improvements
0.14.0
- Fixed decryption of AES-GCM ciphertexts with custom tag length
0.13.1
- Adds AES-GCM tag length configuration.
- Fixes count check for initializing UInt64 from Data.
0.13.0
- Adds CBC-MAC authenticator.
- Adds AES-CCM operation mode.
0.12.0
- Swift 4.2 maintenance update.
0.11.0
- API: Cryptor.seek() is throwable
- Adds proper stream support for CTR encryption with Updaptable interface.
- Refactor internals for the stream cipher modes.
- Set minimum deployment target to 8.0 (again).
0.10.0
- API: BlockMode is no longer an enum. Please migrate to eg. CBC() etc...
- Adds AES-GCM support. #97 - Feature sponsored by GesundheitsCloud (http://www.gesundheitscloud.de/)
- Adds CRC32c support.
- Improves AES variant validation.
- Fixes empty password in PBKDF2.
0.9.0
- Swift 4.1 compatibility
- Added CMAC message authenticator https://tools.ietf.org/html/rfc4493
- Added AEADChaCha20Poly1305 (AEAD_CHACHA20_POLY1305) https://tools.ietf.org/html/rfc7539#section-2.8.1
0.8.3
- Fixes SHA3 padding.
- Fixes Carthage builds.
0.8.2
- Fixes SHA3 partial updates calculations.
- Makes ChaCha20 processing faster again.
0.8.1
- Adds Data(hex:) helper.
- Adds HKDF (HMAC-based Extract-and-Expand Key Derivation Function)
- Prevent ChaCha overflow error
0.8.0
- Adds SHA3 Keccak variants
- Adds String.bytes helper to convert String to array of bytes
- Improves AES performance
- Speeds up compilation times with Swift 4
- Fixes: Blowfish minimum key size is 5
- Removes Ciphers "iv" parameter (value moved to BlockMode)
- BlockMode uses associated value for IV value where apply e.g. .CBC(iv: ivbytes)
- Refactors internal hacks no longer needed with Swift 4
0.7.2
- Adds Padding enum (.pkcs5, .pkcs7, .noPadding, .zeroPadding)
- Removes Generics from the public API.
- Slightly improves SHA1, SHA2, SHA3 performance.
- Update SPM configuration for Swift 4
0.7.1
- Swift 4.0 compatibility release
0.7.0
- Swift 3.2 compatibility release
0.6.9
- Fixed padding issue where padding was not properly added in CTR mode.
- Fixed thrown error on decrypting empty string,
- Fixed CI build script.
- Added String.encryptToBase64()
0.6.8
- Speed up MD5()
- Faster Array(hex:)
- Improve AES performance
- Fix tvOS bitcode
- Fix Blowfish CFB, OFB, CTR block modes.
- Fix Blowfish for 32-bit arch.
- Fix ChaCha20 preconditions
0.6.7
- Release for Xcode 8.2
- Fix playground example
0.6.6
- Rework ChaCha20
- Fix Poly1305
0.6.5
- Significant performance improvement when processing lange amount of data.
- Degeneric functions and change Sequence -> Collection in generic constraints.
0.6.4
- More performance improvements
- Add convenient Digest.sha2(bytes:variant)
- New: Blowfish cipher
0.6.3
- Hotfix release
- Fixes bitPadding() that breaks Digests calculations, introduced in 0.6.2
0.6.2
- SHA performance improvements by using less Swift in Swift
- Fix public access to all digests classes
0.6.1
- Update tests.
- New: RandomBytesSequence urandom values on Linux.
- Throw appropriate error for AES with invalid input where padding is needed.
- Improve performance, especially to SHA-1, SHA-2, PBKDF and related.
- Set deployment targets for all platform. Fixes Carthage builds.
- New: SHA-3 implementation (request #291)
- SHA-1 conforms to Updatable protocol and may be calculated incrementally.
- SHA-2 conforms to Updatable protocol and may be calculated incrementally.
0.6.0
- Remove bridge() workaround for Linux (not needed)
- make MD5() public
- Update README
- Convenience HMAC initializer for String input
0.6.0-beta2
- SHA-2 fix #319
- HashProtocol -> Digest and refactor
- MD5 conforms to Updatable protocol and may be calculated incrementally
- Cipher protocol accepts Collection input now
0.6.0-beta1
- Swift 3 compatibility
- Multiplatform, Single-scheme Xcode Project
- Swift Package Manager fully supported (build and tests)
- Improved Linux support
- Travis configuration added
- Public interface tests added
- enum Authenticator -> protocol Authenticator
- CRC -> Checksum
- String.encrypt() returns hex string instead of Array<UInt8>
- removed String.decrypt()
- enum Hash -> struct Hash
- Convenience initializer of Array of bytes with Hex string. Array<UInt8>(hex: "0xb1b1b2b2")
- Fix reusability of ChaCha20 instance
- Replace optional initializers with throwable initializers
- Allow to set initial counter explicitly (AES block modes). RandomAccessCryptor.seek()
0.5.2
- Fix AES-CTR incremental updates. #287
- Fixed PBKDF2 tests. #295
- Fixed assertion check in PKCS7. #288
- Updatable protocol accept SequenceType in place of Array
0.5.1
- Fixed PBKDF2 not taking key length parameter into account
- Switch to Array<> in code
0.5
- Added PBKDF1 https://tools.ietf.org/html/rfc2898#section-5.1
- Added PBKDF2 https://tools.ietf.org/html/rfc2898#section-5.2
- UpdatableCryptor protocol allows incremental encryption stream of data
- CryptoSwift.playground
- Docs update
- Added reflection control to CRC-32 (Luís Silva)
- Fix AES.init() (Pascal Pfiffner)
0.4.1
- fix NoPadding()
0.4
- Padding setup is now part of cipher constructor
- Added PBKDF2 http://tools.ietf.org/html/rfc2898#section-5.2
- Add BlockCipher protocol
- Rename Cipher -> CipherProtocol
- Remove build-frameworks.sh script
- Keep sensitive data in memory with SecureBytes
- Allows direct use of HMAC and Poly1305
- README update
- Fix missing Foundation import on Linux
0.3.1
- replace deprecated Bit with new enum.
0.3
- Swift 2.2 support
- use generators for cipher block modes should reduce memory overload.
- add OFB block mode
- add PCBC block mode
- String.decryptBase64ToString to decrypt Base64 encoded strings
- broke up complicated expressions which were taking ages to compile
0.2.3
- enable bitcode setting for Debug on an Apple TV
- faster compilation times
- improve padding functions
0.2.2
- Fix ChaCha20 cipher
- Replace for(;;) with for-in
- Workaround for "NSString are not yet implicitly convertible to String" on Linux
0.2.1
- Fix linux build
- re-add umbrella header
0.2
- Rabbit cipher (RFC4503)
- Linux Swift support
- Swift Package Manager support
- tvOS support
- Add optional seed to CRC
- Add umbrella header (CryptoSwift.h)
- Fix AES in CTR mode
- Fix no padding support for CTR and CFB block modes
- Fix access to AES.Error and ChaCha20.Error
0.1.1
- Fix Cococapods package (missing Foundation integration)
0.1.0
- Major performance improvements.
- Transition from Optionals to throw error.
- Replace enum Cipher with protocol for ciphers.
- Added CRC16
- Fixed AES CFB decryption
- Drop internal "Foundation" dependency, nonetheless it is supported as usual.
0.0.16
- Critical fix for private "md5" selector issue (#135)
0.0.15
- Fix 32-bit CTR block mode
- Carthage support update
- Mark as App-Extension-Safe API
0.0.14
- hexString -> toHextString() #105
- CTR (Counter mode)
- Hex string is lowercase now
- Carthage support
- Tests update
- Swift 2.0 support - overall update

Some files were not shown because too many files have changed in this diff Show More