├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── dependabot.yml ├── .gitignore ├── .husky ├── .gitignore └── commit-msg ├── .swiftlint.yml ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── README.md ├── WIKI.md ├── commitlint.config.js ├── example.apns ├── fastlane ├── .env ├── Appfile ├── Fastfile └── README.md ├── package-lock.json ├── package.json ├── screenshots ├── 01.png ├── 02.png └── 03.png ├── swiftgen.yml ├── waosSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── waosSwift.xcscheme ├── waosSwift.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── swiftpm │ └── Package.resolved ├── waosSwift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-72.png │ │ ├── Icon-72@2x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-Small-50.png │ │ ├── Icon-Small-50@2x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon.png │ │ ├── Icon@2x.png │ │ ├── NotificationIcon@2x.png │ │ ├── NotificationIcon@3x.png │ │ ├── NotificationIcon~ipad.png │ │ ├── NotificationIcon~ipad@2x.png │ │ └── ios-marketing.png │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ ├── Contents.json │ │ ├── Default-568h@2x.png │ │ ├── Default-667h@2x.png │ │ ├── Default-Landscape-1792h@3x.png │ │ ├── Default-Landscape-2436h@3x.png │ │ ├── Default-Landscape-2688h@3x.png │ │ ├── Default-Landscape-736h@3x.png │ │ ├── Default-Portrait-1792h@3x.png │ │ ├── Default-Portrait-2436h@3x.png │ │ ├── Default-Portrait-2688h@3x.png │ │ ├── Default-Portrait-736h@3x.png │ │ ├── Default.png │ │ ├── Default@2x.png │ │ ├── Default~ipad.png │ │ ├── Default~ipad@2x.png │ │ ├── Default~ipad~landscape.png │ │ ├── Default~ipad~landscape@2x.png │ │ ├── Default~ipad~landscape~nostatusbar.png │ │ ├── Default~ipad~landscape~nostatusbar@2x.png │ │ ├── Default~ipad~nostatusbar.png │ │ └── Default~ipad~nostatusbar@2x.png │ ├── authBackground.imageset │ │ ├── 14.jpg │ │ └── Contents.json │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ ├── second.imageset │ │ ├── Contents.json │ │ └── second.pdf │ ├── waosBackground.colorset │ │ └── Contents.json │ ├── waosError.colorset │ │ └── Contents.json │ ├── waosFacebook.colorset │ │ └── Contents.json │ ├── waosInstagram.colorset │ │ └── Contents.json │ ├── waosLink.colorset │ │ └── Contents.json │ ├── waosLinkedin.colorset │ │ └── Contents.json │ ├── waosOnBackground.colorset │ │ └── Contents.json │ ├── waosOnError.colorset │ │ └── Contents.json │ ├── waosOnPrimary.colorset │ │ └── Contents.json │ ├── waosOnSecondary.colorset │ │ └── Contents.json │ ├── waosOnSurface.colorset │ │ └── Contents.json │ ├── waosPrimary.colorset │ │ └── Contents.json │ ├── waosSecondary.colorset │ │ └── Contents.json │ ├── waosSurface.colorset │ │ └── Contents.json │ └── waosTwitter.colorset │ │ └── Contents.json ├── Info.plist ├── config │ ├── default │ │ ├── development.json │ │ ├── production.json │ │ └── release.json │ ├── fonts │ │ ├── Font Awesome 5 Brands-Regular-400.otf │ │ ├── Font Awesome 5 Free-Regular-400.otf │ │ └── Font Awesome 5 Free-Solid-900.otf │ └── localizations │ │ ├── Strings.swift │ │ ├── en.lproj │ │ └── Localizable.strings │ │ └── fr.lproj │ │ └── Localizable.strings ├── lib │ ├── helpers │ │ ├── CookiePlugin.swift │ │ ├── Errors.swift │ │ ├── Extensions │ │ │ ├── ASAuthorizationControllerProxy.swift │ │ │ ├── Array+SectionModel.swift │ │ │ ├── Data.swift │ │ │ ├── Eureka.swift │ │ │ ├── KeyboardConstraints.swift │ │ │ ├── L10n.swift │ │ │ ├── String.swift │ │ │ ├── UIButton.swift │ │ │ ├── UIColor.swift │ │ │ ├── UIImage.swift │ │ │ ├── UILabel.swift │ │ │ ├── UILocalizations.swift │ │ │ ├── UINavigationController.swift │ │ │ ├── UIScrollView.swift │ │ │ ├── UISegmentedControl.swift │ │ │ ├── UITextField.swift │ │ │ └── UserDefaults.swift │ │ ├── Libs │ │ │ ├── PopupView │ │ │ │ ├── File.swift │ │ │ │ └── PopupView.swift │ │ │ ├── ReusableKit │ │ │ │ ├── ReusableKit.swift │ │ │ │ ├── UICollectionView+ReusableKit.swift │ │ │ │ ├── UICollectionView+RxReusableKit.swift │ │ │ │ ├── UITableView+ReusableKit.swift │ │ │ │ └── UITableView+RxReusableKit.swift │ │ │ └── Then │ │ │ │ └── Then.swift │ │ ├── MarkDown.swift │ │ ├── MyResult.swift │ │ ├── Rx │ │ │ ├── ASAuthorizationAppleIDButton+Rx.swift │ │ │ ├── Eureka+Valid+Rx.swift │ │ │ ├── NSViewController+Rx.swift │ │ │ ├── ObservableType+Extras.swift │ │ │ ├── SwiftSpinner+Rx.swift │ │ │ ├── UIAlertController+Rx.swift │ │ │ ├── UIImageView+Kingfisher.swift │ │ │ ├── UIScrollView+Rx.swift │ │ │ └── UIViewController+Rx.swift │ │ ├── Stubbed.swift │ │ ├── UITricks.swift │ │ ├── Url.swift │ │ ├── Users.swift │ │ └── Validations.swift │ └── services │ │ ├── Configuration.swift │ │ ├── Logger.swift │ │ ├── Networking.swift │ │ └── Preferences.swift ├── modules │ ├── app │ │ ├── AppDelegate.swift │ │ ├── AppFlow.swift │ │ ├── AppServicesProvider.swift │ │ ├── AppSteps.swift │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── fr.lproj │ │ │ └── LaunchScreen.strings │ ├── auth │ │ ├── controllers │ │ │ ├── AuthForgotController.swift │ │ │ ├── AuthSigninController.swift │ │ │ └── AuthSignupController.swift │ │ ├── flow │ │ │ └── AuthFlow.swift │ │ ├── models │ │ │ └── AuthResponses.swift │ │ ├── reactors │ │ │ ├── AuthForgotReactor.swift │ │ │ ├── AuthSigninReactor.swift │ │ │ └── AuthSignupReactor.swift │ │ └── services │ │ │ ├── AuthService.swift │ │ │ └── api │ │ │ ├── AuthApi.swift │ │ │ └── stubbed │ │ │ ├── forgot.json │ │ │ ├── oauth.json │ │ │ ├── signIn.json │ │ │ ├── signUp.json │ │ │ └── token.json │ ├── core │ │ ├── controllers │ │ │ ├── CoreCollectionViewCellController.swift │ │ │ ├── CoreController.swift │ │ │ ├── CoreFormController.swift │ │ │ └── CoreTableViewCellController.swift │ │ ├── flows │ │ │ └── CoreFlow.swift │ │ ├── models │ │ │ ├── CoreModel.swift │ │ │ └── CoreResponses.swift │ │ ├── services │ │ │ └── CoreService.swift │ │ └── ui │ │ │ ├── CoreUIButton.swift │ │ │ ├── CoreUILabel.swift │ │ │ ├── CoreUITableView.swift │ │ │ ├── CoreUITextField.swift │ │ │ └── CoreUiRefreshControl.swift │ ├── home │ │ ├── controllers │ │ │ ├── HomePageController.swift │ │ │ └── HomeTermsController.swift │ │ ├── models │ │ │ ├── PagesModel.swift │ │ │ └── PagesResponses.swift │ │ ├── reactors │ │ │ ├── HomePageReactor.swift │ │ │ └── HomeTermsReactor.swift │ │ └── services │ │ │ ├── HomeService.swift │ │ │ └── api │ │ │ ├── HomeApi.swift │ │ │ └── stubbed │ │ │ └── changelogs.json │ ├── onBoarding │ │ ├── controllers │ │ │ └── OnBoardingController.swift │ │ ├── flows │ │ │ └── OnBoardingFlow.swift │ │ └── reactors │ │ │ └── OnBoardingReactor.swift │ ├── secondController │ │ ├── controllers │ │ │ └── SecondController.swift │ │ ├── flows │ │ │ └── SecondFlow.swift │ │ └── reactors │ │ │ └── SecondReactor.swift │ ├── tasks │ │ ├── controllers │ │ │ ├── TasksCellController.swift │ │ │ ├── TasksListController.swift │ │ │ └── TasksViewController.swift │ │ ├── flows │ │ │ └── TasksFlow.swift │ │ ├── models │ │ │ ├── TasksModel.swift │ │ │ └── TasksResponses.swift │ │ ├── reactors │ │ │ ├── TasksCellReactor.swift │ │ │ ├── TasksListReactor.swift │ │ │ └── TasksViewReactor.swift │ │ └── services │ │ │ ├── TasksService.swift │ │ │ └── api │ │ │ ├── TasksApi.swift │ │ │ └── stubbed │ │ │ ├── create.json │ │ │ ├── delete.json │ │ │ ├── get.json │ │ │ ├── list.json │ │ │ └── update.json │ └── users │ │ ├── controllers │ │ ├── UserController.swift │ │ ├── UserMoreController.swift │ │ ├── UserPreferenceController.swift │ │ └── UserViewController.swift │ │ ├── flows │ │ └── UserFlow.swift │ │ ├── models │ │ ├── ComplementaryModel.swift │ │ ├── UserModel.swift │ │ └── UserPolicyModel.swift │ │ ├── reactors │ │ ├── UserMoreReactor.swift │ │ ├── UserPreferenceReactor.swift │ │ ├── UserReactor.swift │ │ └── UserViewReactor.swift │ │ └── services │ │ ├── UserService.swift │ │ └── api │ │ ├── UserApi.swift │ │ └── stubbed │ │ └── me.json ├── waosSwift.entitlements ├── waosSwiftRelease.entitlements └── waosSwiftdevelopment.entitlements ├── waosSwiftTests ├── Info.plist └── waosSwiftTests.swift └── waosSwiftUITests ├── Info.plist └── waosSwiftUITests.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: weareopensource 4 | ko_fi: weareopensource 5 | 6 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 7 | # open_collective: weareopensource 8 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | # liberapay: # Replace with a single Liberapay username 11 | # issuehunt: # Replace with a single IssueHunt username 12 | # otechie: # Replace with a single Otechie username 13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'bug(context): title ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'feat(context): title' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request 3 | 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | jobs: 9 | auto-merge: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 14 | with: 15 | target: minor 16 | github-token: ${{ secrets.WAOS }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | # =========== 3 | .DS_Store 4 | ehthumbs.db 5 | Icon? 6 | Thumbs.db 7 | 8 | # Node and related ecosystem 9 | # ========================== 10 | node_modules/ 11 | 12 | # Swift and related ecosystem 13 | # ========================== 14 | .coveralls.yml 15 | reports/ 16 | coverage 17 | *.hmap 18 | *.ipa 19 | *.dSYM.zip 20 | *.dSYM 21 | timeline.xctimeline 22 | playground.xcworkspace 23 | fastlane/report.xml 24 | fastlane/Preview.html 25 | fastlane/screenshots/**/*.png 26 | fastlane/test_output 27 | iOSInjectionProject/ 28 | 29 | # waos Swift app and assets 30 | # ====================== 31 | 32 | 33 | # Build 34 | # ============================ 35 | build/ 36 | DerivedData/ 37 | Carthage/Build 38 | Carthage/Checkouts 39 | 40 | # Ignoring waos Node's gh-pages branch for documenation 41 | _site/ 42 | 43 | # General 44 | # ======= 45 | *.log 46 | *.csv 47 | *.dat 48 | *.out 49 | *.pid 50 | *.gz 51 | *.tmp 52 | *.bak 53 | *.swp 54 | *.moved-aside 55 | *.xccheckout 56 | *.xcscmblueprint 57 | logs/ 58 | 59 | # Various editor 60 | # ============== 61 | *.pbxuser 62 | !default.pbxuser 63 | *.mode1v3 64 | !default.mode1v3 65 | *.mode2v3 66 | !default.mode2v3 67 | *.perspectivev3 68 | !default.perspectivev3 69 | xcuserdata/ 70 | *.suo 71 | *.ntvs* 72 | *.njsproj 73 | *.sln -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - waosSwift 3 | - waosSwiftTests 4 | - waosSwiftUITests -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode12 3 | xcode_workspace: waosSwift.xcworkspace 4 | xcode_scheme: waosSwift 5 | xcode_destination: platform=iOS Simulator,OS=13.2.2,name=iPhone 11 6 | before_install: 7 | - rvm use $RVM_RUBY_VERSION #slather 8 | install: bundle install --without=documentation 9 | after_success: 10 | - fastlane lint 11 | - fastlane build_and_test 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # gem "rails" 8 | gem "fastlane", ">= 2.187.0" 9 | gem "slather", ">= 2.7.2" -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## License 2 | (The MIT License) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | 'Software'), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /WIKI.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | Welcome to the Swift wiki! Here you will find various information about this repo. 4 | 5 | ## Menu 6 | 7 | #### Swift Wiki 8 | 9 | - in construction 10 | 11 | #### WAOS 12 | 13 | * [Mindset and what we would like to create](https://weareopensource.me/) 14 | * [How to start a project and maintain updates from stacks](https://blog.weareopensource.me/start-a-project-and-maintain-updates/) 15 | * [Global roadmap and ideas about stacks](https://github.com/weareopensource/weareopensource.github.io/projects/1) 16 | * [How to contribute and help us](https://blog.weareopensource.me/how-to-contribute/) 17 | 18 | # Swift WIKI 19 | 20 | - in construction 21 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /example.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps" : { 3 | "alert" : { 4 | "title" : "sarunw.com", 5 | "body" : "A weekly blog about iOS development", 6 | "link_url": "openTask:5f5d562f71cbbaf22bad3b13" 7 | }, 8 | "badge" : 1 9 | }, 10 | "Simulator Target Bundle": "me.weareopensoruce.vue.testing", 11 | } 12 | 13 | -------------------------------------------------------------------------------- /fastlane/.env: -------------------------------------------------------------------------------- 1 | # Project Info 2 | WORKSPACE=waosSwift.xcworkspace 3 | PROJECT=waosSwift.xcodeproj 4 | SCHEME=waosSwift -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:ios) 17 | 18 | platform :ios do 19 | 20 | before_all do 21 | sh 'mkdir ../reports || true' 22 | end 23 | 24 | desc "Lint the project - .swiftlint.yml" 25 | lane :lint do 26 | swiftlint( 27 | mode: :lint, 28 | output_file: './reports/swiftlint.txt', 29 | config_file: '.swiftlint.yml' 30 | ) 31 | end 32 | 33 | desc "buil and test the project - .swiftlint.yml" 34 | lane :build_and_test do 35 | # Run tests 36 | scan({ 37 | workspace: ENV["WORKSPACE"], 38 | scheme: ENV["SCHEME"], 39 | cloned_source_packages_path: "SourcePackages" 40 | clean: true, 41 | code_coverage: true, 42 | output_types: "html, junit", 43 | devices: [ 44 | "iPhone 11 (13.2)", 45 | ], 46 | }) 47 | 48 | # Generate code coverage report 49 | slather_ignore = ['ExamplePodCode/*', 'ProjectTestsGroup/*'] 50 | slather({ 51 | scheme: ENV["SCHEME"], 52 | proj: ENV["PROJECT"], 53 | workspace: ENV["WORKSPACE"], 54 | coveralls: true, 55 | ignore: slather_ignore, 56 | output_directory: "./coverage" 57 | }) 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios lint 20 | ``` 21 | fastlane ios lint 22 | ``` 23 | Lint the project - .swiftlint.yml 24 | ### ios test 25 | ``` 26 | fastlane ios test 27 | ``` 28 | test the project - .swiftlint.yml 29 | 30 | ---- 31 | 32 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 33 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 34 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@weareopensource/swift", 3 | "description": "Swift - Boilerplate Front : RxSwift, JWT (WIP)", 4 | "version": "0.1.1", 5 | "private": false, 6 | "author": "https://github.com/weareopensource/Swift/graphs/contributors", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/weareopensource/Swift.git" 11 | }, 12 | "engines": { 13 | "node": ">=9.11.2", 14 | "npm": ">=6.4.1", 15 | "yarn": ">=1.13.0" 16 | }, 17 | "scripts": { 18 | "commit": "npx cz", 19 | "release": "standard-version", 20 | "release:auto": "npx semantic-release" 21 | }, 22 | "devDependencies": { 23 | "@commitlint/cli": "^19.3.0", 24 | "@commitlint/config-conventional": "^19.2.2", 25 | "@semantic-release/changelog": "^6.0.3", 26 | "@semantic-release/git": "^10.0.1", 27 | "@weareopensource/conventional-changelog": "^1.7.0", 28 | "commitizen": "^4.3.0", 29 | "husky": "^9.0.11", 30 | "semantic-release": "^24.0.0", 31 | "standard-version": "^9.5.0" 32 | }, 33 | "release": { 34 | "branches": [ 35 | { 36 | "name": "master" 37 | } 38 | ], 39 | "ci": false, 40 | "repositoryUrl": "https://github.com/weareopensource/Swift.git", 41 | "plugins": [ 42 | "@semantic-release/commit-analyzer", 43 | "@semantic-release/release-notes-generator", 44 | "@semantic-release/changelog", 45 | [ 46 | "@semantic-release/github", 47 | { 48 | "successComment": false, 49 | "failComment": false 50 | } 51 | ], 52 | [ 53 | "@semantic-release/git", 54 | { 55 | "message": "chore(release): ${nextRelease.version} \n\n${nextRelease.notes}" 56 | } 57 | ] 58 | ] 59 | }, 60 | "config": { 61 | "commitizen": { 62 | "path": "./node_modules/@weareopensource/conventional-changelog" 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /screenshots/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/screenshots/01.png -------------------------------------------------------------------------------- /screenshots/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/screenshots/02.png -------------------------------------------------------------------------------- /screenshots/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/screenshots/03.png -------------------------------------------------------------------------------- /swiftgen.yml: -------------------------------------------------------------------------------- 1 | strings: 2 | inputs: ./waosSwift/config/localizations/en.lproj/Localizable.strings 3 | outputs: 4 | templateName: structured-swift4 5 | output: ./waosSwift/config/localizations/Strings.swift -------------------------------------------------------------------------------- /waosSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /waosSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /waosSwift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /waosSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-1792h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-1792h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-2436h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-2436h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-2688h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-2688h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-1792h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-1792h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-2436h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-2436h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-2688h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-2688h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape~nostatusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape~nostatusbar.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape~nostatusbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~landscape~nostatusbar@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~nostatusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~nostatusbar.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~nostatusbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/LaunchImage.launchimage/Default~ipad~nostatusbar@2x.png -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/authBackground.imageset/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/authBackground.imageset/14.jpg -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/authBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "14.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xF9", 13 | "alpha" : "1.000", 14 | "blue" : "0xF9", 15 | "green" : "0xF9" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0xF9", 31 | "alpha" : "1.000", 32 | "blue" : "0xF9", 33 | "green" : "0xF9" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0x1E", 49 | "alpha" : "1.000", 50 | "blue" : "0x23", 51 | "green" : "0x20" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosError.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.125", 9 | "green" : "0.000", 10 | "red" : "0.690" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.125", 27 | "green" : "0.000", 28 | "red" : "0.690" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "0.153", 45 | "green" : "0.153", 46 | "red" : "0.890" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosFacebook.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.698", 9 | "green" : "0.404", 10 | "red" : "0.259" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosInstagram.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.424", 9 | "green" : "0.188", 10 | "red" : "0.882" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosLink.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "0.478", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "0.478", 28 | "red" : "0.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "1.000", 45 | "green" : "0.478", 46 | "red" : "0.000" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosLinkedin.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xB5", 9 | "green" : "0x77", 10 | "red" : "0x00" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosOnBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.000", 13 | "alpha" : "1.000", 14 | "blue" : "0.000", 15 | "green" : "0.000" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0.000", 31 | "alpha" : "1.000", 32 | "blue" : "0.000", 33 | "green" : "0.000" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0xFF", 49 | "alpha" : "1.000", 50 | "blue" : "0xFF", 51 | "green" : "0xFF" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosOnError.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "1.000", 13 | "alpha" : "1.000", 14 | "blue" : "1.000", 15 | "green" : "1.000" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "1.000", 31 | "alpha" : "1.000", 32 | "blue" : "1.000", 33 | "green" : "1.000" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0x00", 49 | "alpha" : "1.000", 50 | "blue" : "0x00", 51 | "green" : "0x00" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosOnPrimary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xFF", 13 | "alpha" : "1.000", 14 | "blue" : "0xFF", 15 | "green" : "0xFF" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0xFF", 31 | "alpha" : "1.000", 32 | "blue" : "0xFF", 33 | "green" : "0xFF" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0xFF", 49 | "alpha" : "1.000", 50 | "blue" : "0xFF", 51 | "green" : "0xFF" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosOnSecondary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.000", 13 | "alpha" : "1.000", 14 | "blue" : "0.000", 15 | "green" : "0.000" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0.000", 31 | "alpha" : "1.000", 32 | "blue" : "0.000", 33 | "green" : "0.000" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0.000", 49 | "alpha" : "1.000", 50 | "blue" : "0.000", 51 | "green" : "0.000" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosOnSurface.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.000", 13 | "alpha" : "1.000", 14 | "blue" : "0.000", 15 | "green" : "0.000" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0.000", 31 | "alpha" : "1.000", 32 | "blue" : "0.000", 33 | "green" : "0.000" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0xFE", 49 | "alpha" : "1.000", 50 | "blue" : "0xFE", 51 | "green" : "0xFE" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosPrimary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x1A", 13 | "alpha" : "1.000", 14 | "blue" : "0x9C", 15 | "green" : "0xBC" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0x1A", 31 | "alpha" : "1.000", 32 | "blue" : "0x9C", 33 | "green" : "0xBC" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0x16", 49 | "alpha" : "1.000", 50 | "blue" : "0x85", 51 | "green" : "0xA0" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosSecondary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0x34", 13 | "alpha" : "1.000", 14 | "blue" : "0xDB", 15 | "green" : "0x98" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0x34", 31 | "alpha" : "1.000", 32 | "blue" : "0xDB", 33 | "green" : "0x98" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0x29", 49 | "alpha" : "1.000", 50 | "blue" : "0xB9", 51 | "green" : "0x80" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosSurface.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0xFF", 13 | "alpha" : "1.000", 14 | "blue" : "0xFF", 15 | "green" : "0xFF" 16 | } 17 | } 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "appearances" : [ 22 | { 23 | "appearance" : "luminosity", 24 | "value" : "light" 25 | } 26 | ], 27 | "color" : { 28 | "color-space" : "srgb", 29 | "components" : { 30 | "red" : "0xFF", 31 | "alpha" : "1.000", 32 | "blue" : "0xFF", 33 | "green" : "0xFF" 34 | } 35 | } 36 | }, 37 | { 38 | "idiom" : "universal", 39 | "appearances" : [ 40 | { 41 | "appearance" : "luminosity", 42 | "value" : "dark" 43 | } 44 | ], 45 | "color" : { 46 | "color-space" : "srgb", 47 | "components" : { 48 | "red" : "0x28", 49 | "alpha" : "1.000", 50 | "blue" : "0x2E", 51 | "green" : "0x2A" 52 | } 53 | } 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /waosSwift/Assets.xcassets/waosTwitter.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.949", 9 | "green" : "0.631", 10 | "red" : "0.114" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /waosSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | Configuration 22 | $(CONFIGURATION) 23 | LSApplicationQueriesSchemes 24 | 25 | linkedin 26 | instagram 27 | twitter 28 | fb 29 | 30 | LSRequiresIPhoneOS 31 | 32 | NSAppTransportSecurity 33 | 34 | NSAllowsArbitraryLoads 35 | 36 | 37 | NSPhotoLibraryUsageDescription 38 | This app requires access to your photos for alerts customizations and profil avatar. 39 | UIBackgroundModes 40 | 41 | remote-notification 42 | 43 | UILaunchStoryboardName 44 | LaunchScreen 45 | UIMainStoryboardFile 46 | LaunchScreen 47 | UIRequiredDeviceCapabilities 48 | 49 | armv7 50 | 51 | UIStatusBarTintParameters 52 | 53 | UINavigationBar 54 | 55 | Style 56 | UIBarStyleDefault 57 | Translucent 58 | 59 | 60 | 61 | UISupportedInterfaceOrientations 62 | 63 | UIInterfaceOrientationPortrait 64 | UIInterfaceOrientationLandscapeLeft 65 | UIInterfaceOrientationLandscapeRight 66 | 67 | UISupportedInterfaceOrientations~ipad 68 | 69 | UIInterfaceOrientationPortrait 70 | UIInterfaceOrientationPortraitUpsideDown 71 | UIInterfaceOrientationLandscapeLeft 72 | UIInterfaceOrientationLandscapeRight 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /waosSwift/config/default/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "protocol": "https", 4 | "host": "node.weareopensource.me", 5 | "port": "", 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /waosSwift/config/default/release.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "protocol": "https", 4 | "host": "node.weareopensource.me", 5 | "port": "", 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /waosSwift/config/fonts/Font Awesome 5 Brands-Regular-400.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/config/fonts/Font Awesome 5 Brands-Regular-400.otf -------------------------------------------------------------------------------- /waosSwift/config/fonts/Font Awesome 5 Free-Regular-400.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/config/fonts/Font Awesome 5 Free-Regular-400.otf -------------------------------------------------------------------------------- /waosSwift/config/fonts/Font Awesome 5 Free-Solid-900.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weareopensource/Swift/e18b06ca2be8790c99e2039f32ae4da7f25884aa/waosSwift/config/fonts/Font Awesome 5 Free-Solid-900.otf -------------------------------------------------------------------------------- /waosSwift/config/localizations/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /** 2 | * Modules 3 | */ 4 | 5 | // links 6 | "links_us" = "https://weareopensource.me/us/"; 7 | "links_support" = "https://developer.apple.com/support/"; 8 | "links_terms" = "terms"; 9 | "links_privacy" = "https://www.apple.com/legal/privacy/en-ww/"; 10 | "links_legal" = "legal"; 11 | 12 | // popup 13 | "popup_signfail" = "Wrong Password or Email."; 14 | "popup_logout" = "You have been logged out !"; 15 | 16 | // modals 17 | "modal_confirmation_message" = "are you sure ?"; 18 | "modal_confirmation_ok" = "Ok"; 19 | "modal_confirmation_remove" = "Remove"; 20 | "modal_confirmation_cancel" = "Cancel"; 21 | "modal_requirement_title" = "Validation required"; 22 | "modal_requirement_accept" = "Accept"; 23 | 24 | // onBoarding 25 | "onBoarding_title"= "Welcome !"; 26 | "onBoarding_introduction"= "this is the first page of the application, it will only be affixed once after installation. We call it onBoarding. We will not be able to recover them."; 27 | "onBoarding_validation"= "I'm in !"; 28 | 29 | // auth 30 | "authSignIn_title"= "Sign In"; 31 | "authSignUp_title"= "Sign Up"; 32 | "auth_mail"= "Email"; 33 | "auth_password"= "Password"; 34 | "auth_firstname"= "Firstname"; 35 | "auth_lastname"= "Lastname"; 36 | "auth_forgot"= "Forgot password?"; 37 | "auth_reset"= "Reset password"; 38 | 39 | // task 40 | "tasks_title"= "Tasks"; 41 | 42 | // example 43 | "second_title"= "Example"; 44 | 45 | // user 46 | "user_title"= "Profile"; 47 | 48 | "user_edit"= "Edit"; 49 | "user_edit_section_account"= "Account"; 50 | "user_edit_mail"= "Email"; 51 | "user_edit_firstname"= "Firstname"; 52 | "user_edit_lastname"= "Lastname"; 53 | "user_edit_section_profile"= "Profile"; 54 | "user_edit_bio"= "Biography"; 55 | "user_edit_section_image"= "Profile picture"; 56 | "user_edit_image"= "iPhone Gallery"; 57 | "user_edit_image_gravatar"= "Gravatar"; 58 | "user_edit_section_socialnetworks"= "Social networks"; 59 | "user_edit_socialnetworks_instagram"= "Instagram account name"; 60 | "user_edit_socialnetworks_twitter"= "Twitter account name"; 61 | "user_edit_socialnetworks_facebook"= "Facebook account name"; 62 | 63 | "user_preferences"= "Preferences"; 64 | "user_preferences_section"= "Style"; 65 | "user_preferences_background"= "Dynamique wallpaper"; 66 | 67 | "user_section_app"= "Waos"; 68 | "user_blog"= "Blog"; 69 | "user_site"= "Website"; 70 | "user_us"= "Us ?"; 71 | "user_more"= "More informations"; 72 | 73 | "user_section_socialnetworks"= "Follow us !"; 74 | 75 | "user_section_about"= "About"; 76 | "user_support"= "Support"; 77 | "user_terms_of_use"= "Terms of use"; 78 | "user_privacy_policy"= "Privacy policy"; 79 | "user_legal_notice"= "Waos legal"; 80 | 81 | "user_section_contact"= "Contact"; 82 | "user_report"= "Report a bug"; 83 | "user_contact"= "Contact us"; 84 | "user_data"= "Request my data"; 85 | 86 | "user_section_actions"= "Other"; 87 | "user_logout"= "Logout"; 88 | "user_delete"= "Delete account and data"; 89 | 90 | "user_error_mail"= "You must configure mail on your phone for this."; 91 | 92 | "user_modal_confirmation_delete_message" = "Are you sure? your account and all your data will be deleted."; 93 | "user_modal_confirmation_data_message" = "We will send you all of your data by email. Do not hesitate to write to us if necessary."; 94 | -------------------------------------------------------------------------------- /waosSwift/config/localizations/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /** 2 | * Modules 3 | */ 4 | 5 | // links 6 | "links_us" = "https://weareopensource.me/us/"; 7 | "links_support" = "https://developer.apple.com/support/"; 8 | "links_terms" = "terms"; 9 | "links_privacy" = "https://www.apple.com/legal/privacy/en-ww/"; 10 | "links_legal" = "legal"; 11 | 12 | // popup 13 | "popup_signfail" = "Mot de passe ou adresse e-mail incorrect."; 14 | "popup_logout" = "Vous avez été déconnecté !"; 15 | 16 | // modals 17 | "modal_confirmation_message" = "êtes-vous sûrs ?"; 18 | "modal_confirmation_ok" = "Ok"; 19 | "modal_confirmation_remove" = "Supprimer"; 20 | "modal_confirmation_cancel" = "Annuler"; 21 | "modal_requirement_title" = "Validation requise"; 22 | "modal_requirement_accept" = "Accepter"; 23 | 24 | // onBoarding 25 | "onBoarding_title"= "Bienvenue !"; 26 | "onBoarding_introduction"= "ceci est la première page de l'application, elle ne sera affihcée qu'une fois après l'installation. Nous appelons cela onBoarding. Nous ne pourrons pas les récupérer."; 27 | "onBoarding_validation"= "J'en suis !"; 28 | 29 | // auth 30 | "authSignIn_title"= "Connexion"; 31 | "authSignUp_title"= "Inscription"; 32 | "auth_mail"= "Email"; 33 | "auth_password"= "Mot de passe"; 34 | "auth_firstname"= "Prénom"; 35 | "auth_lastname"= "Nom"; 36 | "auth_forgot"= "Mot de passe oublié?"; 37 | "auth_reset"= "Reset du mot de passe"; 38 | 39 | // task 40 | "tasks_title"= "Tasks"; 41 | 42 | // example 43 | "second_title"= "Example"; 44 | 45 | // user 46 | "user_title"= "Profil"; 47 | 48 | "user_edit"= "Editer"; 49 | "user_edit_section_account"= "Compte"; 50 | "user_edit_mail"= "Email"; 51 | "user_edit_firstname"= "Prénom"; 52 | "user_edit_lastname"= "Nom"; 53 | "user_edit_section_profile"= "Profil"; 54 | "user_edit_bio"= "Biographie"; 55 | "user_edit_section_image"= "Photo de profil"; 56 | "user_edit_image"= "Galerie de l'iPhone"; 57 | "user_edit_image_gravatar"= "Gravatar"; 58 | "user_edit_section_socialnetworks"= "Réseaux sociaux"; 59 | "user_edit_socialnetworks_instagram"= "Nom de compte Instagram"; 60 | "user_edit_socialnetworks_twitter"= "Nom de compte Twitter"; 61 | "user_edit_socialnetworks_facebook"= "Nom de compte Facebook"; 62 | 63 | "user_preferences"= "Préférences"; 64 | "user_preferences_section"= "Style"; 65 | "user_preferences_background"= "Fond d'écran dynamique"; 66 | 67 | "user_section_app"= "Waos"; 68 | "user_blog"= "Blog"; 69 | "user_site"= "Site web"; 70 | "user_us"= "Qui sommes-nous ?"; 71 | "user_more"= "Plus d'informations"; 72 | 73 | "user_section_socialnetworks"= "Suivez-nous !"; 74 | 75 | "user_section_about"= "À propos"; 76 | "user_support"= "Aide"; 77 | "user_terms_of_use"= "Conditions d'utilisation"; 78 | "user_privacy_policy"= "Politique de confidentialité"; 79 | "user_legal_notice"= "Mentions légales"; 80 | 81 | "user_section_contact"= "Contact"; 82 | "user_report"= "Remonter un bug"; 83 | "user_contact"= "Nous contacter"; 84 | "user_data"= "Demander ma donnée"; 85 | 86 | "user_section_actions"= "Autre"; 87 | "user_logout"= "Se déconnecter"; 88 | "user_delete"= "Supprimer mon compte et mes données"; 89 | 90 | "user_error_mail"= "Vous devez configurer mail sur votre téléphone pour cela."; 91 | 92 | "user_modal_confirmation_delete_message" = "êtes-vous sûrs ? votre compte et toutes vos données seront supprimées."; 93 | "user_modal_confirmation_data_message" = "We will send you all of your data by email. Do not hesitate to write to us if necessary."; 94 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/CookiePlugin.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Moya 7 | import KeychainAccess 8 | import Kingfisher 9 | 10 | let keychain = Keychain(service: config["app"]["service"].string ?? "localhost").synchronizable(true) 11 | 12 | /** 13 | * Moya Plugin 14 | */ 15 | 16 | struct CookiePlugin: PluginType { 17 | func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { 18 | var request = request 19 | if let cookie = CookieStorager.cookie as? String { 20 | request.addValue(cookie, forHTTPHeaderField: "Cookie") 21 | } 22 | return request 23 | } 24 | 25 | func didReceive(_ result: Swift.Result, target: TargetType) { 26 | switch result { 27 | case .success(let response): 28 | guard let response = response.response else { 29 | return 30 | } 31 | _ = CookieStorager.save(httpReq: response) 32 | case .failure: 33 | return 34 | } 35 | } 36 | } 37 | 38 | struct CookieStorager { 39 | static var cookie: Any? { 40 | do { 41 | return try keychain.get("Cookie") 42 | } catch let error { 43 | print(error) 44 | return nil 45 | } 46 | } 47 | 48 | static func save(httpReq: HTTPURLResponse) -> Bool { 49 | guard let cookie = httpReq.allHeaderFields["Set-Cookie"] as? String else { 50 | return false 51 | } 52 | if(cookie.contains("TOKEN")) { 53 | do { 54 | try keychain.set(cookie, key: "Cookie") 55 | } catch let error { 56 | print(error) 57 | return false 58 | } 59 | } 60 | return false 61 | } 62 | } 63 | 64 | /** 65 | * Kingfisher Modifier 66 | */ 67 | 68 | let cookieModifier = AnyModifier { request in 69 | var request = request 70 | if let cookie = CookieStorager.cookie as? String { 71 | request.addValue(cookie, forHTTPHeaderField: "Cookie") 72 | } 73 | return request 74 | } 75 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/ASAuthorizationControllerProxy.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import AuthenticationServices 6 | import RxCocoa 7 | import RxSwift 8 | import UIKit 9 | import AuthenticationServices 10 | 11 | /** 12 | * Extension 13 | */ 14 | 15 | @available(iOS 13.0, *) 16 | extension ASAuthorizationController: HasDelegate { 17 | public typealias Delegate = ASAuthorizationControllerDelegate 18 | } 19 | 20 | @available(iOS 13.0, *) 21 | class ASAuthorizationControllerProxy: DelegateProxy, 22 | DelegateProxyType, 23 | ASAuthorizationControllerDelegate, 24 | ASAuthorizationControllerPresentationContextProviding { 25 | 26 | var presentationWindow: UIWindow = UIWindow() 27 | 28 | public init(controller: ASAuthorizationController) { 29 | super.init(parentObject: controller, delegateProxy: ASAuthorizationControllerProxy.self) 30 | } 31 | 32 | // MARK: - DelegateProxyType 33 | public static func registerKnownImplementations() { 34 | register { ASAuthorizationControllerProxy(controller: $0) } 35 | } 36 | 37 | // MARK: - Proxy Subject 38 | internal lazy var didComplete = PublishSubject() 39 | 40 | // MARK: - ASAuthorizationControllerDelegate 41 | func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { 42 | didComplete.onNext(authorization) 43 | didComplete.onCompleted() 44 | } 45 | 46 | func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { 47 | didComplete.onCompleted() 48 | } 49 | 50 | // MARK: - ASAuthorizationControllerPresentationContextProviding 51 | func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { 52 | return presentationWindow 53 | } 54 | 55 | // MARK: - Completed 56 | deinit { 57 | self.didComplete.onCompleted() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/Array+SectionModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxDataSources 7 | 8 | /** 9 | * Extension 10 | */ 11 | 12 | extension Array where Element: SectionModelType { 13 | 14 | public subscript(indexPath: IndexPath) -> Element.Item { 15 | get { 16 | return self[indexPath.section].items[indexPath.item] 17 | } 18 | mutating set { 19 | self.update(section: indexPath.section) { items in 20 | items[indexPath.item] = newValue 21 | } 22 | } 23 | } 24 | 25 | public mutating func insert(_ newElement: Element.Item, at indexPath: IndexPath) { 26 | self.update(section: indexPath.section) { items in 27 | items.insert(newElement, at: indexPath.item) 28 | } 29 | } 30 | 31 | @discardableResult 32 | public mutating func remove(at indexPath: IndexPath) -> Element.Item { 33 | return self.update(section: indexPath.section) { items in 34 | return items.remove(at: indexPath.item) 35 | } 36 | } 37 | 38 | private mutating func replace(section: Int, items: [Element.Item]) { 39 | self[section] = Element.init(original: self[section], items: items) 40 | } 41 | 42 | private mutating func update(section: Int, mutate: (inout [Element.Item]) -> T) -> T { 43 | var items = self[section].items 44 | let value = mutate(&items) 45 | self[section] = Element.init(original: self[section], items: items) 46 | return value 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/Data.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Extension 9 | */ 10 | 11 | extension Data { 12 | private static let mimeTypeSignatures: [UInt8: String] = [ 13 | 0xFF: "image/jpeg", 14 | 0x89: "image/png", 15 | 0x47: "image/gif", 16 | 0x49: "image/tiff", 17 | 0x4D: "image/tiff", 18 | 0x25: "application/pdf", 19 | 0xD0: "application/vnd", 20 | 0x46: "text/plain" 21 | ] 22 | 23 | var mimeType: String { 24 | var c: UInt8 = 0 25 | copyBytes(to: &c, count: 1) 26 | return Data.mimeTypeSignatures[c] ?? "application/octet-stream" 27 | } 28 | 29 | var imgExtension: String { 30 | var c: UInt8 = 0 31 | copyBytes(to: &c, count: 1) 32 | return String(Data.mimeTypeSignatures[c]?.split(separator: "/")[1] ?? "unknown") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/Eureka.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Eureka 7 | import FontAwesome 8 | 9 | /** 10 | * Extension 11 | */ 12 | 13 | extension Form { 14 | /** 15 | * @desc merge error of sections to footer of it 16 | */ 17 | public func mergeErrorToFooter() { 18 | for section in allSections { 19 | section.footer?.title = "" 20 | for row in section { 21 | if(row.validationErrors.count > 0) { 22 | section.footer?.title = "\(section.footer?.title ?? "") \(row.validationErrors.compactMap({ $0.msg }).joined(separator: ". ")). " 23 | } 24 | } 25 | section.reload() 26 | } 27 | } 28 | 29 | } 30 | 31 | extension TextRow { 32 | /** 33 | * @desc set left font awesome icon on 34 | * @param {String} icon code, 35 | * @param {FontAwesomeStyle} icon style, 36 | * @param {UIColor} icon color, 37 | * @param {Int} icon padding, 38 | * @param {Int} icon size, 39 | * @param {CGFloat} icon opacity, 40 | */ 41 | public func setFontAwesomeIcon(_ code: String = "", style: FontAwesomeStyle = .solid, color: UIColor = .lightGray, padding: Int = 5, size: Int = 22, opacity: CGFloat = 0.5) { 42 | let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size+padding*2, height: size) ) 43 | let iconView = UIImageView(frame: CGRect(x: 0, y: 0, width: size, height: size)) 44 | iconView.image = UIImage.fontAwesomeIcon(code: code, style: style, textColor: color, size: CGSize(width: 22, height: 22)) 45 | iconView.alpha = opacity 46 | outerView.addSubview(iconView) 47 | cell.textField.leftView = outerView 48 | cell.textField.leftViewMode = .always 49 | } 50 | 51 | } 52 | 53 | extension EmailRow { 54 | /** 55 | * @desc set left font awesome icon on 56 | * @param {String} icon code, 57 | * @param {FontAwesomeStyle} icon style, 58 | * @param {UIColor} icon color, 59 | * @param {Int} icon padding, 60 | * @param {Int} icon size, 61 | * @param {CGFloat} icon opacity, 62 | */ 63 | public func setFontAwesomeIcon(_ code: String = "", style: FontAwesomeStyle = .solid, color: UIColor = .lightGray, padding: Int = 5, size: Int = 22, opacity: CGFloat = 0.5) { 64 | let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size+padding*2, height: size) ) 65 | let iconView = UIImageView(frame: CGRect(x: 0, y: 0, width: size, height: size)) 66 | iconView.image = UIImage.fontAwesomeIcon(code: code, style: style, textColor: color, size: CGSize(width: 22, height: 22)) 67 | iconView.alpha = opacity 68 | outerView.addSubview(iconView) 69 | cell.textField.leftView = outerView 70 | cell.textField.leftViewMode = .always 71 | } 72 | 73 | } 74 | 75 | extension ButtonRow { 76 | /** 77 | * @desc set left font awesome icon on 78 | * @param {String} icon code, 79 | * @param {FontAwesomeStyle} icon style, 80 | * @param {UIColor} icon color, 81 | * @param {Int} icon size, 82 | * @param {CGFloat} icon opacity, 83 | */ 84 | public func setFontAwesomeIcon(_ code: String = "", style: FontAwesomeStyle = .solid, color: UIColor = .lightGray, size: Int = 22, opacity: CGFloat = 0.5) { 85 | cell.imageView?.image = UIImage.fontAwesomeIcon(code: code, style: style, textColor: color.withAlphaComponent(opacity), size: CGSize(width: size, height: size)) 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/L10n.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Extension 9 | */ 10 | 11 | extension L10n { 12 | 13 | static func get(_ table: String, _ key: String) -> String { 14 | // swiftlint:disable:next nslocalizedstring_key 15 | let format = NSLocalizedString(key, tableName: table, comment: "") 16 | return String(format: format, locale: Locale.current) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Foundation 7 | import CommonCrypto 8 | 9 | /** 10 | * extension 11 | */ 12 | 13 | extension String { 14 | var md5: String { 15 | let data = Data(self.utf8) 16 | let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in 17 | var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH)) 18 | CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash) 19 | return hash 20 | } 21 | return hash.map { String(format: "%02x", $0) }.joined() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UIButton.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * extension 9 | */ 10 | 11 | extension UIButton { 12 | /** 13 | * @desc setBackgroundColor 14 | * @param {UIColor} UIControl, 15 | * @param {UIControl.state} forState, 16 | */ 17 | func setBackgroundColor(color: UIColor, forState: UIControl.State) { 18 | self.clipsToBounds = true // add this to maintain corner radius 19 | UIGraphicsBeginImageContext(CGSize(width: 1, height: 1)) 20 | if let context = UIGraphicsGetCurrentContext() { 21 | context.setFillColor(color.cgColor) 22 | context.fill(CGRect(x: 0, y: 0, width: 1, height: 1)) 23 | let colorImage = UIGraphicsGetImageFromCurrentImageContext() 24 | UIGraphicsEndImageContext() 25 | self.setBackgroundImage(colorImage, for: forState) 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UIColor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Extension 9 | */ 10 | 11 | extension UIColor { 12 | 13 | /** 14 | * @desc lighter color 15 | * @param {CGFloat} percentage, 16 | */ 17 | func lighter(by percentage: CGFloat = 30.0) -> UIColor? { 18 | return self.adjust(by: abs(percentage) ) 19 | } 20 | 21 | /** 22 | * @desc darker color 23 | * @param {CGFloat} percentage, 24 | */ 25 | func darker(by percentage: CGFloat = 30.0) -> UIColor? { 26 | return self.adjust(by: -1 * abs(percentage) ) 27 | } 28 | 29 | /** 30 | * @desc adjust color darkness / lightness from coefficient 31 | * @param {CGFloat} percentage, 32 | */ 33 | func adjust(by percentage: CGFloat = 30.0) -> UIColor? { 34 | var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 35 | if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) { 36 | return UIColor(red: min(red + percentage/100, 1.0), 37 | green: min(green + percentage/100, 1.0), 38 | blue: min(blue + percentage/100, 1.0), 39 | alpha: alpha) 40 | } else { 41 | return nil 42 | } 43 | } 44 | 45 | func toHex(alpha: Bool = false) -> String? { 46 | guard let components = cgColor.components, components.count >= 3 else { 47 | return nil 48 | } 49 | 50 | let r = Float(components[0]) 51 | let g = Float(components[1]) 52 | let b = Float(components[2]) 53 | var a = Float(1.0) 54 | 55 | if components.count >= 4 { 56 | a = Float(components[3]) 57 | } 58 | 59 | if alpha { 60 | return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255)) 61 | } else { 62 | return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255)) 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UIImage.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * extension 9 | */ 10 | 11 | extension UIImage { 12 | /** 13 | * @desc set blur effect on UIImageView with a radius coefficient 14 | * @param {CGFloat} radius, 15 | */ 16 | func blurred(radius: CGFloat) -> UIImage { 17 | let ciContext = CIContext(options: nil) 18 | guard let cgImage = cgImage else { return self } 19 | let inputImage = CIImage(cgImage: cgImage) 20 | guard let clampFilter = CIFilter(name: "CIAffineClamp") else { return self } 21 | clampFilter.setDefaults() 22 | clampFilter.setValue(inputImage, forKey: kCIInputImageKey) 23 | guard let ciFilter = CIFilter(name: "CIGaussianBlur") else { return self } 24 | ciFilter.setValue(clampFilter.outputImage, forKey: kCIInputImageKey) 25 | ciFilter.setValue(radius, forKey: "inputRadius") 26 | guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self } 27 | guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self } 28 | return UIImage(cgImage: cgImage2) 29 | } 30 | 31 | /** 32 | * @desc lighter image 33 | * @param {CGFloat} percentage, 34 | */ 35 | func lighter(by percentage: CGFloat = 30) -> UIImage? { 36 | return self.adjust(by: abs(percentage) ) 37 | } 38 | 39 | /** 40 | * @desc darker image 41 | * @param {CGFloat} percentage, 42 | */ 43 | func darker(by percentage: CGFloat = 30) -> UIImage? { 44 | return self.adjust(by: -1 * abs(percentage) ) 45 | } 46 | 47 | /** 48 | * @desc adjust image darkness / lightness from coefficient 49 | * @param {CGFloat} percentage, 50 | */ 51 | func adjust(by percentage: CGFloat = 30) -> UIImage? { 52 | let ciContext = CIContext(options: nil) 53 | guard let cgImage = cgImage else { return self } 54 | let inputImage = CIImage(cgImage: cgImage) 55 | guard let ciFilter = CIFilter(name: "CIExposureAdjust") else { return self } 56 | ciFilter.setValue(inputImage, forKey: "inputImage") 57 | ciFilter.setValue(percentage/100, forKey: "inputEV") 58 | guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self } 59 | guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self } 60 | return UIImage(cgImage: cgImage2) 61 | } 62 | 63 | /** 64 | * @desc adjust image orientation if needed in exif 65 | */ 66 | func adjustOrientation() -> UIImage? { 67 | switch imageOrientation { 68 | case .up: 69 | return self 70 | default: 71 | UIGraphicsBeginImageContextWithOptions(size, false, scale) 72 | draw(in: CGRect(origin: .zero, size: size)) 73 | let result = UIGraphicsGetImageFromCurrentImageContext() 74 | UIGraphicsEndImageContext() 75 | return result 76 | } 77 | } 78 | 79 | /** 80 | * @desc resizeImage width max target Size 81 | */ 82 | func resizeImage(targetSize: CGSize) -> UIImage? { 83 | let size = self.size 84 | let widthRatio = targetSize.width / size.width 85 | let heightRatio = targetSize.height / size.height 86 | let newSize = widthRatio > heightRatio ? CGSize(width: size.width * heightRatio, height: size.height * heightRatio) : CGSize(width: size.width * widthRatio, height: size.height * widthRatio) 87 | let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) 88 | 89 | UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) 90 | self.draw(in: rect) 91 | let newImage = UIGraphicsGetImageFromCurrentImageContext() 92 | UIGraphicsEndImageContext() 93 | 94 | return newImage 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UILabel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * extension 9 | */ 10 | 11 | extension UILabel { 12 | /** 13 | * @desc set new spacing for uilabel, line and letters 14 | * @param {CGFloat} lineSpacing, 15 | * @param {CGFloat} letterSpacing, 16 | */ 17 | func setSpacing(lineSpacing: CGFloat = 0.0, letterSpacing: CGFloat = 1.15) { 18 | if let labelText = text, labelText.count > 0 { 19 | let paragraphStyle = NSMutableParagraphStyle() 20 | paragraphStyle.lineSpacing = lineSpacing 21 | paragraphStyle.lineHeightMultiple = lineSpacing 22 | let attributedString = NSMutableAttributedString(string: labelText) 23 | attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length)) 24 | attributedString.addAttribute(NSAttributedString.Key.kern, value: letterSpacing, range: NSRange(location: 0, length: attributedString.length - 1)) 25 | self.attributedText = attributedString 26 | } 27 | } 28 | 29 | /** 30 | * @desc set bold text in uilabel 31 | * @param {String} regualText, 32 | * @param {String} boldiText, 33 | **/ 34 | public func setBold(string: String, bold: String) { 35 | let attrs = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: font.pointSize)] 36 | let regularString = NSMutableAttributedString(string: string) 37 | let range = (string as NSString).range(of: bold) 38 | regularString.addAttributes(attrs, range: range) 39 | attributedText = regularString 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UILocalizations.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * @desc Extension of UILabel which allows to select a localization string "localizableText" from the storyboard 9 | */ 10 | extension UILabel { 11 | @IBInspectable var localizableText: String? { 12 | get { return "" } 13 | set(value) { text = NSLocalizedString(value!, comment: "") } 14 | } 15 | } 16 | 17 | /** 18 | * @desc Extension of String which allows to select a localization string "localizableText" from the storyboard 19 | */ 20 | extension String { 21 | var localized: String { 22 | get { return NSLocalizedString(self, comment: "") } 23 | } 24 | } 25 | 26 | /** 27 | * @desc Extension of UIButton which allows to select a localization string "localizableText" from the storyboard 28 | */ 29 | extension UIButton { 30 | @IBInspectable var localizedTitle: String { 31 | get { return "" } 32 | set { self.setTitle(newValue.localized, for: .normal) } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UINavigationController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * extension 9 | */ 10 | 11 | extension UINavigationController { 12 | /** 13 | * @desc clear navigation controller background 14 | */ 15 | func clear() { 16 | self.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default) 17 | self.navigationBar.shadowImage = UIImage() 18 | self.navigationBar.isTranslucent = true 19 | self.view.backgroundColor = .clear 20 | self.navigationBar.backgroundColor = .clear 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UIScrollView.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Dependencies 9 | */ 10 | 11 | extension UIScrollView { 12 | 13 | func isBottom(toleranceHeight: CGFloat) -> Bool { 14 | return contentOffset.y > contentSize.height - frame.height + contentInset.bottom - toleranceHeight 15 | } 16 | 17 | func isNeedScroll() -> Bool { 18 | return (contentSize.width > self.frame.width) || 19 | (contentSize.height > self.frame.height) 20 | } 21 | 22 | func scrollToBottom(animation: Bool) { 23 | scrollToBottom(offset: 0, animation: animation) 24 | } 25 | 26 | func scrollToBottom(offset: CGFloat, animation: Bool) { 27 | UIView.animate(withDuration: animation ? 0.25 : 0) { 28 | self.contentOffset = CGPoint(x: self.contentOffset.x, 29 | y: self.contentSize.height - self.frame.size.height + self.contentInset.bottom + offset) 30 | } 31 | } 32 | 33 | var remaining: CGPoint { 34 | let horizontal = self.contentSize.width - self.frame.width - self.contentOffset.x 35 | let vertical = self.contentSize.height - self.frame.height - self.contentOffset.y 36 | return CGPoint(x: horizontal, y: vertical) 37 | } 38 | 39 | func setCurrentPage(_ page: Int, animated: Bool) { 40 | var rect = bounds 41 | rect.origin.x = rect.width * CGFloat(page) 42 | rect.origin.y = 0 43 | scrollRectToVisible(rect, animated: animated) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UISegmentedControl.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * extension 9 | */ 10 | 11 | extension UISegmentedControl { 12 | 13 | func updateTitle(_ titles: [String?]) { 14 | removeAllSegments() 15 | for t in titles { 16 | insertSegment(withTitle: t, at: numberOfSegments, animated: true) 17 | } 18 | 19 | } 20 | 21 | } 22 | 23 | func fixBackgroundSegmentControl( _ segmentControl: UISegmentedControl) { 24 | if #available(iOS 13.0, *) { 25 | //just to be sure it is full loaded 26 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 27 | for i in 0...(segmentControl.numberOfSegments-1) { 28 | let backgroundSegmentView = segmentControl.subviews[i] 29 | //it is not enogh changing the background color. It has some kind of shadow layer 30 | backgroundSegmentView.isHidden = true 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UITextField.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import FontAwesome 7 | 8 | /** 9 | * extension 10 | */ 11 | 12 | extension UITextField { 13 | // MARK: Constants 14 | 15 | struct Metric { 16 | static let error = UIColor(named: config["theme"]["themes"]["waos"]["error"].string ?? "") 17 | } 18 | 19 | /** 20 | * @desc set left font awesome icon on 21 | * @param {String} icon code, 22 | * @param {FontAwesomeStyle} icon style, 23 | * @param {UIColor} icon color, 24 | * @param {Int} icon padding, 25 | * @param {Int} icon size, 26 | * @param {CGFloat} icon opacity, 27 | */ 28 | public func setFontAwesomeIcon(_ code: String = "", style: FontAwesomeStyle = .solid, _ color: UIColor = .gray, padding: Int = 10, size: Int = 22, opacity: CGFloat = 0.5) { 29 | let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size+padding*2, height: size) ) 30 | let iconView = UIImageView(frame: CGRect(x: padding, y: 0, width: size, height: size)) 31 | iconView.image = UIImage.fontAwesomeIcon(code: code, style: style, textColor: color, size: CGSize(width: 22, height: 22)) 32 | iconView.alpha = opacity 33 | outerView.addSubview(iconView) 34 | leftView = outerView 35 | leftViewMode = .always 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Extensions/UserDefaults.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Extension 9 | */ 10 | 11 | extension UserDefaults { 12 | 13 | subscript(key: String) -> T? { 14 | get { 15 | return value(forKey: key) as? T 16 | } 17 | set { 18 | set(newValue, forKey: key) 19 | } 20 | } 21 | 22 | subscript(key: String) -> T? { 23 | get { 24 | if let rawValue = value(forKey: key) as? T.RawValue { 25 | return T(rawValue: rawValue) 26 | } 27 | return nil 28 | } 29 | set { 30 | set(newValue?.rawValue, forKey: key) 31 | } 32 | } 33 | 34 | func contains(_ key: String) -> Bool { 35 | return UserDefaults.standard.object(forKey: key) != nil 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/ReusableKit/ReusableKit.swift: -------------------------------------------------------------------------------- 1 | #if os(iOS) 2 | import UIKit 3 | 4 | public protocol CellType: AnyObject { 5 | var reuseIdentifier: String? { get } 6 | } 7 | 8 | /// A generic class that represents reusable cells. 9 | public struct ReusableCell { 10 | public typealias Class = Cell 11 | 12 | public let `class`: Class.Type = Class.self 13 | public let identifier: String 14 | public let nib: UINib? 15 | 16 | /// Create and returns a new `ReusableCell` instance. 17 | /// 18 | /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. 19 | /// - parameter nib: A `UINib` instance. Use this when registering from xib. 20 | public init(identifier: String? = nil, nib: UINib? = nil) { 21 | self.identifier = nib?.instantiate(withOwner: nil, options: nil).lazy 22 | .compactMap { ($0 as? CellType)?.reuseIdentifier } 23 | .first ?? identifier ?? UUID().uuidString 24 | self.nib = nib 25 | } 26 | 27 | /// A convenience initializer. 28 | /// 29 | /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. 30 | /// - parameter nibName: A name of nib. 31 | public init(identifier: String? = nil, nibName: String) { 32 | let nib = UINib(nibName: nibName, bundle: nil) 33 | self.init(identifier: identifier, nib: nib) 34 | } 35 | } 36 | 37 | public protocol ViewType: AnyObject { 38 | } 39 | 40 | /// A generic class that represents reusable views. 41 | public struct ReusableView { 42 | public typealias Class = View 43 | 44 | public let `class`: Class.Type = Class.self 45 | public let identifier: String 46 | public let nib: UINib? 47 | 48 | /// Create and returns a new `ReusableView` instance. 49 | /// 50 | /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. 51 | /// - parameter nib: A `UINib` instance. Use this when registering from xib. 52 | public init(identifier: String? = nil, nib: UINib? = nil) { 53 | self.identifier = identifier ?? UUID().uuidString 54 | self.nib = nib 55 | } 56 | 57 | /// A convenience initializer. 58 | /// 59 | /// - parameter identifier: A reuse identifier. Use random UUID string if identifier is not provided. 60 | /// - parameter nibName: A name of nib. 61 | public init(identifier: String? = nil, nibName: String) { 62 | let nib = UINib(nibName: nibName, bundle: nil) 63 | self.init(identifier: identifier, nib: nib) 64 | } 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/ReusableKit/UICollectionView+ReusableKit.swift: -------------------------------------------------------------------------------- 1 | #if os(iOS) 2 | import UIKit 3 | 4 | /// An enumeration that represents UICollectionView supplementary view kind. 5 | public enum SupplementaryViewKind: String { 6 | case header, footer 7 | 8 | public init?(rawValue: String) { 9 | switch rawValue { 10 | case UICollectionView.elementKindSectionHeader: self = .header 11 | case UICollectionView.elementKindSectionFooter: self = .footer 12 | default: return nil 13 | } 14 | } 15 | 16 | public var rawValue: String { 17 | switch self { 18 | case .header: return UICollectionView.elementKindSectionHeader 19 | case .footer: return UICollectionView.elementKindSectionFooter 20 | } 21 | } 22 | } 23 | 24 | extension UICollectionViewCell: CellType { 25 | } 26 | 27 | extension UIView: ViewType { 28 | } 29 | 30 | extension UICollectionView { 31 | 32 | // MARK: Cell 33 | /// Registers a generic cell for use in creating new collection view cells. 34 | public func register(_ cell: ReusableCell) { 35 | if let nib = cell.nib { 36 | self.register(nib, forCellWithReuseIdentifier: cell.identifier) 37 | } else { 38 | self.register(Cell.self, forCellWithReuseIdentifier: cell.identifier) 39 | } 40 | } 41 | 42 | /// Returns a generic reusable cell located by its identifier. 43 | public func dequeue(_ cell: ReusableCell, for indexPath: IndexPath) -> Cell { 44 | return self.dequeueReusableCell(withReuseIdentifier: cell.identifier, for: indexPath) as! Cell 45 | } 46 | 47 | // MARK: Supplementary View 48 | /// Registers a generic view for use in creating new supplementary views for the collection view. 49 | public func register(_ view: ReusableView, kind: SupplementaryViewKind) { 50 | self.register(view, kind: kind.rawValue) 51 | } 52 | 53 | /// Registers a generic view for use in creating new supplementary views for the collection view. 54 | public func register(_ view: ReusableView, kind: String) { 55 | if let nib = view.nib { 56 | self.register(nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: view.identifier) 57 | } else { 58 | self.register(View.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: view.identifier) 59 | } 60 | } 61 | 62 | /// Returns a generic reusable supplementary view located by its identifier and kind. 63 | public func dequeue(_ view: ReusableView, kind: String, for indexPath: IndexPath) -> View { 64 | return self.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: view.identifier, for: indexPath) as! View 65 | } 66 | 67 | /// Returns a generic reusable supplementary view located by its identifier and kind. 68 | public func dequeue(_ view: ReusableView, kind: SupplementaryViewKind, for indexPath: IndexPath) -> View { 69 | return self.dequeue(view, kind: kind.rawValue, for: indexPath) 70 | } 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/ReusableKit/UICollectionView+RxReusableKit.swift: -------------------------------------------------------------------------------- 1 | import RxCocoa 2 | import RxSwift 3 | 4 | #if os(iOS) 5 | import UIKit 6 | 7 | extension Reactive where Base: UICollectionView { 8 | public func items( 9 | _ reusableCell: ReusableCell 10 | ) -> (_ source: O) 11 | -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) 12 | -> Disposable 13 | where O.Element == S { 14 | return { source in 15 | return { configureCell in 16 | return self.items(cellIdentifier: reusableCell.identifier, cellType: Cell.self)(source)(configureCell) 17 | } 18 | } 19 | } 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/ReusableKit/UITableView+ReusableKit.swift: -------------------------------------------------------------------------------- 1 | #if os(iOS) 2 | import UIKit 3 | 4 | extension UITableViewCell: CellType { 5 | } 6 | 7 | extension UITableView { 8 | 9 | // MARK: Cell 10 | /// Registers a generic cell for use in creating new table cells. 11 | public func register(_ cell: ReusableCell) { 12 | if let nib = cell.nib { 13 | self.register(nib, forCellReuseIdentifier: cell.identifier) 14 | } else { 15 | self.register(Cell.self, forCellReuseIdentifier: cell.identifier) 16 | } 17 | } 18 | 19 | /// Returns a generic reusable cell located by its identifier. 20 | public func dequeue(_ cell: ReusableCell) -> Cell? { 21 | return self.dequeueReusableCell(withIdentifier: cell.identifier) as? Cell 22 | } 23 | 24 | /// Returns a generic reusable cell located by its identifier. 25 | public func dequeue(_ cell: ReusableCell, for indexPath: IndexPath) -> Cell { 26 | return self.dequeueReusableCell(withIdentifier: cell.identifier, for: indexPath) as! Cell 27 | } 28 | 29 | // MARK: View 30 | /// Registers a generic view for use in creating new table header or footer views. 31 | public func register(_ cell: ReusableView) { 32 | if let nib = cell.nib { 33 | self.register(nib, forHeaderFooterViewReuseIdentifier: cell.identifier) 34 | } else { 35 | self.register(View.self, forHeaderFooterViewReuseIdentifier: cell.identifier) 36 | } 37 | } 38 | 39 | /// Returns a generic reusable header of footer view located by its identifier. 40 | public func dequeue(_ view: ReusableView) -> View? { 41 | return self.dequeueReusableHeaderFooterView(withIdentifier: view.identifier) as? View 42 | } 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/ReusableKit/UITableView+RxReusableKit.swift: -------------------------------------------------------------------------------- 1 | import RxCocoa 2 | import RxSwift 3 | 4 | #if os(iOS) 5 | import UIKit 6 | 7 | extension Reactive where Base: UITableView { 8 | public func items( 9 | _ reusableCell: ReusableCell 10 | ) -> (_ source: O) 11 | -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) 12 | -> Disposable 13 | where O.Element == S { 14 | return { source in 15 | return { configureCell in 16 | return self.items(cellIdentifier: reusableCell.identifier, cellType: Cell.self)(source)(configureCell) 17 | } 18 | } 19 | } 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Libs/Then/Then.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Suyeol Jeon (xoul.kr) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | import CoreGraphics 25 | #if os(iOS) || os(tvOS) 26 | import UIKit.UIGeometry 27 | #endif 28 | 29 | public protocol Then {} 30 | 31 | extension Then where Self: Any { 32 | 33 | /// Makes it available to set properties with closures just after initializing and copying the value types. 34 | /// 35 | /// let frame = CGRect().with { 36 | /// $0.origin.x = 10 37 | /// $0.size.width = 100 38 | /// } 39 | public func with(_ block: (inout Self) throws -> Void) rethrows -> Self { 40 | var copy = self 41 | try block(©) 42 | return copy 43 | } 44 | 45 | /// Makes it available to execute something with closures. 46 | /// 47 | /// UserDefaults.standard.do { 48 | /// $0.set("devxoul", forKey: "username") 49 | /// $0.set("devxoul@gmail.com", forKey: "email") 50 | /// $0.synchronize() 51 | /// } 52 | public func `do`(_ block: (Self) throws -> Void) rethrows { 53 | try block(self) 54 | } 55 | 56 | } 57 | 58 | extension Then where Self: AnyObject { 59 | 60 | /// Makes it available to set properties with closures just after initializing. 61 | /// 62 | /// let label = UILabel().then { 63 | /// $0.textAlignment = .Center 64 | /// $0.textColor = UIColor.blackColor() 65 | /// $0.text = "Hello, World!" 66 | /// } 67 | public func then(_ block: (Self) throws -> Void) rethrows -> Self { 68 | try block(self) 69 | return self 70 | } 71 | 72 | } 73 | 74 | extension NSObject: Then {} 75 | 76 | extension CGPoint: Then {} 77 | extension CGRect: Then {} 78 | extension CGSize: Then {} 79 | extension CGVector: Then {} 80 | 81 | #if os(iOS) || os(tvOS) 82 | extension UIEdgeInsets: Then {} 83 | extension UIOffset: Then {} 84 | extension UIRectEdge: Then {} 85 | #endif 86 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/MyResult.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Result 3 | */ 4 | 5 | public enum MyResult { 6 | case success(T) 7 | case error(E) 8 | } 9 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/ASAuthorizationAppleIDButton+Rx.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import AuthenticationServices 6 | import UIKit 7 | import RxSwift 8 | 9 | /** 10 | * extension 11 | */ 12 | 13 | @available(iOS 13.0, *) 14 | extension Reactive where Base: ASAuthorizationAppleIDProvider { 15 | public func login(scope: [ASAuthorization.Scope]? = nil) -> Observable { 16 | let request = base.createRequest() 17 | request.requestedScopes = scope 18 | 19 | let controller = ASAuthorizationController(authorizationRequests: [request]) 20 | 21 | let proxy = ASAuthorizationControllerProxy.proxy(for: controller) 22 | 23 | controller.presentationContextProvider = proxy 24 | controller.performRequests() 25 | 26 | return proxy.didComplete 27 | } 28 | } 29 | 30 | @available(iOS 13.0, *) 31 | extension Reactive where Base: ASAuthorizationAppleIDButton { 32 | public func loginOnTap(scope: [ASAuthorization.Scope]? = nil) -> Observable { 33 | return controlEvent(.touchUpInside) 34 | .flatMap { 35 | ASAuthorizationAppleIDProvider().rx.login(scope: scope) 36 | } 37 | } 38 | 39 | public func login(scope: [ASAuthorization.Scope]? = nil) -> Observable { 40 | return ASAuthorizationAppleIDProvider().rx.login(scope: scope) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/Eureka+Valid+Rx.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import Eureka 6 | import RxSwift 7 | import RxCocoa 8 | 9 | /** 10 | * extentsion 11 | */ 12 | 13 | extension BaseRow: ReactiveCompatible { } 14 | 15 | var extensionPropertyStorage: [String: [String: Any]] = [:] 16 | 17 | public extension Reactive where Base: BaseRow, Base: RowType { 18 | 19 | var text: ControlProperty { 20 | let source = Observable.create { [weak base] observer in 21 | if let strongBase = base { 22 | observer.onNext(strongBase.value) 23 | strongBase.onChange { row in 24 | observer.onNext(row.value) 25 | } 26 | } 27 | return Disposables.create(with: observer.onCompleted) 28 | } 29 | let bindingObserver = Binder(base) { (row, value) in 30 | row.value = value 31 | } 32 | return ControlProperty(values: source, valueSink: bindingObserver) 33 | } 34 | 35 | var tap: ControlEvent<()> { 36 | // let source = methodInvoked(#selector(Base.onCellSelection(_:))).map { _ in } 37 | let source = Observable<()>.create { [weak base] observer in 38 | if let _base = base { 39 | _base.onCellSelection({ (_, _) in 40 | observer.onNext(()) 41 | }) 42 | } 43 | return Disposables.create { 44 | observer.onCompleted() 45 | } 46 | } 47 | return ControlEvent(events: source) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/NSViewController+Rx.swift: -------------------------------------------------------------------------------- 1 | #if os(macOS) 2 | 3 | /** 4 | * Dependencies 5 | */ 6 | 7 | import AppKit 8 | import RxCocoa 9 | import RxSwift 10 | 11 | /** 12 | * extension 13 | */ 14 | 15 | public extension Reactive where Base: NSViewController { 16 | var viewDidLoad: ControlEvent { 17 | let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in } 18 | return ControlEvent(events: source) 19 | } 20 | 21 | var viewWillAppear: ControlEvent { 22 | let source = self.methodInvoked(#selector(Base.viewWillAppear)).map { _ in } 23 | return ControlEvent(events: source) 24 | } 25 | var viewDidAppear: ControlEvent { 26 | let source = self.methodInvoked(#selector(Base.viewDidAppear)).map { _ in } 27 | return ControlEvent(events: source) 28 | } 29 | 30 | var viewWillDisappear: ControlEvent { 31 | let source = self.methodInvoked(#selector(Base.viewWillDisappear)).map { _ in } 32 | return ControlEvent(events: source) 33 | } 34 | var viewDidDisappear: ControlEvent { 35 | let source = self.methodInvoked(#selector(Base.viewDidDisappear)).map { _ in } 36 | return ControlEvent(events: source) 37 | } 38 | 39 | var viewWillLayout: ControlEvent { 40 | let source = self.methodInvoked(#selector(Base.viewWillLayout)).map { _ in } 41 | return ControlEvent(events: source) 42 | } 43 | var viewDidLayout: ControlEvent { 44 | let source = self.methodInvoked(#selector(Base.viewDidLayout)).map { _ in } 45 | return ControlEvent(events: source) 46 | } 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/ObservableType+Extras.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import Foundation 6 | import RxSwift 7 | import RxCocoa 8 | 9 | /** 10 | * extension 11 | */ 12 | 13 | extension ObservableType { 14 | 15 | func ignoreAll() -> Observable { 16 | return map { _ in } 17 | } 18 | 19 | func unwrap() -> Observable where Element == Optional { 20 | return filter { $0 != nil }.map { $0! } 21 | } 22 | 23 | func execute(_ selector: @escaping (Element) -> Void) -> Observable { 24 | return flatMap { result in 25 | return Observable 26 | .just(selector(result)) 27 | .map { _ in result } 28 | .take(1) 29 | } 30 | } 31 | 32 | func count() -> Observable<(Element, Int)> { 33 | var numberOfTimesCalled = 0 34 | let result = map { _ -> Int in 35 | numberOfTimesCalled += 1 36 | return numberOfTimesCalled 37 | } 38 | 39 | return Observable.combineLatest(self, result) 40 | } 41 | 42 | func merge(with other: Observable) -> Observable { 43 | return Observable.merge(self.asObservable(), other) 44 | } 45 | 46 | } 47 | extension Observable where Element == String { 48 | 49 | func mapToURL() -> Observable { 50 | return map { URL(string: $0) } 51 | .filter { $0 != nil } 52 | .map { $0! } 53 | } 54 | } 55 | extension Observable where Element == Data { 56 | func map( _ type: D.Type) -> Observable { 57 | return map { try JSONDecoder().decode(type, from: $0) } 58 | } 59 | } 60 | 61 | extension Observable where Element == Bool { 62 | 63 | func negate() -> Observable { 64 | return map { !$0 } 65 | } 66 | 67 | } 68 | 69 | extension Observable where Element: Sequence, Element.Iterator.Element: Comparable { 70 | 71 | /** 72 | Transforms an observable of sequences into an observable of ordered arrays by using the sequence element's 73 | natural comparator. 74 | */ 75 | 76 | func sorted() -> Observable<[T]> where Element.Iterator.Element == T { 77 | return map { $0.sorted() } 78 | } 79 | 80 | func sorted(_ areInIncreasingOrder: @escaping (T, T) -> Bool) -> Observable<[T]> 81 | where Element.Iterator.Element == T { 82 | return map { $0.sorted(by: areInIncreasingOrder) } 83 | } 84 | } 85 | 86 | extension ObservableType where Element: Collection { 87 | 88 | func mapMany(_ transform: @escaping (Self.Element.Element) -> T) -> Observable<[T]> { 89 | return self.map { collection -> [T] in 90 | collection.map(transform) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/SwiftSpinner+Rx.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxSwift 7 | import ReactorKit 8 | import SwiftSpinner 9 | 10 | /** 11 | * Extension 12 | */ 13 | 14 | extension Reactive where Base: UIViewController { 15 | 16 | /// Bindable sink for `startAnimating()`, `stopAnimating()` methods. 17 | public var isAnimating: Binder { 18 | return Binder(self.base) { _, active in 19 | if active { 20 | SwiftSpinner.show(delay: Double(config["theme"]["loader"]["minimumDisplayTime"].int ?? 2000)/1000, title: [ 21 | "Swapping time and space...", 22 | "Loading the Loading message...", 23 | "E.T phone loading...", 24 | "Swapping time and space...", 25 | "Have a good day.", 26 | "May the force be with you!", 27 | "Don't panic...", 28 | "We're making you a cookie.", 29 | "Is this Windows?", 30 | "Spinning the hamster...", 31 | "Dividing by zero...", 32 | "Winter is coming...", 33 | "Pushing pixels...", 34 | "Change the world by being yourself." 35 | ].randomElement()!) 36 | } else { 37 | SwiftSpinner.hide() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/UIAlertController+Rx.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxSwift 7 | 8 | /** 9 | * Extention 10 | */ 11 | 12 | struct AlertAction { 13 | var title: String 14 | var style: UIAlertAction.Style 15 | 16 | static func action(title: String, style: UIAlertAction.Style = .default) -> AlertAction { 17 | return AlertAction(title: title, style: style) 18 | } 19 | } 20 | 21 | extension UIViewController { 22 | 23 | func showAlert(title: String?, message: String?, style: UIAlertController.Style, actions: [AlertAction], maxHeight: CGFloat? = 0) 24 | -> Observable { 25 | return Observable.create { observer in 26 | let alertController = UIAlertController(title: title, message: message, preferredStyle: style) 27 | // heigth 28 | if(maxHeight != nil && maxHeight ?? 0 > 0) { 29 | let height: NSLayoutConstraint = NSLayoutConstraint(item: alertController.view!, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: maxHeight!) 30 | alertController.view.addConstraint(height) 31 | } 32 | // actions 33 | actions.enumerated().forEach { index, action in 34 | let action = UIAlertAction(title: action.title, style: action.style) { _ in 35 | observer.onNext(index) 36 | observer.onCompleted() 37 | } 38 | alertController.addAction(action) 39 | } 40 | // present 41 | self.present(alertController, animated: true, completion: nil) 42 | return Disposables.create { alertController.dismiss(animated: true, completion: nil) } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/UIImageView+Kingfisher.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Kingfisher 7 | 8 | /** 9 | * Extension 10 | */ 11 | 12 | typealias ImageOptions = KingfisherOptionsInfo 13 | 14 | enum imageStyle { 15 | case blured 16 | case bw 17 | case bwBlured 18 | case animated 19 | } 20 | 21 | extension UIImageView { 22 | 23 | struct Metric { 24 | static let imgStylesBlured = CGFloat(config["img"]["styles"]["blured"].float ?? 10) 25 | static let imgStylesOverlayFraction = CGFloat(config["img"]["styles"]["overlayFraction"].float ?? 0.9) 26 | } 27 | 28 | @discardableResult 29 | func setImage( 30 | url: String?, 31 | placeholder: UIImage? = nil, 32 | options: ImageOptions? = nil, 33 | style: imageStyle? = nil, 34 | defaultImage: String? = nil, // should be a Bundle.main.path image png 35 | progressBlock: DownloadProgressBlock? = nil, 36 | completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? { 37 | 38 | // Custome Filters add 39 | var options = options ?? [] 40 | switch style { 41 | case .animated: 42 | options.append(.cacheSerializer(FormatIndicatedCacheSerializer.gif)) 43 | options.append(.forceRefresh) 44 | case .blured: 45 | options.append(.processor( 46 | OverlayImageProcessor(overlay: .black, fraction: Metric.imgStylesOverlayFraction) 47 | |> BlurImageProcessor(blurRadius: Metric.imgStylesBlured) 48 | )) 49 | case .bw: 50 | options.append(.processor( 51 | OverlayImageProcessor(overlay: .black, fraction: Metric.imgStylesOverlayFraction) 52 | |> BlackWhiteProcessor() 53 | )) 54 | case .bwBlured: 55 | options.append(.processor( 56 | OverlayImageProcessor(overlay: .black, fraction: Metric.imgStylesOverlayFraction) 57 | |> BlurImageProcessor(blurRadius: Metric.imgStylesBlured) 58 | |> BlackWhiteProcessor() 59 | )) 60 | default: 61 | break 62 | } 63 | 64 | // GIF will only animates in the AnimatedImageView 65 | if self is AnimatedImageView == false { 66 | options.append(.onlyLoadFirstFrame) 67 | } 68 | 69 | if(defaultImage != nil && (url == "default" || url == "default.png" || url == "default.jpg" || url == "")) { 70 | let provider = LocalFileImageDataProvider(fileURL: URL(fileURLWithPath: Bundle.main.path(forResource: defaultImage, ofType: "png") ?? "")) 71 | return self.kf.setImage( 72 | with: provider, 73 | placeholder: placeholder, 74 | options: options, 75 | progressBlock: progressBlock, 76 | completionHandler: completionHandler 77 | ) 78 | } else { 79 | // return image else 80 | return self.kf.setImage( 81 | with: URL(string: url ?? ""), 82 | placeholder: placeholder, 83 | options: options, 84 | progressBlock: progressBlock, 85 | completionHandler: completionHandler 86 | ) 87 | } 88 | 89 | } 90 | 91 | func deleteImageCache(url: String) { 92 | ImageCache.default.removeImage(forKey: url) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/UIScrollView+Rx.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | import UIKit 5 | import RxCocoa 6 | import RxSwift 7 | 8 | /** 9 | * Dependencies 10 | */ 11 | 12 | extension Reactive where Base: UIScrollView { 13 | 14 | var currentPage: Observable { 15 | return didEndDecelerating.map({ 16 | let pageWidth = self.base.frame.width 17 | let pageHorizontal = floor((self.base.contentOffset.x - pageWidth / 2) / pageWidth) + 1 18 | let pageHeight = self.base.frame.height 19 | let pageVertical = floor((self.base.contentOffset.y - pageHeight / 2) / pageHeight) + 1 20 | return Int(pageHorizontal != 0 ? pageHorizontal : pageVertical != 0 ? pageVertical : 0 ) 21 | }) 22 | } 23 | 24 | var isReachedBottom: ControlEvent { 25 | let source = self.contentOffset 26 | .filter { [weak base = self.base] _ in 27 | guard let base = base else { return false } 28 | return base.isBottom(toleranceHeight: base.frame.height / 2) 29 | } 30 | .map { _ in Void() } 31 | return ControlEvent(events: source) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Rx/UIViewController+Rx.swift: -------------------------------------------------------------------------------- 1 | #if os(iOS) || os(tvOS) 2 | 3 | /** 4 | * Dependencies 5 | */ 6 | 7 | import UIKit 8 | import RxCocoa 9 | import RxSwift 10 | 11 | /** 12 | * extension 13 | */ 14 | 15 | public extension Reactive where Base: UIViewController { 16 | var viewDidLoad: ControlEvent { 17 | let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in } 18 | return ControlEvent(events: source) 19 | } 20 | 21 | var viewWillAppear: ControlEvent { 22 | let source = self.methodInvoked(#selector(Base.viewWillAppear)).map { $0.first as? Bool ?? false } 23 | return ControlEvent(events: source) 24 | } 25 | var viewDidAppear: ControlEvent { 26 | let source = self.methodInvoked(#selector(Base.viewDidAppear)).map { $0.first as? Bool ?? false } 27 | return ControlEvent(events: source) 28 | } 29 | 30 | var viewWillDisappear: ControlEvent { 31 | let source = self.methodInvoked(#selector(Base.viewWillDisappear)).map { $0.first as? Bool ?? false } 32 | return ControlEvent(events: source) 33 | } 34 | var viewDidDisappear: ControlEvent { 35 | let source = self.methodInvoked(#selector(Base.viewDidDisappear)).map { $0.first as? Bool ?? false } 36 | return ControlEvent(events: source) 37 | } 38 | 39 | var viewWillLayoutSubviews: ControlEvent { 40 | let source = self.methodInvoked(#selector(Base.viewWillLayoutSubviews)).map { _ in } 41 | return ControlEvent(events: source) 42 | } 43 | var viewDidLayoutSubviews: ControlEvent { 44 | let source = self.methodInvoked(#selector(Base.viewDidLayoutSubviews)).map { _ in } 45 | return ControlEvent(events: source) 46 | } 47 | 48 | var willMoveToParentViewController: ControlEvent { 49 | let source = self.methodInvoked(#selector(Base.willMove)).map { $0.first as? UIViewController } 50 | return ControlEvent(events: source) 51 | } 52 | var didMoveToParentViewController: ControlEvent { 53 | let source = self.methodInvoked(#selector(Base.didMove)).map { $0.first as? UIViewController } 54 | return ControlEvent(events: source) 55 | } 56 | 57 | var didReceiveMemoryWarning: ControlEvent { 58 | let source = self.methodInvoked(#selector(Base.didReceiveMemoryWarning)).map { _ in } 59 | return ControlEvent(events: source) 60 | } 61 | 62 | /// Rx observable, triggered when the ViewController appearance state changes (true if the View is being displayed, false otherwise) 63 | var isVisible: Observable { 64 | let viewDidAppearObservable = self.base.rx.viewDidAppear.map { _ in true } 65 | let viewWillDisappearObservable = self.base.rx.viewWillDisappear.map { _ in false } 66 | return Observable.merge(viewDidAppearObservable, viewWillDisappearObservable) 67 | } 68 | 69 | /// Rx observable, triggered when the ViewController is being dismissed 70 | var isDismissing: ControlEvent { 71 | let source = self.sentMessage(#selector(Base.dismiss)).map { $0.first as? Bool ?? false } 72 | return ControlEvent(events: source) 73 | } 74 | 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Stubbed.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * functions 9 | */ 10 | 11 | /** 12 | * @desc return stubbed json response mock 13 | * @param {String} filename 14 | * @return {Data} file 15 | */ 16 | func stubbed(_ filename: String) -> Data! { 17 | @objc class TestClass: NSObject {} 18 | 19 | let bundle = Bundle(for: TestClass.self) 20 | let path = bundle.path(forResource: filename, ofType: "json") 21 | return (try? Data(contentsOf: URL(fileURLWithPath: path!))) 22 | } 23 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/UITricks.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * Functions 9 | */ 10 | 11 | /** 12 | * @desc make CircleUIButton 13 | */ 14 | class CircleUIButton: UIButton { 15 | override func layoutSubviews() { 16 | super.layoutSubviews() 17 | layer.cornerRadius = bounds.height / 2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Url.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * functions 9 | */ 10 | 11 | /** 12 | * @desc generate api url 13 | * @param {String} _protocol, 14 | * @param {String} _host, 15 | * @param {String} _port, 16 | * @param {String} _path, 17 | * @return {URL} 18 | */ 19 | func getUrl(_protocol: String, _host: String, _port: String, _path: String) -> URL! { 20 | var port = "" 21 | if(_port.count > 0) { 22 | port = ":\(_port)" 23 | } 24 | guard let url = URL(string: "\(_protocol)://\(_host)\(port)/\(_path)") else { fatalError("baseUrl could not be configured." ) } 25 | return url 26 | } 27 | 28 | /** 29 | * @desc generate an upload image url with options (from http://toto.png to http://toto-300-blur.png) 30 | * @param {String} image, 31 | * @param {[Int]} sizes, 32 | * @param {String} operation, 33 | * @return {String} 34 | */ 35 | func setUploadImageUrl(_ image: String, sizes: [Int]? = [], operation: String? = nil) -> String! { 36 | 37 | let baseUrl: String = getUrl(_protocol: config["api"]["protocol"].string ?? "http", 38 | _host: config["api"]["host"].string ?? "localhost", 39 | _port: config["api"]["port"].string ?? "3000", 40 | _path: config["api"]["endPoints"]["basePath"].string ?? "api").absoluteString 41 | 42 | let apiPathUploads = config["api"]["endPoints"]["uploads"].string ?? "uploads" 43 | 44 | var _image: String 45 | let base = image.split {$0 == "."} 46 | if(base.count != 2) { 47 | return image 48 | } else { 49 | _image = "\(base[0])" 50 | } 51 | if sizes?.count ?? 0 > 0 { 52 | if(sizes!.count == 2) { 53 | if(UIDevice.current.userInterfaceIdiom == .phone) { 54 | _image = "\(_image)-\(sizes![0])" 55 | } else { 56 | _image = "\(_image)-\(sizes![1])" 57 | } 58 | } else { 59 | _image = "\(_image)-\(sizes![0])" 60 | } 61 | } 62 | if let _operation = operation { 63 | _image = "\(_image)-\(_operation)" 64 | } 65 | _image = "\(_image).\(base[1])" 66 | return baseUrl + "/" + apiPathUploads + "/images/" + _image 67 | } 68 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Users.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * functions 9 | */ 10 | 11 | /** 12 | * @desc Calc and return the state of the token absed on CookieExpire var 13 | * @return {TokenState} 14 | */ 15 | func getTokenStatus() -> TokenState { 16 | if let result = UserDefaults.standard.value(forKey: "CookieExpire") { 17 | let expireIn = config["jwt"]["expireIn"].int ?? (7 * 24 * 60 * 60) 18 | let renewIn = config["jwt"]["renewIn"].int ?? (60 * 60) 19 | 20 | let tokenLife = Int64(expireIn * 1000) 21 | let limitToReset = Int64(renewIn * 1000) 22 | let currentTime = Int64(NSDate().timeIntervalSince1970 * 1000) 23 | let expireTime = result as! Int64 24 | let tokenTimeSpent = tokenLife-(expireTime-currentTime) 25 | 26 | if (currentTime > expireTime) { 27 | return TokenState.toDefine 28 | } else if( tokenTimeSpent > limitToReset ) { 29 | return TokenState.toRenew 30 | } else { 31 | return TokenState.isOk 32 | } 33 | } else { 34 | return TokenState.toDefine 35 | } 36 | } 37 | 38 | enum TokenState { 39 | case toDefine 40 | case isOk 41 | case toRenew 42 | } 43 | 44 | /** 45 | * @desc Calc and return the state of the terms , last version signed or not 46 | * @return {Bool} 47 | */ 48 | func getTermsStatus(terms: String?) -> Bool { 49 | let dateFormatter = DateFormatter() 50 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" 51 | 52 | // if terms undefined 53 | if (terms == nil || terms == "") { 54 | log.error("No terms updated date available, couldn't check user terms") 55 | return true 56 | } 57 | if let user = UserDefaults.standard.value(forKey: "Terms") { 58 | if let _terms = dateFormatter.date(from: terms!) { 59 | if let user = dateFormatter.date(from: user as! String) { 60 | return user > _terms 61 | } 62 | } 63 | } 64 | return false 65 | } 66 | -------------------------------------------------------------------------------- /waosSwift/lib/helpers/Validations.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Validator 7 | 8 | /** 9 | * functions 10 | */ 11 | 12 | public struct NameValidationPattern: ValidationPattern { 13 | public var pattern: String { 14 | return "[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]*" 15 | } 16 | } 17 | 18 | public struct AccountValidationPattern: ValidationPattern { 19 | public var pattern: String { 20 | return "[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð,.'-_]*" 21 | } 22 | } 23 | 24 | public struct UpperCaseValidationPattern: ValidationPattern { 25 | public var pattern: String { 26 | return "(?s)[^A-Z]*[A-Z].*" 27 | } 28 | } 29 | 30 | public struct DigitValidationPattern: ValidationPattern { 31 | public var pattern: String { 32 | return "(?s)[^0-9]*[0-9].*" 33 | } 34 | } 35 | 36 | func SpecialCharValidationCondition(_ string: String?) -> Bool { 37 | let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 38 | if string?.rangeOfCharacter(from: characterset.inverted) != nil { 39 | return true 40 | } 41 | return false 42 | } 43 | -------------------------------------------------------------------------------- /waosSwift/lib/services/Configuration.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import SwiftyJSON 7 | 8 | /** 9 | * class 10 | */ 11 | 12 | final class Config { 13 | 14 | let global: JSON = { 15 | // init development config 16 | let pathDev = Bundle.main.path(forResource: "development", ofType: "json")! 17 | let jsonStringDev = try? String(contentsOfFile: pathDev, encoding: String.Encoding.utf8) 18 | var result = JSON(parseJSON: jsonStringDev!) 19 | if let configuration = Bundle.main.infoDictionary?["Configuration"] as? String { 20 | if configuration.range(of: "development") != nil { 21 | return result 22 | } 23 | } 24 | // merge with production config if needed 25 | let pathProd = Bundle.main.path(forResource: "production", ofType: "json")! 26 | do { 27 | let jsonStringProd = try? String(contentsOfFile: pathProd, encoding: String.Encoding.utf8) 28 | try result.merge(with: JSON(parseJSON: jsonStringProd!)) 29 | result = try result.merged(with: JSON(parseJSON: jsonStringProd!)) 30 | } catch { 31 | log.error(error) 32 | } 33 | if let configuration = Bundle.main.infoDictionary?["Configuration"] as? String { 34 | if configuration.range(of: "production") != nil { 35 | return result 36 | } 37 | } 38 | 39 | // merge with release config if needed 40 | let pathRelease = Bundle.main.path(forResource: "release", ofType: "json")! 41 | do { 42 | let jsonStringRelease = try? String(contentsOfFile: pathRelease, encoding: String.Encoding.utf8) 43 | try result.merge(with: JSON(parseJSON: jsonStringRelease!)) 44 | result = try result.merged(with: JSON(parseJSON: jsonStringRelease!)) 45 | } catch { 46 | log.error(error) 47 | } 48 | return result 49 | }() 50 | } 51 | 52 | let config = Config().global 53 | -------------------------------------------------------------------------------- /waosSwift/lib/services/Networking.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import RxSwift 6 | import Moya 7 | import RxMoya 8 | import UIKit 9 | 10 | /** 11 | * class 12 | */ 13 | 14 | final class Networking: MoyaProvider { 15 | // init(plugins: [PluginType] = []) { 16 | // let configuration = URLSessionConfiguration.default 17 | // configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders 18 | // configuration.timeoutIntervalForRequest = 10 19 | // 20 | // let manager = Manager(configuration: configuration) 21 | // manager.startRequestsImmediately = false 22 | // super.init(manager: manager, plugins: plugins) 23 | // } 24 | 25 | func request( 26 | _ target: Target, 27 | file: StaticString = #file, 28 | function: StaticString = #function, 29 | line: UInt = #line 30 | ) -> Single { 31 | let requestString = "\(target.method) \(target.path)" 32 | 33 | return self.rx.request(target) 34 | .filterSuccessfulStatusCodes() 35 | .do( 36 | onSuccess: { value in 37 | let message = "🌎 success -> \(requestString) (\(value.statusCode))" 38 | log.debug(message, file: file, function: function, line: line) 39 | }, 40 | onError: { error in 41 | if let response = (error as? MoyaError)?.response { 42 | // .mapJson() 43 | if let jsonObject = try? response.mapString() { 44 | let message = "🌎 failure -> \(requestString) (\(response.statusCode)) : \(jsonObject) (\(target))" 45 | log.warning(message, file: file, function: function, line: line) 46 | } else if let rawString = String(data: response.data, encoding: .utf8) { 47 | let message = "🌎 failure -> \(requestString) (\(response.statusCode)) : \(rawString) (\(target))" 48 | log.warning(message, file: file, function: function, line: line) 49 | } else { 50 | let message = "🌎 failure -> \(requestString) (\(response.statusCode)) (\(target))" 51 | log.warning(message, file: file, function: function, line: line) 52 | } 53 | } else { 54 | let message = "🌎 failure -> \(requestString)\n\(error)" 55 | log.warning(message, file: file, function: function, line: line) 56 | } 57 | }, 58 | onSubscribed: { 59 | let message = "🌎 request -> \(requestString)" 60 | log.debug(message, file: file, function: function, line: line) 61 | } 62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /waosSwift/lib/services/Preferences.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import RxSwift 6 | import KeychainAccess 7 | import UIKit 8 | 9 | /** 10 | * Struct 11 | */ 12 | 13 | struct Status { 14 | var onBoarded: Bool 15 | var isLogged: Bool 16 | 17 | init(onBoarded: Bool, isLogged: Bool) { 18 | self.onBoarded = onBoarded 19 | self.isLogged = isLogged 20 | } 21 | } 22 | 23 | /** 24 | * protocol 25 | */ 26 | 27 | protocol PreferencesServiceType { 28 | // global 29 | var onBoarded: Bool { get set } 30 | var isLogged: Bool { get set } 31 | // status 32 | var status: Status { get } 33 | // options 34 | var isBackground: Bool { get set } 35 | } 36 | 37 | /** 38 | * class 39 | */ 40 | 41 | class PreferencesService: PreferencesServiceType { 42 | // global 43 | var onBoarded: Bool { 44 | get { 45 | return UserDefaults.standard[#function] ?? true 46 | } 47 | set { 48 | UserDefaults.standard[#function] = newValue 49 | } 50 | } 51 | var isLogged: Bool { 52 | get { 53 | return UserDefaults.standard[#function] ?? true 54 | } 55 | set { 56 | if(!newValue) { 57 | do { 58 | try keychain.remove("Cookie") 59 | } catch let error { 60 | log.error(error) 61 | } 62 | } 63 | UserDefaults.standard[#function] = newValue 64 | } 65 | } 66 | // status 67 | var status: Status { 68 | get { 69 | return Status(onBoarded: onBoarded, isLogged: isLogged) 70 | } 71 | } 72 | // options 73 | var isBackground: Bool { 74 | get { 75 | return UserDefaults.standard[#function] ?? true 76 | } 77 | set { 78 | UserDefaults.standard[#function] = newValue 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * extension 85 | */ 86 | 87 | extension PreferencesService: ReactiveCompatible {} 88 | extension Reactive where Base: PreferencesService { 89 | // global 90 | var onBoarded: Observable { 91 | return UserDefaults.standard 92 | .rx 93 | .observe(Bool.self, #function) 94 | .map { $0 ?? false } 95 | } 96 | var isLogged: Observable { 97 | return UserDefaults.standard 98 | .rx 99 | .observe(Bool.self, #function) 100 | .map { $0 ?? false } 101 | } 102 | // status 103 | var status: Observable { 104 | return Observable.combineLatest( 105 | onBoarded, isLogged, 106 | resultSelector: { onBoarded, isLogged in 107 | return Status(onBoarded: onBoarded, isLogged: isLogged) 108 | } 109 | ) 110 | } 111 | // options 112 | var isBackground: Observable { 113 | return UserDefaults.standard 114 | .rx 115 | .observe(Bool.self, #function) 116 | .map { $0 ?? true } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /waosSwift/modules/app/AppServicesProvider.swift: -------------------------------------------------------------------------------- 1 | protocol AppServicesProviderType: AnyObject { 2 | var tasksService: TasksServiceType { get } 3 | var authService: AuthServiceType { get } 4 | var userService: UserServiceType { get } 5 | var homeService: HomeServiceType { get } 6 | var preferencesService: PreferencesService { get } 7 | } 8 | 9 | final class AppServicesProvider: AppServicesProviderType { 10 | lazy var tasksService: TasksServiceType = TasksService(provider: self) 11 | lazy var authService: AuthServiceType = AuthService(provider: self) 12 | lazy var userService: UserServiceType = UserService(provider: self) 13 | lazy var homeService: HomeServiceType = HomeService(provider: self) 14 | lazy var preferencesService: PreferencesService = PreferencesService() 15 | } 16 | -------------------------------------------------------------------------------- /waosSwift/modules/app/AppSteps.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import RxFlow 6 | 7 | /** 8 | * Steps 9 | */ 10 | 11 | enum Steps: Step { 12 | case onboardingIsRequired 13 | case onboardingIsComplete 14 | 15 | case authIsRequired 16 | case authIsComplete 17 | 18 | case dashboardIsRequired 19 | 20 | case tasksIsRequired 21 | 22 | case secondIsRequired 23 | 24 | case userIsRequired 25 | } 26 | -------------------------------------------------------------------------------- /waosSwift/modules/app/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /waosSwift/modules/app/fr.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/flow/AuthFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxFlow 7 | 8 | /** 9 | * Flow 10 | */ 11 | 12 | final class AuthFlow: Flow { 13 | var root: Presentable { 14 | return self.rootViewController 15 | } 16 | 17 | private let rootViewController = UINavigationController() 18 | private let services: AppServicesProvider 19 | 20 | init(withServices services: AppServicesProvider) { 21 | self.services = services 22 | } 23 | 24 | deinit { 25 | log.info("🗑 \(type(of: self))") 26 | } 27 | 28 | func navigate(to step: Step) -> FlowContributors { 29 | guard let step = step as? Steps else { return .none } 30 | switch step { 31 | case .authIsRequired: 32 | return navigateToAuthScreen() 33 | case .authIsComplete: 34 | return .end(forwardToParentFlowWithStep: Steps.authIsComplete) 35 | default: 36 | return .none 37 | } 38 | } 39 | 40 | private func navigateToAuthScreen() -> FlowContributors { 41 | let reactor = AuthSigninReactor(provider: self.services) 42 | let viewController = AuthSignInController(reactor: reactor) 43 | self.rootViewController.pushViewController(viewController, animated: false) 44 | return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: viewController)) 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/models/AuthResponses.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Signin Response 3 | */ 4 | 5 | struct SignResponse { 6 | var user: User 7 | var tokenExpiresIn: Int 8 | } 9 | extension SignResponse: Codable { 10 | enum SignResponseCodingKeys: String, CodingKey { 11 | case user 12 | case tokenExpiresIn 13 | } 14 | } 15 | 16 | /** 17 | * Model Token Response 18 | */ 19 | 20 | struct TokenResponse { 21 | var user: User 22 | var tokenExpiresIn: Int 23 | } 24 | extension TokenResponse: Codable { 25 | enum TokenResponseCodingKeys: String, CodingKey { 26 | case user 27 | case tokenExpiresIn 28 | } 29 | } 30 | 31 | /** 32 | * Model forgot Response 33 | */ 34 | 35 | struct Forgot { 36 | var status: Bool 37 | init(status: Bool = false) { 38 | self.status = status 39 | } 40 | } 41 | 42 | extension Forgot: Hashable, Codable { 43 | enum TasksCodingKeys: String, CodingKey { 44 | case status 45 | } 46 | init(from decoder: Decoder) throws { 47 | let container = try decoder.container(keyedBy: TasksCodingKeys.self) 48 | status = try container.decode(Bool.self, forKey: .status) 49 | } 50 | } 51 | 52 | struct ForgotResponse { 53 | var message: String 54 | } 55 | extension ForgotResponse: Codable { 56 | enum TasksResponseCodingKeys: String, CodingKey { 57 | case message 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/AuthApi.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Moya 7 | 8 | /** 9 | * Api 10 | */ 11 | 12 | enum AuthApi { 13 | case signUp(firstName: String, lastName: String, email: String, password: String) 14 | case signIn(email: String, password: String) 15 | case token 16 | case forgot(email: String) 17 | case oauth(strategy: Bool, key: String, value: String, firstName: String, lastName: String, email: String) 18 | } 19 | 20 | extension AuthApi: TargetType { 21 | 22 | public var baseURL: URL { 23 | return getUrl(_protocol: config["api"]["protocol"].string ?? "http", 24 | _host: config["api"]["host"].string ?? "localhost", 25 | _port: config["api"]["port"].string ?? "3000", 26 | _path: config["api"]["endPoints"]["basePath"].string ?? "api") 27 | } 28 | 29 | var path: String { 30 | let apiPathAuth = config["api"]["endPoints"]["auth"].string ?? "auth" 31 | 32 | switch self { 33 | case .signUp : 34 | return "/" + apiPathAuth + "/signup" 35 | case .signIn : 36 | return "/" + apiPathAuth + "/signin" 37 | case .token : 38 | return "/" + apiPathAuth + "/token" 39 | case .forgot : 40 | return "/" + apiPathAuth + "/forgot" 41 | case .oauth : 42 | return "/" + apiPathAuth + "/apple/callback" 43 | } 44 | } 45 | 46 | var method: Moya.Method { 47 | switch self { 48 | case .signUp, .signIn, .forgot, .oauth: 49 | return .post 50 | case .token: 51 | return .get 52 | } 53 | } 54 | 55 | var sampleData: Data { 56 | switch self { 57 | case .signUp: return stubbed("signup") 58 | case .signIn: return stubbed("signin") 59 | case .token: return stubbed("token") 60 | case .forgot: return stubbed("forgot") 61 | case .oauth: return stubbed("oauth") 62 | } 63 | } 64 | 65 | var task: Task { 66 | switch self { 67 | case .signUp(let firstName, let lastName, let email, let password): 68 | return .requestParameters(parameters: ["firstName": firstName, "lastName": lastName, "email": email, "password": password], encoding: JSONEncoding.default) 69 | case .signIn(let email, let password): 70 | return .requestParameters(parameters: ["email": email, "password": password], encoding: JSONEncoding.default) 71 | case .token: 72 | return .requestPlain 73 | case .forgot(let email): 74 | return .requestParameters(parameters: ["email": email], encoding: JSONEncoding.default) 75 | case .oauth(let strategy, let key, let value, let firstName, let lastName, let email): 76 | return .requestParameters(parameters: ["strategy": strategy, "key": key, "value": value, "firstName": firstName, "lastName": lastName, "email": email], encoding: JSONEncoding.default) 77 | } 78 | } 79 | 80 | var headers: [String: String]? { 81 | return ["Content-Type": "application/json"] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/stubbed/forgot.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "roles": [ 4 | "user" 5 | ], 6 | "_id": "5ce3eb6236bb3afbb77dcaae", 7 | "username": "seeduser", 8 | "provider": "local", 9 | "email": "user@localhost.com", 10 | "firstName": "User", 11 | "lastName": "Local", 12 | "displayName": "User Local", 13 | "password": "$2b$10$iZbH86I1cn.U2j07QiWkoO6ZBZzymjpPOnCnbbTRqB5dQkrMmsleq", 14 | "__v": 0, 15 | "id": "5ce3eb6236bb3afbb77dcaae" 16 | }, 17 | "tokenExpiresIn": 1558603098841 18 | } 19 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/stubbed/oauth.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "roles": [ 4 | "user" 5 | ], 6 | "_id": "5ce3eb6236bb3afbb77dcaae", 7 | "username": "seeduser", 8 | "provider": "local", 9 | "email": "user@localhost.com", 10 | "firstName": "User", 11 | "lastName": "Local", 12 | "displayName": "User Local", 13 | "password": "$2b$10$iZbH86I1cn.U2j07QiWkoO6ZBZzymjpPOnCnbbTRqB5dQkrMmsleq", 14 | "__v": 0, 15 | "id": "5ce3eb6236bb3afbb77dcaae" 16 | }, 17 | "tokenExpiresIn": 1558603098841 18 | } 19 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/stubbed/signIn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "roles": [ 4 | "user" 5 | ], 6 | "_id": "5ce3eb6236bb3afbb77dcaae", 7 | "username": "seeduser", 8 | "provider": "local", 9 | "email": "user@localhost.com", 10 | "firstName": "User", 11 | "lastName": "Local", 12 | "displayName": "User Local", 13 | "password": "$2b$10$iZbH86I1cn.U2j07QiWkoO6ZBZzymjpPOnCnbbTRqB5dQkrMmsleq", 14 | "__v": 0, 15 | "id": "5ce3eb6236bb3afbb77dcaae" 16 | }, 17 | "tokenExpiresIn": 1558603098841 18 | } 19 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/stubbed/signUp.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "roles": [ 4 | "user" 5 | ], 6 | "_id": "5ce3eb6236bb3afbb77dcaae", 7 | "username": "seeduser", 8 | "provider": "local", 9 | "email": "user@localhost.com", 10 | "firstName": "User", 11 | "lastName": "Local", 12 | "displayName": "User Local", 13 | "password": "$2b$10$iZbH86I1cn.U2j07QiWkoO6ZBZzymjpPOnCnbbTRqB5dQkrMmsleq", 14 | "__v": 0, 15 | "id": "5ce3eb6236bb3afbb77dcaae" 16 | }, 17 | "tokenExpiresIn": 1558603098841 18 | } 19 | -------------------------------------------------------------------------------- /waosSwift/modules/auth/services/api/stubbed/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "roles": [ 4 | "user" 5 | ], 6 | "_id": "5ce3eb6236bb3afbb77dcaae", 7 | "username": "seeduser", 8 | "provider": "local", 9 | "email": "user@localhost.com", 10 | "firstName": "User", 11 | "lastName": "Local", 12 | "displayName": "User Local", 13 | "password": "$2b$10$iZbH86I1cn.U2j07QiWkoO6ZBZzymjpPOnCnbbTRqB5dQkrMmsleq", 14 | "__v": 0, 15 | "id": "5ce3eb6236bb3afbb77dcaae" 16 | }, 17 | "tokenExpiresIn": 1558603098841 18 | } 19 | -------------------------------------------------------------------------------- /waosSwift/modules/core/controllers/CoreCollectionViewCellController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxSwift 7 | import SwiftMessages 8 | import MessageUI 9 | 10 | /** 11 | * Controller 12 | */ 13 | 14 | class CoreCollectionViewCellController: UICollectionViewCell { 15 | 16 | // MARK: UI 17 | 18 | let error = MessageView.viewFromNib(layout: .cardView).then { 19 | $0.configureTheme(.error, iconStyle: .subtle) 20 | $0.backgroundView.backgroundColor = UIColor(named: config["theme"]["themes"]["waos"]["error"].string ?? "")?.withAlphaComponent(CGFloat(config["theme"]["popup"]["alpha"].float ?? 0.9)) 21 | $0.button?.backgroundColor = .clear 22 | $0.button?.tintColor = UIColor.white.withAlphaComponent(0.5) 23 | $0.button?.setTitle("", for: .normal) 24 | $0.button?.setImage(UIImage.fontAwesomeIcon(code: "fa-paper-plane", style: .solid, textColor: .white, size: CGSize(width: 22, height: 22)), for: .normal) 25 | } 26 | var popupConfig = SwiftMessages.defaultConfig 27 | 28 | // MARK: Initializing 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | // popup 33 | popupConfig.duration = .seconds(seconds: TimeInterval(Int(config["theme"]["popup"]["duration"].int ?? 3))) 34 | // constraints 35 | self.updateConstraintsIfNeeded() 36 | } 37 | 38 | required convenience init?(coder aDecoder: NSCoder) { 39 | self.init(frame: .zero) 40 | } 41 | 42 | func initialize() { 43 | // Override point 44 | } 45 | 46 | // MARK: Rx 47 | 48 | var disposeBag: DisposeBag = DisposeBag() 49 | 50 | // MARK: Layout Constraints 51 | 52 | private(set) var didSetupConstraints = false 53 | 54 | override func updateConstraints() { 55 | if !self.didSetupConstraints { 56 | self.setupConstraints() 57 | self.didSetupConstraints = true 58 | } 59 | super.updateConstraints() 60 | } 61 | 62 | func setupConstraints() { 63 | // Override point 64 | } 65 | } 66 | 67 | /** 68 | * Extension 69 | */ 70 | 71 | extension CoreCollectionViewCellController: MFMailComposeViewControllerDelegate { 72 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { 73 | controller.dismiss(animated: true, completion: nil) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /waosSwift/modules/core/controllers/CoreTableViewCellController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxSwift 7 | import SwiftMessages 8 | import MessageUI 9 | 10 | /** 11 | * Controller 12 | */ 13 | 14 | class CoreTableViewCellController: UITableViewCell { 15 | 16 | // MARK: UI 17 | 18 | let error = MessageView.viewFromNib(layout: .cardView).then { 19 | $0.configureTheme(.error, iconStyle: .subtle) 20 | $0.backgroundView.backgroundColor = UIColor(named: config["theme"]["themes"]["waos"]["error"].string ?? "")?.withAlphaComponent(CGFloat(config["theme"]["popup"]["alpha"].float ?? 0.9)) 21 | $0.button?.backgroundColor = .clear 22 | $0.button?.tintColor = UIColor.white.withAlphaComponent(0.5) 23 | $0.button?.setTitle("", for: .normal) 24 | $0.button?.setImage(UIImage.fontAwesomeIcon(code: "fa-paper-plane", style: .solid, textColor: .white, size: CGSize(width: 22, height: 22)), for: .normal) 25 | } 26 | var popupConfig = SwiftMessages.defaultConfig 27 | 28 | // MARK: Initializing 29 | 30 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 31 | super.init(style: style, reuseIdentifier: reuseIdentifier) 32 | self.initialize() 33 | // popup 34 | popupConfig.duration = .seconds(seconds: TimeInterval(Int(config["theme"]["popup"]["duration"].int ?? 3))) 35 | // constraints 36 | self.updateConstraintsIfNeeded() 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | func initialize() { 44 | // Override point 45 | } 46 | 47 | // MARK: Rx 48 | 49 | var disposeBag: DisposeBag = DisposeBag() 50 | 51 | // MARK: Layout Constraints 52 | 53 | private(set) var didSetupConstraints = false 54 | 55 | override func updateConstraints() { 56 | if !self.didSetupConstraints { 57 | self.setupConstraints() 58 | self.didSetupConstraints = true 59 | } 60 | super.updateConstraints() 61 | } 62 | 63 | func setupConstraints() { 64 | // Override point 65 | } 66 | 67 | } 68 | 69 | /** 70 | * Extension 71 | */ 72 | 73 | extension CoreTableViewCellController: MFMailComposeViewControllerDelegate { 74 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { 75 | controller.dismiss(animated: true, completion: nil) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /waosSwift/modules/core/flows/CoreFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxFlow 7 | 8 | /** 9 | * Flow 10 | */ 11 | 12 | final class CoreFlow: Flow { 13 | var root: Presentable { 14 | return self.rootViewController 15 | } 16 | 17 | let rootViewController = UITabBarController() 18 | private let services: AppServicesProvider 19 | 20 | init(withServices services: AppServicesProvider) { 21 | self.services = services 22 | } 23 | 24 | deinit { 25 | log.info("🗑 \(type(of: self))") 26 | } 27 | 28 | func navigate(to step: Step) -> FlowContributors { 29 | guard let step = step as? Steps else { return FlowContributors.none } 30 | 31 | switch step { 32 | case .dashboardIsRequired: 33 | return navigateToDashboard() 34 | default: 35 | return .none 36 | } 37 | } 38 | 39 | private func navigateToDashboard() -> FlowContributors { 40 | let tasksFlow = TasksFlow(withServices: self.services) 41 | let secondFlow = SecondFlow(withServices: self.services) 42 | let profilFlow = UserFlow(withServices: self.services) 43 | 44 | Flows.use([tasksFlow, secondFlow, profilFlow], when: .ready) { [unowned self] (root: [UINavigationController]) in 45 | 46 | for (index, route) in root.enumerated() { 47 | route.tabBarItem = UITabBarItem(title: L10n.get("Localizable", config["router"][index]["name"].string ?? ""), image: UIImage.fontAwesomeIcon(code: "fa-" + (config["router"][index]["meta"]["icon"].string ?? ""), style: .solid, textColor: .blue, size: CGSize(width: config["router"][index]["meta"]["width"].int ?? 0, height: config["router"][index]["meta"]["height"].int ?? 0)), selectedImage: nil) 48 | } 49 | 50 | self.rootViewController.setViewControllers(root, animated: false) 51 | } 52 | 53 | return .multiple(flowContributors: [.contribute(withNextPresentable: tasksFlow, 54 | withNextStepper: OneStepper(withSingleStep: Steps.tasksIsRequired)), 55 | .contribute(withNextPresentable: secondFlow, 56 | withNextStepper: OneStepper(withSingleStep: Steps.secondIsRequired)), 57 | .contribute(withNextPresentable: profilFlow, 58 | withNextStepper: OneStepper(withSingleStep: Steps.userIsRequired))]) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /waosSwift/modules/core/models/CoreModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Delete 3 | */ 4 | 5 | struct Delete { 6 | var id: String? 7 | var deletedCount: Int? 8 | var n: Int? 9 | var ok: Int? 10 | } 11 | 12 | extension Delete: Codable { 13 | enum DeleteCodingKeys: String, CodingKey { 14 | case id 15 | case deletedCount 16 | case n 17 | case ok 18 | } 19 | 20 | init(from decoder: Decoder) throws { 21 | let container = try decoder.container(keyedBy: DeleteCodingKeys.self) 22 | 23 | id = try container.decodeIfPresent(String.self, forKey: .id) 24 | deletedCount = try container.decodeIfPresent(Int.self, forKey: .deletedCount) 25 | n = try container.decodeIfPresent(Int.self, forKey: .n) 26 | ok = try container.decodeIfPresent(Int.self, forKey: .ok) 27 | } 28 | } 29 | 30 | /** 31 | * Model DeleteData 32 | */ 33 | 34 | struct DeleteData { 35 | var user: Delete? 36 | var tasks: Delete? 37 | var uploads: Delete? 38 | } 39 | 40 | extension DeleteData: Codable { 41 | enum DeleteDataCodingKeys: String, CodingKey { 42 | case user 43 | case tasks 44 | case uploads 45 | } 46 | 47 | init(from decoder: Decoder) throws { 48 | let container = try decoder.container(keyedBy: DeleteDataCodingKeys.self) 49 | 50 | user = try container.decodeIfPresent(Delete.self, forKey: .user) 51 | tasks = try container.decodeIfPresent(Delete.self, forKey: .tasks) 52 | uploads = try container.decodeIfPresent(Delete.self, forKey: .uploads) 53 | } 54 | } 55 | 56 | /** 57 | * Model Mail 58 | */ 59 | 60 | struct Mail { 61 | var status: Bool 62 | } 63 | 64 | extension Mail: Codable { 65 | enum MailCodingKeys: String, CodingKey { 66 | case status 67 | } 68 | 69 | init(from decoder: Decoder) throws { 70 | let container = try decoder.container(keyedBy: MailCodingKeys.self) 71 | 72 | status = try container.decode(Bool.self, forKey: .status) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /waosSwift/modules/core/models/CoreResponses.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Delete Response 3 | */ 4 | 5 | struct DeleteResponse { 6 | var data: Delete 7 | } 8 | extension DeleteResponse: Codable { 9 | enum DeleteResponseCodingKeys: String, CodingKey { 10 | case data 11 | } 12 | } 13 | 14 | /** 15 | * Model Delete Data Response 16 | */ 17 | 18 | struct DeleteDataResponse { 19 | var data: DeleteData 20 | } 21 | extension DeleteDataResponse: Codable { 22 | enum DeleteDataResponseCodingKeys: String, CodingKey { 23 | case data 24 | } 25 | } 26 | 27 | /** 28 | * Model Mail Response 29 | */ 30 | 31 | struct MailResponse { 32 | var data: Mail 33 | } 34 | extension MailResponse: Codable { 35 | enum MailResponseCodingKeys: String, CodingKey { 36 | case Mail 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /waosSwift/modules/core/services/CoreService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Service.swift 3 | // RxTodo 4 | // 5 | // Created by Suyeol Jeon on 12/01/2017. 6 | // Copyright © 2017 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | class CoreService { 10 | unowned let provider: AppServicesProviderType 11 | 12 | init(provider: AppServicesProviderType) { 13 | self.provider = provider 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /waosSwift/modules/core/ui/CoreUIButton.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * IBDesignable 9 | */ 10 | 11 | @IBDesignable class CoreUIButton: UIButton { 12 | 13 | // MARK: Constants 14 | 15 | struct Metric { 16 | static let surface = UIColor(named: config["theme"]["themes"]["waos"]["surface"].string ?? "") 17 | static let onSurface = UIColor(named: config["theme"]["themes"]["waos"]["onSurface"].string ?? "") 18 | static let radius = CGFloat(config["theme"]["global"]["radius"].int ?? 0) 19 | } 20 | 21 | // MARK: Initializing 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | shared() 26 | } 27 | required init?(coder aDecoder: NSCoder) { 28 | super.init(coder: aDecoder) 29 | shared() 30 | } 31 | 32 | override func prepareForInterfaceBuilder() { 33 | super.prepareForInterfaceBuilder() 34 | shared() 35 | } 36 | 37 | override var isHighlighted: Bool { 38 | didSet { 39 | if isHighlighted { 40 | self.highlightBtn() 41 | } else { 42 | self.clearHighlighted() 43 | } 44 | } 45 | } 46 | 47 | open override var isEnabled: Bool { 48 | didSet { 49 | alpha = isEnabled ? 1.0 : 0.4 50 | } 51 | } 52 | 53 | func highlightBtn() { 54 | if traitCollection.userInterfaceStyle == .light { 55 | self.backgroundColor = Metric.surface!.darker(by: 5) 56 | } else { 57 | self.backgroundColor = Metric.surface!.lighter(by: 5) 58 | } 59 | 60 | } 61 | 62 | func clearHighlighted() { 63 | self.backgroundColor = Metric.surface 64 | } 65 | 66 | func shared() { 67 | self.layer.cornerRadius = Metric.radius 68 | self.backgroundColor = Metric.surface 69 | self.setTitleColor(Metric.onSurface, for: .normal) 70 | 71 | // animation at first launch 72 | self.transform = CGAffineTransform.init(scaleX: 0.8, y: 0.8) 73 | UIView.animate(withDuration: 0.2, animations: { () -> Void in 74 | self.transform = CGAffineTransform.init(scaleX: 1, y: 1) 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /waosSwift/modules/core/ui/CoreUILabel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * IBDesignable 9 | */ 10 | 11 | @IBDesignable class CoreUILabel: UILabel { 12 | 13 | // MARK: Constants 14 | 15 | struct Metric { 16 | static let onSurface = UIColor(named: config["theme"]["themes"]["waos"]["onSurface"].string ?? "") 17 | } 18 | 19 | // MARK: Initializing 20 | 21 | override init(frame: CGRect) { 22 | super.init(frame: frame) 23 | shared() 24 | } 25 | required init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | shared() 28 | } 29 | 30 | override func prepareForInterfaceBuilder() { 31 | super.prepareForInterfaceBuilder() 32 | shared() 33 | } 34 | 35 | func shared() { 36 | self.textColor = Metric.onSurface 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /waosSwift/modules/core/ui/CoreUITableView.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * IBDesignable 9 | */ 10 | 11 | @IBDesignable class CoreUITableView: UITableView { 12 | 13 | // MARK: Constants 14 | 15 | struct Metric { 16 | static let background = UIColor(named: config["theme"]["themes"]["waos"]["background"].string ?? "") 17 | static let surface = UIColor(named: config["theme"]["themes"]["waos"]["surface"].string ?? "") 18 | static let tableViewRowHeight = CGFloat(config["theme"]["tableView"]["rowHeight"].int ?? 0) 19 | static let tableViewSectionHeaderHeight = CGFloat(config["theme"]["tableView"]["sectionHeaderHeight"].int ?? 0) 20 | static let tableViewSectionFooterHeight = CGFloat(config["theme"]["tableView"]["sectionFooterHeight"].int ?? 0) 21 | } 22 | 23 | // MARK: Initializing 24 | 25 | override init(frame: CGRect, style: UITableView.Style) { 26 | super.init(frame: frame, style: style) 27 | shared() 28 | } 29 | required init?(coder aDecoder: NSCoder) { 30 | super.init(coder: aDecoder) 31 | shared() 32 | } 33 | 34 | override func prepareForInterfaceBuilder() { 35 | super.prepareForInterfaceBuilder() 36 | shared() 37 | } 38 | 39 | override func layoutSubviews() { 40 | super.layoutSubviews() 41 | sharedCalc() 42 | } 43 | 44 | func shared() { 45 | // tableView 46 | self.backgroundColor = Metric.background 47 | self.rowHeight = Metric.tableViewRowHeight 48 | self.sectionHeaderHeight = Metric.tableViewSectionHeaderHeight 49 | self.sectionFooterHeight = Metric.tableViewSectionFooterHeight 50 | // $0.separatorStyle = .none // no border 51 | } 52 | 53 | func sharedCalc() { 54 | // tableView 55 | self.separatorColor = Metric.surface?.darker(by: 8) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /waosSwift/modules/core/ui/CoreUITextField.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * IBDesignable 9 | */ 10 | 11 | @IBDesignable class CoreUITextField: UITextField { 12 | 13 | // MARK: Constants 14 | 15 | var icon: String = "" 16 | struct Metric { 17 | static let surface = UIColor(named: config["theme"]["themes"]["waos"]["surface"].string ?? "") 18 | static let error = UIColor(named: config["theme"]["themes"]["waos"]["error"].string ?? "") 19 | static let radius = CGFloat(config["theme"]["global"]["radius"].int ?? 0) 20 | } 21 | 22 | // MARK: Initializing 23 | 24 | override init(frame: CGRect) { 25 | super.init(frame: frame) 26 | shared() 27 | } 28 | required init?(coder aDecoder: NSCoder) { 29 | super.init(coder: aDecoder) 30 | shared() 31 | } 32 | 33 | override func prepareForInterfaceBuilder() { 34 | super.prepareForInterfaceBuilder() 35 | shared() 36 | } 37 | 38 | override func didMoveToSuperview() { 39 | if(self.icon != "") { 40 | self.setFontAwesomeIcon(self.icon) 41 | } 42 | } 43 | 44 | func shared() { 45 | self.backgroundColor = Metric.surface 46 | self.borderStyle = .none 47 | self.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 20)) 48 | self.leftViewMode = .always 49 | self.layer.cornerRadius = Metric.radius 50 | // prepare for error 51 | self.layer.borderColor = Metric.error?.withAlphaComponent(0.75).cgColor 52 | } 53 | 54 | public func error() { 55 | self.layer.borderWidth = 1.0 56 | self.setFontAwesomeIcon(icon, Metric.error?.lighter() ?? .red) 57 | } 58 | 59 | public func valid() { 60 | self.layer.borderWidth = 0 61 | self.setFontAwesomeIcon(icon, .gray) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /waosSwift/modules/core/ui/CoreUiRefreshControl.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller 3 | */ 4 | 5 | import UIKit 6 | 7 | /** 8 | * IBDesignable 9 | */ 10 | 11 | @IBDesignable class CoreUIRefreshControl: UIRefreshControl { 12 | 13 | // MARK: Constants 14 | 15 | struct Metric { 16 | static let onBackground = UIColor(named: config["theme"]["themes"]["waos"]["onBackground"].string ?? "") 17 | } 18 | 19 | // MARK: Initializing 20 | 21 | override init() { 22 | super.init() 23 | shared() 24 | } 25 | required init?(coder aDecoder: NSCoder) { 26 | super.init(coder: aDecoder) 27 | shared() 28 | } 29 | 30 | override func prepareForInterfaceBuilder() { 31 | super.prepareForInterfaceBuilder() 32 | shared() 33 | } 34 | 35 | func shared() { 36 | self.tintColor = Metric.onBackground 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /waosSwift/modules/home/models/PagesModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Tasks 3 | */ 4 | 5 | struct Pages { 6 | var title: String 7 | var markdown: String 8 | var updatedAt: String? 9 | 10 | init(title: String = "", markdown: String = "", updatedAt: String? = "") { 11 | self.title = title 12 | self.markdown = markdown 13 | self.updatedAt = updatedAt 14 | } 15 | } 16 | 17 | extension Pages: Hashable, Codable { 18 | enum PagesCodingKeys: String, CodingKey { 19 | case title 20 | case markdown 21 | case updatedAt 22 | } 23 | 24 | init(from decoder: Decoder) throws { 25 | let container = try decoder.container(keyedBy: PagesCodingKeys.self) 26 | 27 | title = try container.decode(String.self, forKey: .title) 28 | markdown = try container.decode(String.self, forKey: .markdown) 29 | updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /waosSwift/modules/home/models/PagesResponses.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Tasks Responses 3 | */ 4 | 5 | struct PagesResponse { 6 | var data: [Pages] 7 | } 8 | extension PagesResponse: Codable { 9 | enum PagesResponseCodingKeys: String, CodingKey { 10 | case data 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /waosSwift/modules/home/reactors/HomeTermsReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import ReactorKit 7 | 8 | /** 9 | * Reactor 10 | */ 11 | 12 | final class HomeTermsReactor: Reactor { 13 | 14 | // MARK: Constants 15 | 16 | // user actions 17 | enum Action { 18 | // default 19 | case done 20 | } 21 | 22 | // state changes 23 | enum Mutation { 24 | // inputs 25 | case dismiss 26 | case success(String) 27 | case error(CustomError) 28 | } 29 | 30 | // the current view state 31 | struct State { 32 | // terms 33 | var pages: [Pages] 34 | // settings 35 | var displayLinks: Bool 36 | var style: markDownStyles 37 | // work 38 | var isDismissed: Bool 39 | var errors: [DisplayError] 40 | var error: DisplayError? 41 | 42 | init(terms: Pages, style: markDownStyles, displayLinks: Bool) { 43 | // pages 44 | self.pages = [terms] 45 | // settings 46 | self.style = style 47 | self.displayLinks = displayLinks 48 | // work 49 | self.isDismissed = false 50 | self.errors = [] 51 | } 52 | } 53 | 54 | // MARK: Properties 55 | 56 | let provider: AppServicesProviderType 57 | let initialState: State 58 | 59 | // MARK: Initialization 60 | 61 | init(provider: AppServicesProviderType, terms: Pages, style: markDownStyles = .air, displayLinks: Bool = true) { 62 | self.provider = provider 63 | self.initialState = State(terms: terms, style: style, displayLinks: displayLinks) 64 | } 65 | 66 | // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable) 67 | 68 | func mutate(action: Action) -> Observable { 69 | switch action { 70 | // done 71 | case .done: 72 | return self.provider.userService 73 | .terms() 74 | .map { result in 75 | switch result { 76 | case let .success(response): 77 | UserDefaults.standard.set(response.data.terms ?? nil, forKey: "Terms") 78 | return .dismiss 79 | case let .error(err): return .error(err) 80 | } 81 | } 82 | } 83 | } 84 | 85 | // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation) 86 | 87 | func reduce(state: State, mutation: Mutation) -> State { 88 | var state = state 89 | switch mutation { 90 | // dissmiss 91 | case .dismiss: 92 | log.verbose("♻️ Mutation -> State : dismiss") 93 | state.isDismissed = true 94 | state.errors = [] 95 | // success 96 | case let .success(success): 97 | log.verbose("♻️ Mutation -> State : succes \(success)") 98 | state.error = nil 99 | state.errors = purgeErrors(errors: state.errors, specificTitles: [success]) 100 | // error 101 | case let .error(error): 102 | log.verbose("♻️ Mutation -> State : error \(error)") 103 | let _error: DisplayError = getDisplayError(error, self.provider.preferencesService.isLogged) 104 | self.provider.preferencesService.isLogged = _error.code == 401 ? false : true 105 | state.error = _error 106 | } 107 | return state 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /waosSwift/modules/home/services/HomeService.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import RxSwift 6 | 7 | /** 8 | * Service 9 | */ 10 | 11 | protocol HomeServiceType { 12 | var pages: Observable<[Pages]?> { get } 13 | 14 | func getPages(_ api: HomeApi) -> Observable> 15 | } 16 | 17 | final class HomeService: CoreService, HomeServiceType { 18 | fileprivate let networking = Networking(plugins: [CookiePlugin()]) 19 | 20 | // temporary array 21 | var defaultPages: [Pages] = [Pages()] 22 | 23 | fileprivate let pagesSubject = ReplaySubject<[Pages]?>.create(bufferSize: 1) 24 | lazy var pages: Observable<[Pages]?> = self.pagesSubject.asObservable() 25 | .startWith(nil) 26 | .share(replay: 1) 27 | 28 | func getPages(_ api: HomeApi) -> Observable> { 29 | log.verbose("🔌 service : get Pages") 30 | return self.networking 31 | .request(api) 32 | .map(PagesResponse.self) 33 | .map { response in 34 | self.defaultPages = response.data 35 | return response 36 | } 37 | .asObservable() 38 | .map(MyResult.success) 39 | .catch { err in .just(.error(getError(err)))} 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /waosSwift/modules/home/services/api/HomeApi.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Moya 7 | 8 | /** 9 | * Api 10 | */ 11 | 12 | enum HomeApi { 13 | case changelogs 14 | case page(_ name: String) 15 | } 16 | 17 | extension HomeApi: TargetType { 18 | 19 | public var baseURL: URL { 20 | return getUrl(_protocol: config["api"]["protocol"].string ?? "http", 21 | _host: config["api"]["host"].string ?? "localhost", 22 | _port: config["api"]["port"].string ?? "3000", 23 | _path: config["api"]["endPoints"]["basePath"].string ?? "api") 24 | } 25 | 26 | var path: String { 27 | let apiPathHome = config["api"]["endPoints"]["home"].string ?? "home" 28 | 29 | switch self { 30 | case .changelogs: 31 | return "/" + apiPathHome + "/changelogs" 32 | case .page(let name): 33 | return "/" + apiPathHome + "/pages/" + (name ) 34 | 35 | } 36 | } 37 | 38 | var method: Moya.Method { 39 | switch self { 40 | case .changelogs: 41 | return .get 42 | case .page: 43 | return .get 44 | } 45 | } 46 | 47 | var sampleData: Data { 48 | switch self { 49 | case .changelogs: return stubbed("changelogs") 50 | case .page: return stubbed("getPages") 51 | } 52 | } 53 | 54 | var task: Task { 55 | switch self { 56 | case .changelogs, .page: 57 | return .requestPlain 58 | 59 | } 60 | } 61 | 62 | var headers: [String: String]? { 63 | return ["Content-Type": "application/json"] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /waosSwift/modules/home/services/api/stubbed/changelogs.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task deleted","data":{"title":"title1","description":"do something about something else","user":"5cdbd2c554b9e1f85c922603","id":"5cdbd2c654b9e1f85c922605"}} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/onBoarding/flows/OnBoardingFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import RxFlow 6 | import UIKit 7 | 8 | /** 9 | * Flow 10 | */ 11 | 12 | final class OnboardingFlow: Flow { 13 | var root: Presentable { 14 | return self.rootViewController 15 | } 16 | 17 | private let rootViewController = UINavigationController() 18 | private let services: AppServicesProvider 19 | 20 | init(withServices services: AppServicesProvider) { 21 | self.services = services 22 | } 23 | 24 | deinit { 25 | log.info("🗑 \(type(of: self))") 26 | } 27 | 28 | func navigate(to step: Step) -> FlowContributors { 29 | guard let step = step as? Steps else { return .none } 30 | switch step { 31 | case .onboardingIsRequired: 32 | return navigationToOnboardingScreen() 33 | case .onboardingIsComplete: 34 | return .end(forwardToParentFlowWithStep: Steps.onboardingIsComplete) 35 | default: 36 | return .none 37 | } 38 | } 39 | 40 | private func navigationToOnboardingScreen() -> FlowContributors { 41 | let provider = AppServicesProvider() 42 | let reactor = OnboardingReactor(provider: provider) 43 | let viewController = OnboardingController(reactor: reactor) 44 | viewController.title = L10n.onBoardingTitle 45 | self.rootViewController.pushViewController(viewController, animated: false) 46 | return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: viewController)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /waosSwift/modules/onBoarding/reactors/OnBoardingReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import ReactorKit 6 | 7 | let contents: [String] = [L10n.onBoardingIntroduction, "toto", "titi"] 8 | 9 | /** 10 | * Reactor 11 | */ 12 | 13 | final class OnboardingReactor: Reactor { 14 | 15 | // MARK: Constants 16 | 17 | // user actions 18 | enum Action { 19 | case complete 20 | case update(Int) 21 | } 22 | 23 | // state changes 24 | enum Mutation { 25 | case setContent(Int) 26 | case dismiss 27 | } 28 | 29 | // the current view state 30 | struct State { 31 | var content: String 32 | var isDismissed: Bool 33 | 34 | init() { 35 | self.content = contents[0] 36 | self.isDismissed = false 37 | } 38 | } 39 | 40 | // MARK: Properties 41 | 42 | let provider: AppServicesProviderType 43 | let initialState: State 44 | 45 | // MARK: Initialization 46 | 47 | init(provider: AppServicesProviderType) { 48 | self.provider = provider 49 | self.initialState = State() 50 | } 51 | 52 | // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable) 53 | 54 | func mutate(action: Action) -> Observable { 55 | switch action { 56 | case .complete: 57 | self.provider.preferencesService.onBoarded = true 58 | return .just(.dismiss) 59 | case let .update(page): 60 | return .just(.setContent(page)) 61 | } 62 | } 63 | 64 | // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation) 65 | 66 | func reduce(state: State, mutation: Mutation) -> State { 67 | var state = state 68 | switch mutation { 69 | case let .setContent(page): 70 | state.content = contents[page] 71 | case .dismiss: 72 | state.isDismissed = true 73 | } 74 | return state 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /waosSwift/modules/secondController/controllers/SecondController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import ReactorKit 7 | 8 | /** 9 | * Controller 10 | */ 11 | 12 | class SecondController: CoreController, View { 13 | 14 | // MARK: UI 15 | 16 | let label = CoreUILabel().then { 17 | $0.text = L10n.secondTitle 18 | $0.textAlignment = .center 19 | } 20 | 21 | // MARK: Initializing 22 | 23 | init(reactor: SecondReactor) { 24 | super.init() 25 | self.reactor = reactor 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | // MARK: View Life Cycle 33 | 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | self.view.addSubview(self.label) 37 | } 38 | 39 | override func setupConstraints() { 40 | label.snp.makeConstraints { (make) -> Void in 41 | make.width.height.equalTo(250) 42 | make.center.equalTo(self.view) 43 | } 44 | } 45 | 46 | // MARK: Binding 47 | 48 | func bind(reactor: SecondReactor) { 49 | bindView(reactor) 50 | bindAction(reactor) 51 | bindState(reactor) 52 | } 53 | } 54 | 55 | /** 56 | * Extensions 57 | */ 58 | 59 | private extension SecondController { 60 | 61 | // MARK: views (View -> View) 62 | 63 | func bindView(_ reactor: SecondReactor) {} 64 | 65 | // MARK: actions (View -> Reactor) 66 | 67 | func bindAction(_ reactor: SecondReactor) {} 68 | 69 | // MARK: states (Reactor -> View) 70 | 71 | func bindState(_ reactor: SecondReactor) {} 72 | } 73 | -------------------------------------------------------------------------------- /waosSwift/modules/secondController/flows/SecondFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxFlow 7 | /** 8 | * Flow 9 | */ 10 | 11 | final class SecondFlow: Flow { 12 | var root: Presentable { 13 | return self.rootViewController 14 | } 15 | 16 | private let rootViewController = UINavigationController() 17 | private let services: AppServicesProvider 18 | 19 | init(withServices services: AppServicesProvider) { 20 | self.services = services 21 | } 22 | 23 | deinit { 24 | log.info("🗑 \(type(of: self))") 25 | } 26 | 27 | func navigate(to step: Step) -> FlowContributors { 28 | guard let step = step as? Steps else { return .none } 29 | switch step { 30 | case .secondIsRequired: 31 | return navigateToSecondScreen() 32 | default: 33 | return .none 34 | } 35 | } 36 | 37 | private func navigateToSecondScreen() -> FlowContributors { 38 | let reactor = SecondReactor() 39 | let viewController = SecondController(reactor: reactor) 40 | viewController.title = L10n.secondTitle 41 | self.rootViewController.pushViewController(viewController, animated: true) 42 | return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: OneStepper(withSingleStep: Steps.secondIsRequired))) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /waosSwift/modules/secondController/reactors/SecondReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import ReactorKit 6 | 7 | /** 8 | * Reactor 9 | */ 10 | 11 | final class SecondReactor: Reactor { 12 | 13 | // MARK: Constants 14 | 15 | // user actions 16 | enum Action { 17 | } 18 | 19 | // state changes 20 | enum Mutation { 21 | } 22 | 23 | // the current view state 24 | struct State { 25 | 26 | init() { 27 | } 28 | } 29 | 30 | // MARK: Properties 31 | 32 | let initialState = State() 33 | 34 | // MARK: Initialization 35 | 36 | init() { 37 | } 38 | 39 | // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable) 40 | 41 | // func mutate(action: Action) -> Observable { 42 | // switch action { 43 | // 44 | // } 45 | // } 46 | 47 | // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation) 48 | 49 | // func reduce(state: State, mutation: Mutation) -> State { 50 | // var state = state 51 | // switch mutation { 52 | // 53 | // } 54 | // return state 55 | // } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/controllers/TasksCellController.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import ReactorKit 7 | 8 | /** 9 | * Controller 10 | */ 11 | 12 | final class TasksCellController: CoreTableViewCellController, View { 13 | 14 | typealias Reactor = TasksCellReactor 15 | 16 | // MARK: Constants 17 | 18 | struct Metric { 19 | static let surface = UIColor(named: config["theme"]["themes"]["waos"]["surface"].string ?? "") 20 | } 21 | 22 | // MARK: UI 23 | 24 | let labelTitle = CoreUILabel().then { 25 | $0.numberOfLines = 2 26 | } 27 | 28 | // MARK: Initializing 29 | 30 | override func initialize() { 31 | self.contentView.addSubview(self.labelTitle) 32 | self.contentView.backgroundColor = Metric.surface 33 | } 34 | 35 | // MARK: Layout 36 | override func setupConstraints() { 37 | self.labelTitle.snp.makeConstraints { make in 38 | make.left.equalTo(25) 39 | make.centerY.equalToSuperview() 40 | } 41 | } 42 | 43 | // MARK: Binding 44 | 45 | func bind(reactor: Reactor) { 46 | self.labelTitle.text = reactor.currentState.title 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/flows/TasksFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxFlow 7 | 8 | /** 9 | * Flow 10 | */ 11 | 12 | final class TasksFlow: Flow { 13 | var root: Presentable { 14 | return self.rootViewController 15 | } 16 | 17 | private let rootViewController = UINavigationController() 18 | private let services: AppServicesProvider 19 | 20 | init(withServices services: AppServicesProvider) { 21 | self.services = services 22 | } 23 | 24 | deinit { 25 | log.info("🗑 \(type(of: self))") 26 | } 27 | 28 | func navigate(to step: Step) -> FlowContributors { 29 | guard let step = step as? Steps else { return .none } 30 | switch step { 31 | case .tasksIsRequired: 32 | return navigateToTasksScreen() 33 | default: 34 | return .none 35 | } 36 | } 37 | 38 | private func navigateToTasksScreen() -> FlowContributors { 39 | let reactor = TasksListReactor(provider: self.services) 40 | let viewController = TasksListController(reactor: reactor) 41 | viewController.title = L10n.tasksTitle 42 | self.rootViewController.pushViewController(viewController, animated: true) 43 | return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: OneStepper(withSingleStep: Steps.tasksIsRequired))) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/models/TasksModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Tasks 3 | */ 4 | 5 | struct Tasks { 6 | var id: String? 7 | var title: String 8 | var description: String? 9 | 10 | init(title: String = "", description: String? = "") { 11 | self.title = title 12 | self.description = description 13 | } 14 | } 15 | 16 | extension Tasks: Hashable, Codable { 17 | enum TasksCodingKeys: String, CodingKey { 18 | case id 19 | case title 20 | case description 21 | } 22 | 23 | init(from decoder: Decoder) throws { 24 | let container = try decoder.container(keyedBy: TasksCodingKeys.self) 25 | 26 | id = try container.decodeIfPresent(String.self, forKey: .id) 27 | title = try container.decode(String.self, forKey: .title) 28 | description? = try container.decode(String.self, forKey: .description) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/models/TasksResponses.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Tasks Responses 3 | */ 4 | 5 | struct TasksResponse { 6 | var data: [Tasks] 7 | } 8 | extension TasksResponse: Codable { 9 | enum TasksResponseCodingKeys: String, CodingKey { 10 | case data 11 | } 12 | } 13 | 14 | /** 15 | * Model Task Responses 16 | */ 17 | 18 | struct TaskResponse { 19 | var data: Tasks 20 | } 21 | extension TaskResponse: Codable { 22 | enum TaskResponseCodingKeys: String, CodingKey { 23 | case data 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/reactors/TasksCellReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import ReactorKit 6 | 7 | /** 8 | * Reactor 9 | */ 10 | 11 | final class TasksCellReactor: Reactor { 12 | 13 | // MARK: Constants 14 | 15 | // user actions 16 | typealias Action = NoAction 17 | 18 | // MARK: Properties 19 | 20 | let initialState: Tasks 21 | 22 | // MARK: Initialization 23 | 24 | init(task: Tasks) { 25 | self.initialState = task 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/TasksService.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxSwift 7 | 8 | /** 9 | * Service 10 | */ 11 | 12 | protocol TasksServiceType { 13 | var tasks: Observable<[Tasks]?> { get } 14 | 15 | func list() -> Observable> 16 | func create(_ task: Tasks) -> Observable> 17 | func update(_ task: Tasks) -> Observable> 18 | func delete(_ task: Tasks) -> Observable> 19 | } 20 | 21 | final class TasksService: CoreService, TasksServiceType { 22 | fileprivate let networking = Networking(plugins: [CookiePlugin()]) 23 | 24 | // temporary array 25 | var defaultTasks: [Tasks] = [Tasks()] 26 | 27 | fileprivate let tasksSubject = ReplaySubject<[Tasks]?>.create(bufferSize: 1) 28 | lazy var tasks: Observable<[Tasks]?> = self.tasksSubject.asObservable() 29 | .startWith(nil) 30 | .share(replay: 1) 31 | 32 | func list() -> Observable> { 33 | log.verbose("🔌 service : get") 34 | return self.networking 35 | .request(.list) 36 | .map(TasksResponse.self) 37 | .map { response in 38 | self.defaultTasks = response.data 39 | return response 40 | } 41 | .asObservable() 42 | .map(MyResult.success) 43 | .catch { err in .just(.error(getError(err)))} 44 | } 45 | 46 | func create(_ task: Tasks) -> Observable> { 47 | log.verbose("🔌 service : create") 48 | return self.networking 49 | .request(.create(task)) 50 | .map(TaskResponse.self) 51 | .map { response in 52 | self.defaultTasks.insert(response.data, at: 0) 53 | self.tasksSubject.onNext(self.defaultTasks) 54 | return response 55 | } 56 | .asObservable() 57 | .map(MyResult.success) 58 | .catch { err in .just(.error(getError(err)))} 59 | } 60 | 61 | func update(_ task: Tasks) -> Observable> { 62 | log.verbose("🔌 service : update") 63 | return self.networking 64 | .request(.update(task)) 65 | .map(TaskResponse.self) 66 | .map { response in 67 | if let index = self.defaultTasks.firstIndex(where: { $0.id == response.data.id }) { 68 | self.defaultTasks[index] = response.data 69 | } 70 | self.tasksSubject.onNext(self.defaultTasks) 71 | return response 72 | } 73 | .asObservable() 74 | .map(MyResult.success) 75 | .catch { err in .just(.error(getError(err)))} 76 | } 77 | 78 | func delete(_ task: Tasks) -> Observable> { 79 | log.verbose("🔌 service : delete") 80 | return self.networking 81 | .request(.delete(task)) 82 | .map(DeleteResponse.self) 83 | .map { response in 84 | if let index = self.defaultTasks.firstIndex(where: { $0.id == response.data.id }) { 85 | self.defaultTasks.remove(at: index) 86 | } 87 | self.tasksSubject.onNext(self.defaultTasks) 88 | return response 89 | } 90 | .asObservable() 91 | .map(MyResult.success) 92 | .catch { err in .just(.error(getError(err)))} 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/TasksApi.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Moya 7 | 8 | /** 9 | * Api 10 | */ 11 | 12 | enum TasksApi { 13 | case list 14 | case create(_ task: Tasks) 15 | case get(_ task: Tasks) 16 | case update(_ task: Tasks) 17 | case delete(_ task: Tasks) 18 | 19 | } 20 | 21 | extension TasksApi: TargetType { 22 | 23 | public var baseURL: URL { 24 | return getUrl(_protocol: config["api"]["protocol"].string ?? "http", 25 | _host: config["api"]["host"].string ?? "localhost", 26 | _port: config["api"]["port"].string ?? "3000", 27 | _path: config["api"]["endPoints"]["basePath"].string ?? "api") 28 | } 29 | 30 | var path: String { 31 | let apiPathTasks = config["api"]["endPoints"]["tasks"].string ?? "tasks" 32 | 33 | switch self { 34 | case .list, .create: 35 | return "/" + apiPathTasks 36 | case .get(let task), .update(let task), .delete(let task): 37 | return "/" + apiPathTasks + "/" + (task.id ?? "") 38 | } 39 | } 40 | 41 | var method: Moya.Method { 42 | switch self { 43 | case .list: 44 | return .get 45 | case .create: 46 | return .post 47 | case .get: 48 | return .get 49 | case .update: 50 | return .put 51 | case .delete: 52 | return .delete 53 | } 54 | } 55 | 56 | var sampleData: Data { 57 | switch self { 58 | case .list: return stubbed("list") 59 | case .create: return stubbed("create") 60 | case .get: return stubbed("get") 61 | case .update: return stubbed("update") 62 | case .delete: return stubbed("delete") 63 | } 64 | } 65 | 66 | var task: Task { 67 | switch self { 68 | case .list, .get, .delete: 69 | return .requestPlain 70 | 71 | case .create(let task), .update(let task): 72 | return .requestJSONEncodable(task) 73 | // return .requestParameters(parameters: ["title": task.title, "description": task.description ?? ""], encoding: JSONEncoding.default) 74 | } 75 | } 76 | 77 | var headers: [String: String]? { 78 | return ["Content-Type": "application/json"] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/stubbed/create.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task deleted","data":{"title":"title1","description":"do something about something else","user":"5cdbd2c554b9e1f85c922603","id":"5cdbd2c654b9e1f85c922605"}} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/stubbed/delete.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task deleted","data":{"id":"5cdbd2c654b9e1f85c922605", "deletedCount": 1}} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/stubbed/get.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task get","data":{"title":"title1","description":"do something about something else","user":"5cdbd2c554b9e1f85c922603","id":"5cdbd2c654b9e1f85c922605"}} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/stubbed/list.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task list","data":[{"title":"title1","description":"do something about something else","user":"5cdbd2c554b9e1f85c922603","id":"5cdbd2c654b9e1f85c922605"},{"title":"title2","description":"do something about something else","user":"5cdbd2c654b9e1f85c922604","id":"5cdbd2c654b9e1f85c922606"}]} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/tasks/services/api/stubbed/update.json: -------------------------------------------------------------------------------- 1 | {"type":"success","message":"task updated","data":{"title":"title1","description":"do something about something else","user":"5cdbd2c554b9e1f85c922603","id":"5cdbd2c654b9e1f85c922605"}} 2 | -------------------------------------------------------------------------------- /waosSwift/modules/users/flows/UserFlow.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import RxFlow 7 | 8 | /** 9 | * Flow 10 | */ 11 | 12 | final class UserFlow: Flow { 13 | var root: Presentable { 14 | return self.rootViewController 15 | } 16 | 17 | private let rootViewController = UINavigationController() 18 | private let services: AppServicesProvider 19 | 20 | init(withServices services: AppServicesProvider) { 21 | self.services = services 22 | } 23 | 24 | deinit { 25 | log.info("🗑 \(type(of: self))") 26 | } 27 | 28 | func navigate(to step: Step) -> FlowContributors { 29 | guard let step = step as? Steps else { return .none } 30 | switch step { 31 | case .userIsRequired: 32 | return navigateToUserScreen() 33 | default: 34 | return .none 35 | } 36 | } 37 | 38 | private func navigateToUserScreen() -> FlowContributors { 39 | let reactor = UserReactor(provider: self.services) 40 | let viewController = UserController(reactor: reactor) 41 | viewController.title = L10n.userTitle 42 | self.rootViewController.pushViewController(viewController, animated: true) 43 | return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: OneStepper(withSingleStep: Steps.userIsRequired))) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /waosSwift/modules/users/models/ComplementaryModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Complementary 3 | */ 4 | 5 | struct Complementary: Equatable { 6 | var iosDevices: [Devices]? 7 | var socialNetworks: SocialNetworks? 8 | 9 | init(iosDevices: [Devices]? = nil, socialNetworks: SocialNetworks? = nil) { 10 | self.iosDevices = iosDevices 11 | self.socialNetworks = socialNetworks 12 | } 13 | } 14 | 15 | extension Complementary: Codable, Hashable { 16 | enum ComplementaryCodingKeys: String, CodingKey { 17 | case iosDevices 18 | case socialNetworks 19 | } 20 | 21 | init(from decoder: Decoder) throws { 22 | let container = try decoder.container(keyedBy: ComplementaryCodingKeys.self) 23 | 24 | iosDevices = try container.decodeIfPresent([Devices].self, forKey: .iosDevices) 25 | socialNetworks = try container.decodeIfPresent(SocialNetworks.self, forKey: .socialNetworks) 26 | } 27 | } 28 | 29 | /** 30 | * Model SocialNetworks 31 | */ 32 | 33 | struct SocialNetworks: Equatable { 34 | var instagram: String? 35 | var twitter: String? 36 | var facebook: String? 37 | 38 | init(instagram: String? = nil, twitter: String? = nil, facebook: String? = nil) { 39 | self.instagram = instagram 40 | self.twitter = twitter 41 | self.facebook = facebook 42 | } 43 | } 44 | 45 | extension SocialNetworks: Codable, Hashable { 46 | enum SocialNetworksCodingKeys: String, CodingKey { 47 | case instagram 48 | case twitter 49 | case facebook 50 | } 51 | 52 | init(from decoder: Decoder) throws { 53 | let container = try decoder.container(keyedBy: SocialNetworksCodingKeys.self) 54 | 55 | instagram = try container.decodeIfPresent(String.self, forKey: .instagram) 56 | twitter = try container.decodeIfPresent(String.self, forKey: .twitter) 57 | facebook = try container.decodeIfPresent(String.self, forKey: .facebook) 58 | } 59 | } 60 | 61 | /** 62 | * Model Devices 63 | */ 64 | 65 | struct Devices: Equatable { 66 | var token: String? 67 | var swift: String? 68 | 69 | init(token: String? = nil, swift: String? = nil) { 70 | self.token = token 71 | self.swift = swift 72 | } 73 | } 74 | 75 | extension Devices: Codable, Hashable { 76 | enum DevicesCodingKeys: String, CodingKey { 77 | case token 78 | case swift 79 | } 80 | 81 | init(from decoder: Decoder) throws { 82 | let container = try decoder.container(keyedBy: DevicesCodingKeys.self) 83 | 84 | token = try container.decodeIfPresent(String.self, forKey: .token) 85 | swift = try container.decodeIfPresent(String.self, forKey: .swift) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /waosSwift/modules/users/models/UserPolicyModel.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Model Alerts 3 | */ 4 | 5 | struct UsersPolicy { 6 | var preference: Bool 7 | 8 | init( 9 | preference: Bool = false 10 | ) { 11 | self.preference = preference 12 | } 13 | } 14 | 15 | extension UsersPolicy: Codable, Hashable { 16 | enum UsersPolicyCodingKeys: String, CodingKey { 17 | case preference 18 | } 19 | 20 | init(from decoder: Decoder) throws { 21 | let container = try decoder.container(keyedBy: UsersPolicyCodingKeys.self) 22 | 23 | preference = try container.decode(Bool.self, forKey: .preference) 24 | } 25 | 26 | func encode(to encoder: Encoder) throws { 27 | var container = encoder.container(keyedBy: UsersPolicyCodingKeys.self) 28 | 29 | try container.encode(preference, forKey: .preference) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /waosSwift/modules/users/reactors/UserMoreReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import ReactorKit 6 | 7 | /** 8 | * Reactor 9 | */ 10 | 11 | final class UserMoreReactor: Reactor { 12 | 13 | // MARK: Constants 14 | 15 | // user actions 16 | enum Action { 17 | } 18 | 19 | // state changes 20 | enum Mutation { 21 | // inputs 22 | case dismiss 23 | case success(String) 24 | case error(CustomError) 25 | } 26 | 27 | // the current view state 28 | struct State { 29 | var isDismissed: Bool 30 | var errors: [DisplayError] 31 | var error: DisplayError? 32 | 33 | init() { 34 | self.isDismissed = false 35 | self.errors = [] 36 | } 37 | } 38 | 39 | // MARK: Properties 40 | 41 | let provider: AppServicesProviderType 42 | let initialState: State 43 | 44 | // MARK: Initialization 45 | 46 | init(provider: AppServicesProviderType) { 47 | self.provider = provider 48 | self.initialState = State() 49 | } 50 | 51 | // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable) 52 | 53 | // func mutate(action: Action) -> Observable { 54 | // switch action { 55 | // } 56 | // } 57 | 58 | // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation) 59 | 60 | func reduce(state: State, mutation: Mutation) -> State { 61 | var state = state 62 | switch mutation { 63 | // dissmiss 64 | case .dismiss: 65 | log.verbose("♻️ Mutation -> State : dismiss") 66 | state.isDismissed = true 67 | state.errors = [] 68 | // success 69 | case let .success(success): 70 | log.verbose("♻️ Mutation -> State : succes \(success)") 71 | state.error = nil 72 | state.errors = purgeErrors(errors: state.errors, specificTitles: [success]) 73 | // error 74 | case let .error(error): 75 | log.verbose("♻️ Mutation -> State : error \(error)") 76 | let _error: DisplayError = getDisplayError(error, self.provider.preferencesService.isLogged) 77 | self.provider.preferencesService.isLogged = _error.code == 401 ? false : true 78 | state.error = _error 79 | } 80 | return state 81 | } 82 | 83 | func pageReactor(name: String) -> HomePageReactor { 84 | return HomePageReactor(provider: self.provider, api: .page(name), style: .classic, displayLinks: true) 85 | } 86 | 87 | func changelogReactor() -> HomePageReactor { 88 | return HomePageReactor(provider: self.provider, api: .changelogs, style: .air, displayLinks: false) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /waosSwift/modules/users/reactors/UserPreferenceReactor.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import ReactorKit 6 | 7 | /** 8 | * Reactor 9 | */ 10 | 11 | final class UserPreferenceReactor: Reactor { 12 | 13 | // MARK: Constants 14 | 15 | // user actions 16 | enum Action { 17 | // inputs 18 | case updateBackground(Bool) 19 | // work 20 | case done 21 | } 22 | 23 | // state changes 24 | enum Mutation { 25 | // inputs 26 | case updateBackground(Bool) 27 | // work 28 | case dismiss 29 | // default 30 | case success(String) 31 | case error(CustomError) 32 | } 33 | 34 | // the current view state 35 | struct State { 36 | var user: User 37 | var policy: UsersPolicy 38 | var background: Bool 39 | // work 40 | var isDismissed: Bool 41 | // default 42 | var errors: [DisplayError] 43 | var error: DisplayError? 44 | 45 | init(background: Bool, user: User, policy: UsersPolicy) { 46 | self.user = user 47 | self.policy = policy 48 | self.background = background 49 | // work 50 | self.isDismissed = false 51 | // default 52 | self.errors = [] 53 | } 54 | } 55 | 56 | // MARK: Properties 57 | 58 | let provider: AppServicesProviderType 59 | let initialState: State 60 | 61 | // MARK: Initialization 62 | 63 | init(provider: AppServicesProviderType, user: User, policy: UsersPolicy) { 64 | self.provider = provider 65 | self.initialState = State(background: self.provider.preferencesService.isBackground, user: user, policy: policy) 66 | } 67 | 68 | // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable) 69 | 70 | func mutate(action: Action) -> Observable { 71 | switch action { 72 | // inputs 73 | case let .updateBackground(background): 74 | self.provider.preferencesService.isBackground = background 75 | return .just(.updateBackground(background)) 76 | // done 77 | case .done: 78 | return self.provider.userService 79 | .update(self.currentState.user) 80 | .map { result in 81 | switch result { 82 | case .success: return .dismiss 83 | case let .error(err): return .error(err) 84 | } 85 | } 86 | } 87 | } 88 | 89 | // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation) 90 | 91 | func reduce(state: State, mutation: Mutation) -> State { 92 | var state = state 93 | switch mutation { 94 | // inputs 95 | case let .updateBackground(background): 96 | state.background = background 97 | // dissmiss 98 | case .dismiss: 99 | log.verbose("♻️ Mutation -> State : dismiss") 100 | state.isDismissed = true 101 | state.errors = [] 102 | // success 103 | case let .success(success): 104 | log.verbose("♻️ Mutation -> State : succes \(success)") 105 | state.error = nil 106 | state.errors = purgeErrors(errors: state.errors, specificTitles: [success]) 107 | // error 108 | case let .error(error): 109 | log.verbose("♻️ Mutation -> State : error \(error)") 110 | let _error: DisplayError = getDisplayError(error, self.provider.preferencesService.isLogged) 111 | self.provider.preferencesService.isLogged = _error.code == 401 ? false : true 112 | state.error = _error 113 | } 114 | return state 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /waosSwift/modules/users/services/api/UserApi.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Dependencies 3 | */ 4 | 5 | import UIKit 6 | import Moya 7 | 8 | /** 9 | * Api 10 | */ 11 | 12 | enum UserApi { 13 | case me 14 | case update(_ user: User) 15 | case terms 16 | case delete 17 | case updateAvatar(file: Data, partName: String, fileName: String, mimeType: String) 18 | case deleteAvatar 19 | case data 20 | 21 | } 22 | 23 | extension UserApi: TargetType { 24 | 25 | public var baseURL: URL { 26 | return getUrl(_protocol: config["api"]["protocol"].string ?? "http", 27 | _host: config["api"]["host"].string ?? "localhost", 28 | _port: config["api"]["port"].string ?? "3000", 29 | _path: config["api"]["endPoints"]["basePath"].string ?? "api") 30 | } 31 | 32 | var path: String { 33 | let apiPathUser = config["api"]["endPoints"]["users"].string ?? "users" 34 | 35 | switch self { 36 | case .me : 37 | return "/" + apiPathUser + "/me" 38 | case .update : 39 | return "/" + apiPathUser 40 | case .terms : 41 | return "/" + apiPathUser + "/terms" 42 | case .delete : 43 | return "/" + apiPathUser + "/data" 44 | case .updateAvatar : 45 | return "/" + apiPathUser + "/avatar" 46 | case .deleteAvatar : 47 | return "/" + apiPathUser + "/avatar" 48 | case .data : 49 | return "/" + apiPathUser + "/data/mail" 50 | } 51 | } 52 | 53 | var method: Moya.Method { 54 | switch self { 55 | case .me: 56 | return .get 57 | case .update: 58 | return .put 59 | case .terms: 60 | return .get 61 | case .delete: 62 | return .delete 63 | case .updateAvatar: 64 | return .post 65 | case .deleteAvatar: 66 | return .delete 67 | case .data: 68 | return .get 69 | } 70 | } 71 | 72 | var sampleData: Data { 73 | switch self { 74 | case .me: return stubbed("me") 75 | case .update: return stubbed("update") 76 | case .terms: return stubbed("terms") 77 | case .delete: return stubbed("delete") 78 | case .updateAvatar: return stubbed("avatar") 79 | case .deleteAvatar: return stubbed("avatar") 80 | case .data: return stubbed("data") 81 | } 82 | } 83 | 84 | var task: Task { 85 | switch self { 86 | case .me, .terms, .delete, .deleteAvatar, .data: 87 | return .requestPlain 88 | case .update(let user): 89 | return .requestJSONEncodable(user) 90 | case .updateAvatar(let data, let partName, let fileName, let mimeType): 91 | let gifData = MultipartFormData(provider: .data(data), name: partName, fileName: fileName, mimeType: mimeType) 92 | return .uploadMultipart([gifData]) 93 | } 94 | } 95 | 96 | var headers: [String: String]? { 97 | return ["Content-Type": "application/json"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /waosSwift/modules/users/services/api/stubbed/me.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "success", 3 | "message": "user get", 4 | "data": { 5 | "id": "5ce3eb6236bb3afbb77dcaae", 6 | "provider": "local", 7 | "username": "seeduser", 8 | "roles": [ 9 | "user" 10 | ], 11 | "email": "user@localhost.com", 12 | "lastName": "Local", 13 | "firstName": "User" 14 | "bio": "bio" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /waosSwift/waosSwift.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.applesignin 8 | 9 | Default 10 | 11 | com.apple.developer.associated-domains 12 | 13 | webcredentials:vue.weareopensource.me 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /waosSwift/waosSwiftRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.applesignin 8 | 9 | Default 10 | 11 | com.apple.developer.associated-domains 12 | 13 | webcredentials:vue.weareopensource.me 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /waosSwift/waosSwiftdevelopment.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.applesignin 8 | 9 | Default 10 | 11 | com.apple.developer.associated-domains 12 | 13 | webcredentials:vue.weareopensource.me 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /waosSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /waosSwiftTests/waosSwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // waosSwiftTests.swift 3 | // waosSwiftTests 4 | // 5 | // Created by pierre brisorgueil on 21/02/2019. 6 | // Copyright © 2019 WeAreOpenSource. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import waosSwift 11 | 12 | class WaosSwiftTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /waosSwiftUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /waosSwiftUITests/waosSwiftUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // waosSwiftUITests.swift 3 | // waosSwiftUITests 4 | // 5 | // Created by pierre brisorgueil on 21/02/2019. 6 | // Copyright © 2019 WeAreOpenSource. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class WaosSwiftUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it 20 | // happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - 24 | // required for your tests before they run. The setUp method is a good place to do this. 25 | } 26 | 27 | override func tearDown() { 28 | // Put teardown code here. This method is called after the invocation of each test method in the class. 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------