Compare commits

...
Author SHA1 Message Date
lwouis 8f23f7d74d ci: iterating on gh-actions 2024-11-02 21:29:28 +01:00
lwouis 997a638235 ci: iterating on gh-actions 2024-11-02 21:26:13 +01:00
lwouis cb1bc72335 ci: iterating on gh-actions 2024-11-02 18:55:07 +01:00
lwouis 63b386f581 ci: iterating on gh-actions 2024-11-02 18:18:57 +01:00
lwouis be58bc4a11 feat: improve localizations 2024-11-02 18:06:27 +01:00
lwouis ad1d4bde2a ci: test push 1 commit on branch 2024-11-02 18:00:04 +01:00
lwouis 4ef0b52df5 ci: test push 1 commit on branch 2024-11-02 17:46:53 +01:00
lwouis 3bca6aab6e ci: test push 1 commit on branch 2024-11-02 17:26:26 +01:00
lwouis 13547e0252 ci: test push 1 commit on branch 2024-11-02 17:18:19 +01:00
lwouis bb1972e559 ci: test push 1 commit on branch 2024-11-02 17:15:02 +01:00
lwouis 633ce3f063 ci: switch to github actions (#1241) 2024-11-02 16:54:47 +01:00
lwouis 2c857a6eb1 feat: improve appearance at the different sizes 2024-11-02 00:05:27 +01:00
lwouis ad64ced2df feat: hide space labels when showing same-space windows (closes #3766) 2024-11-02 00:05:27 +01:00
lwouis 81fb54cff5 chore: preload fewer recycled views ahead of time 2024-11-02 00:05:27 +01:00
lwouis 45bbc93bee feat: improve display of wide/tall thumbnails (closes #3791) 2024-11-02 00:05:27 +01:00
lwouis f9542f9214 chore: add jekyll vendor folder to gitignore 2024-10-31 22:57:53 +01:00
lwouis 60052b8ab7 chore: remove metal api checker from xcode runs 2024-10-31 22:57:53 +01:00
lwouis 7b9a6b4e9c docs: update website contents 2024-10-30 22:04:10 +01:00
lwouis 09ca2a6740 chore: improve language preference support 2024-10-29 14:54:51 +01:00
lwouis 20cd27e68d chore: better read preferences + remove unused ones 2024-10-28 21:29:36 +01:00
lwouis 129d061434 feat: allow to run the app without screen-recording permissions
closes #1082
2024-10-17 22:28:34 +02:00
lwouis 19f59b207f feat: add irish localization 2024-10-15 23:28:38 +02:00
lwouis 528028d41b ci: add poeditor upload script 2024-10-15 23:18:28 +02:00
allsochen d77fa67729 feat: i18n 2024-10-15 10:52:08 +02:00
allsochen f33418ec35 feat: add language preference 2024-10-15 10:52:07 +02:00
allsochen dd379c41c9 fix: fixed the rounded corners when the mouse moves over the table 2024-10-12 18:38:18 +02:00
semantic-release-bot 609a25119d chore(release): 7.0.2 [skip ci]
## [7.0.2](https://github.com/lwouis/alt-tab-macos/compare/v7.0.1...v7.0.2) (2024-10-12)

### Bug Fixes

* avoid crashing in some cases ([d46d903](https://github.com/lwouis/alt-tab-macos/commit/d46d903))
* block titles in preferences in rtl languages ([ea5574b](https://github.com/lwouis/alt-tab-macos/commit/ea5574b))
* preferences panel icons wouldnt show on old macos versions ([9258acf](https://github.com/lwouis/alt-tab-macos/commit/9258acf))
* show titles > application name, in thumbnails style (closes [#3667](https://github.com/lwouis/alt-tab-macos/issues/3667)) ([b4e97ea](https://github.com/lwouis/alt-tab-macos/commit/b4e97ea))
2024-10-12 11:10:07 +00:00
lwouis 9258acf765 fix: preferences panel icons wouldnt show on old macos versions 2024-10-12 12:56:12 +02:00
allsochen d46d90395c fix: avoid crashing in some cases 2024-10-12 12:43:56 +02:00
allsochen b4e97ead04 fix: show titles > application name, in thumbnails style (closes #3667) 2024-10-12 12:43:56 +02:00
ShlomoCode ea5574bfa5 fix: block titles in preferences in rtl languages 2024-10-12 12:43:55 +02:00
semantic-release-bot 5b6e8de9a1 chore(release): 7.0.1 [skip ci]
## [7.0.1](https://github.com/lwouis/alt-tab-macos/compare/v7.0.0...v7.0.1) (2024-10-11)

### Bug Fixes

* avoid crashing in rare cases ([9980ec6](https://github.com/lwouis/alt-tab-macos/commit/9980ec6))
2024-10-11 10:14:13 +00:00
lwouis 9980ec62fd fix: avoid crashing in rare cases 2024-10-11 12:00:12 +02:00
semantic-release-bot 8abb9b4358 chore(release): 7.0.0 [skip ci]
# [7.0.0](https://github.com/lwouis/alt-tab-macos/compare/v6.73.0...v7.0.0) (2024-10-09)

### Features

* major rehaul of the preferences! ([6d97cbd](https://github.com/lwouis/alt-tab-macos/commit/6d97cbd)), closes [#351](https://github.com/lwouis/alt-tab-macos/issues/351)

### BREAKING CHANGES

* The old preferences panel has been replaced with a brand new one

Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
2024-10-10 11:20:02 +02:00
lwouis 40c1c0ec02 chore: fix swiftc errors on xcode 13.6 2024-10-10 11:20:02 +02:00
lwouis f2bc95ffcc ci: print xcode version and swift version 2024-10-10 11:20:02 +02:00
lwouis 4b1fbb1243 ci: improve logs 2024-10-10 11:20:01 +02:00
lwouis e7c2a8b880 ci: temporarily disable l10n check 2024-10-10 11:20:01 +02:00
lwouis dbbe9e8b0e ci: do not install swiftformat on npm install 2024-10-10 11:20:01 +02:00
lwouis b26a12074f ci: upgrade node/npm version 2024-10-10 11:20:01 +02:00
allsochen 79f99bd4f1 feat: major rehaul of the preferences!
closes #351

BREAKING CHANGE: The old preferences panel has been replaced with a brand new one

Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
2024-10-10 11:19:39 +02:00
semantic-release-bot 38cfe3a23b chore(release): 6.73.0 [skip ci]
# [6.73.0](https://github.com/lwouis/alt-tab-macos/compare/v6.72.0...v6.73.0) (2024-10-08)

### Bug Fixes

* closing alttab windows gives focus to previous app (closes [#3577](https://github.com/lwouis/alt-tab-macos/issues/3577)) ([0e6f200](https://github.com/lwouis/alt-tab-macos/commit/0e6f200))
* detect passwords app (closes [#3545](https://github.com/lwouis/alt-tab-macos/issues/3545)) ([d0cd206](https://github.com/lwouis/alt-tab-macos/commit/d0cd206))
* dragging files onto windowless apps was inconsistent ([0a9fe9b](https://github.com/lwouis/alt-tab-macos/commit/0a9fe9b))
* finder would sometimes not be listed (closes [#3350](https://github.com/lwouis/alt-tab-macos/issues/3350)) ([eba5e42](https://github.com/lwouis/alt-tab-macos/commit/eba5e42))
* focusing an app could open another version of it ([9d5f11a](https://github.com/lwouis/alt-tab-macos/commit/9d5f11a))
* preview window could remain after focusing an app (closes [#3505](https://github.com/lwouis/alt-tab-macos/issues/3505)) ([e890a60](https://github.com/lwouis/alt-tab-macos/commit/e890a60))

### Features

* add kannada and malayalam and localizations ([5e27701](https://github.com/lwouis/alt-tab-macos/commit/5e27701))
2024-10-08 20:34:17 +00:00
lwouis 5e27701228 feat: add kannada and malayalam and localizations 2024-10-08 22:19:04 +02:00
lwouis 0a9fe9bb89 fix: dragging files onto windowless apps was inconsistent 2024-10-08 21:36:43 +02:00
decodism d0cd206f51 fix: detect passwords app (closes #3545) 2024-09-18 22:00:08 +02:00
lwouis 9d5f11aed5 fix: focusing an app could open another version of it 2024-09-11 09:22:13 +02:00
lwouis eba5e422bf fix: finder would sometimes not be listed (closes #3350) 2024-08-31 18:01:32 +02:00
lwouis 0e6f200efe fix: closing alttab windows gives focus to previous app (closes #3577) 2024-08-30 22:22:13 +02:00
lwouis e890a603c2 fix: preview window could remain after focusing an app (closes #3505) 2024-07-30 12:00:44 +02:00
semantic-release-bot 97951e7e8e chore(release): 6.72.0 [skip ci]
# [6.72.0](https://github.com/lwouis/alt-tab-macos/compare/v6.71.0...v6.72.0) (2024-07-14)

### Bug Fixes

* preferences window quit button supports right-to-left languages ([d3dfd54](https://github.com/lwouis/alt-tab-macos/commit/d3dfd54)), closes [#3487](https://github.com/lwouis/alt-tab-macos/issues/3487)

### Features

* don't show caps-lock indicator as a window (closes [#3171](https://github.com/lwouis/alt-tab-macos/issues/3171)) ([2e15732](https://github.com/lwouis/alt-tab-macos/commit/2e15732))
* improve da/he localizations ([3d98b64](https://github.com/lwouis/alt-tab-macos/commit/3d98b64))
2024-07-14 17:02:49 +00:00
lwouis 3d98b64a42 feat: improve da/he localizations 2024-07-14 18:48:11 +02:00
ShlomoCode d3dfd54f54 fix: preferences window quit button supports right-to-left languages
closes #3487
2024-07-14 18:15:45 +02:00
lwouis 2e15732f64 feat: don't show caps-lock indicator as a window (closes #3171)
this is a not a fix: it's a macos bug; we providing a workaround
2024-07-14 16:14:13 +02:00
lwouis 45f5c5368a ci: ensure website download link is always up-to-date 2024-07-08 17:05:53 +02:00
semantic-release-bot 012e3b5f3c chore(release): 6.71.0 [skip ci]
# [6.71.0](https://github.com/lwouis/alt-tab-macos/compare/v6.70.1...v6.71.0) (2024-07-06)

### Bug Fixes

* restore default cmd+tab shortcut when alt-tab crashes ([2c47b4e](https://github.com/lwouis/alt-tab-macos/commit/2c47b4e))
* showing permissions window would crash on macos < 10.15 ([bb5215c](https://github.com/lwouis/alt-tab-macos/commit/bb5215c)), closes [#3437](https://github.com/lwouis/alt-tab-macos/issues/3437)
* traffic light icons could appear half-transparent (closes [#2892](https://github.com/lwouis/alt-tab-macos/issues/2892)) ([1eef1e0](https://github.com/lwouis/alt-tab-macos/commit/1eef1e0))

### Features

* improve fr/he/it/ja/pt/sv localizations ([35521a5](https://github.com/lwouis/alt-tab-macos/commit/35521a5))
* show de-fullscreen button on fullscreen windows ([ad1e8d0](https://github.com/lwouis/alt-tab-macos/commit/ad1e8d0))
2024-07-06 10:30:19 +00:00
lwouis 35521a5214 feat: improve fr/he/it/ja/pt/sv localizations 2024-07-06 12:15:39 +02:00
allsochen ad1e8d03e4 feat: show de-fullscreen button on fullscreen windows 2024-07-06 12:15:37 +02:00
lwouis 1eef1e083d fix: traffic light icons could appear half-transparent (closes #2892) 2024-06-26 19:25:47 +02:00
lwouis bb5215c306 fix: showing permissions window would crash on macos < 10.15
closes #3437
2024-06-21 11:52:49 +02:00
lwouis 2c47b4e585 fix: restore default cmd+tab shortcut when alt-tab crashes 2024-06-21 11:52:36 +02:00
semantic-release-bot 4371eb7c39 chore(release): 6.70.1 [skip ci]
## [6.70.1](https://github.com/lwouis/alt-tab-macos/compare/v6.70.0...v6.70.1) (2024-06-01)

### Bug Fixes

* prevent crash introduced in v6.70.0 (closes [#3392](https://github.com/lwouis/alt-tab-macos/issues/3392)) ([e16a3d4](https://github.com/lwouis/alt-tab-macos/commit/e16a3d4))
2024-06-01 11:38:26 +00:00
lwouis e16a3d46b3 fix: prevent crash introduced in v6.70.0 (closes #3392) 2024-06-01 13:20:36 +02:00
semantic-release-bot 1a3996e73b chore(release): 6.70.0 [skip ci]
# [6.70.0](https://github.com/lwouis/alt-tab-macos/compare/v6.69.0...v6.70.0) (2024-05-29)

### Bug Fixes

* detect safari fullscreen windows better (closes [#3384](https://github.com/lwouis/alt-tab-macos/issues/3384)) ([c3006c6](https://github.com/lwouis/alt-tab-macos/commit/c3006c6))

### Features

* add utm-app to default blacklist ([523bcbb](https://github.com/lwouis/alt-tab-macos/commit/523bcbb))
2024-05-29 22:15:51 +00:00
lwouis c3006c69a6 fix: detect safari fullscreen windows better (closes #3384) 2024-05-29 23:49:23 +02:00
lwouis 523bcbb6e3 feat: add utm-app to default blacklist 2024-05-26 00:17:04 +02:00
semantic-release-bot 127ad155ba chore(release): 6.69.0 [skip ci]
# [6.69.0](https://github.com/lwouis/alt-tab-macos/compare/v6.68.0...v6.69.0) (2024-05-12)

### Bug Fixes

* only show standard windows of preview.app (closes [#3351](https://github.com/lwouis/alt-tab-macos/issues/3351)) ([6231977](https://github.com/lwouis/alt-tab-macos/commit/6231977))

### Features

* update fi, ja, ko, pt-br, th localizations ([14d287a](https://github.com/lwouis/alt-tab-macos/commit/14d287a))
2024-05-12 20:18:54 +00:00
lwouis 14d287afe3 feat: update fi, ja, ko, pt-br, th localizations 2024-05-12 22:04:07 +02:00
lwouis 6231977e70 fix: only show standard windows of preview.app (closes #3351) 2024-05-12 22:03:12 +02:00
semantic-release-bot 758b3e804e chore(release): 6.68.0 [skip ci]
# [6.68.0](https://github.com/lwouis/alt-tab-macos/compare/v6.67.0...v6.68.0) (2024-04-03)

### Bug Fixes

* preview.app window may not show when opening many docs ([31b5a3d](https://github.com/lwouis/alt-tab-macos/commit/31b5a3d)), closes [#3276](https://github.com/lwouis/alt-tab-macos/issues/3276)
* preview.app windows would sometimes not show (closes [#3275](https://github.com/lwouis/alt-tab-macos/issues/3275)) ([785cf9c](https://github.com/lwouis/alt-tab-macos/commit/785cf9c))

### Features

* improve ar/nb/nn/pl/tr/uk localizations ([67a6d81](https://github.com/lwouis/alt-tab-macos/commit/67a6d81))
* new thai and norwegian localizations (closes [#3260](https://github.com/lwouis/alt-tab-macos/issues/3260)) ([d9aa319](https://github.com/lwouis/alt-tab-macos/commit/d9aa319))
2024-04-03 19:30:58 +00:00
lwouis 67a6d8194d feat: improve ar/nb/nn/pl/tr/uk localizations 2024-04-03 21:16:30 +02:00
lwouis 31b5a3df77 fix: preview.app window may not show when opening many docs
(closes #3276)
2024-04-03 20:17:05 +02:00
lwouis 785cf9c73a fix: preview.app windows would sometimes not show (closes #3275) 2024-04-03 20:01:32 +02:00
lwouis d9aa319e2c feat: new thai and norwegian localizations (closes #3260) 2024-03-25 20:25:10 +01:00
Dmytro Pashchenko 141e7233ff docs: fix typo on ukrainian/russian languages on the website 2024-03-19 16:43:47 +01:00
semantic-release-bot 9ff25e7c3a chore(release): 6.67.0 [skip ci]
# [6.67.0](https://github.com/lwouis/alt-tab-macos/compare/v6.66.0...v6.67.0) (2024-03-15)

### Features

* add icelandic localization ([7df1432](https://github.com/lwouis/alt-tab-macos/commit/7df1432))
* improve ru/cn localizations ([c9fe54e](https://github.com/lwouis/alt-tab-macos/commit/c9fe54e))
* support autocad non-native windows ([#3219](https://github.com/lwouis/alt-tab-macos/issues/3219)) ([bbaef6c](https://github.com/lwouis/alt-tab-macos/commit/bbaef6c))
2024-03-15 17:33:32 +00:00
lwouis c9fe54ed29 feat: improve ru/cn localizations 2024-03-15 18:18:03 +01:00
lwouis bbaef6cf8e feat: support autocad non-native windows (#3219) 2024-03-15 18:18:01 +01:00
lwouis 7df1432744 feat: add icelandic localization 2024-03-15 18:13:45 +01:00
semantic-release-bot 6442d85c9f chore(release): 6.66.0 [skip ci]
# [6.66.0](https://github.com/lwouis/alt-tab-macos/compare/v6.65.0...v6.66.0) (2024-03-03)

### Bug Fixes

* alttab would crash after menubar icon was actioned with voiceover ([f7fdf3f](https://github.com/lwouis/alt-tab-macos/commit/f7fdf3f)), closes [#3211](https://github.com/lwouis/alt-tab-macos/issues/3211)

### Features

* improved ca/de/es/ko/nl/sv/vi/zh localizations ([f4ad2ea](https://github.com/lwouis/alt-tab-macos/commit/f4ad2ea))
2024-03-03 12:03:34 +00:00
lwouis f4ad2ea6c8 feat: improved ca/de/es/ko/nl/sv/vi/zh localizations 2024-03-03 12:49:05 +01:00
lwouis f7fdf3f987 fix: alttab would crash after menubar icon was actioned with voiceover
closes #3211
2024-03-03 12:45:07 +01:00
lwouis b8833b74eb refactor: fix inkscape bin path 2024-02-08 17:08:21 +01:00
lwouis bc29d1e86c refactor: apply compiler hints 2024-02-08 17:08:21 +01:00
lwouis 5ea9b8211e docs: improve website performance, and lazy-load images 2024-02-08 17:08:21 +01:00
243 changed files with 35039 additions and 8863 deletions

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
on:
push:
branches:
- master
- test-github-actions
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPCENTER_SECRET: ${{ secrets.APPCENTER_SECRET }}
APPCENTER_TOKEN: ${{ secrets.APPCENTER_TOKEN }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }}
FEEDBACK_TOKEN: ${{ secrets.FEEDBACK_TOKEN }}
NETLIFY_WEBHOOK: ${{ secrets.NETLIFY_WEBHOOK }}
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
GITHUB_EVENT_AFTER: ${{ github.event.after }}
BUILD_DIR: ${{ github.workspace }}
jobs:
build:
runs-on: macos-12
environment: production
steps:
# - name: Dump gitHub context
# env:
# GITHUB_CONTEXT: ${{ toJSON(github) }}
# run: echo "$GITHUB_CONTEXT"
- name: Set up node
uses: actions/setup-node@v3
with:
node-version: 16
- name: Set up xcode
run: sudo xcode-select -s /Applications/Xcode_13.4.1.app/Contents/Developer
- name: Calculate checkout fetch-depth
id: pre-checkout
env:
COMMITS: ${{ toJson(github.event.commits) }}
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
COUNT=$(( $(echo "$COMMITS" | jq length) + 1 ))
else
COUNT=1
fi
echo "fetch-depth=$COUNT" >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: ${{ steps.pre-checkout.outputs.fetch-depth }}
- name: Set up environment variables
run: |
echo "IS_RELEASE=$(if [ \"$GITHUB_REF_NAME\" = \"master\" -a \"$GITHUB_EVENT_NAME\" = \"push\" ]; then echo true; else echo false; fi)" >> $GITHUB_ENV
echo "XCODE_BUILD_PATH=DerivedData/Build/Products/Release" >> $GITHUB_ENV
echo "VERSION_FILE=VERSION.txt" >> $GITHUB_ENV
echo "APP_NAME=$(awk -F ' = ' '/PRODUCT_NAME/ { print $2; }' < config/base.xcconfig)" >> $GITHUB_ENV
- name: Run print-env.sh
run: |
scripts/print_env.sh
- name: Run ci_cd.sh
run: |
scripts/pre_build_checks.sh
scripts/ci_cd.sh
# - name: Deploy to github releases
# uses: softprops/action-gh-release@v1
# with:
# files: ${{ env.XCODE_BUILD_PATH }}/*.zip
# - name: Run after_deploy.sh
# run: |
# scripts/after_deploy.sh
+2 -1
View File
@@ -6,7 +6,8 @@ IDEWorkspaceChecks.plist
/DerivedData/
/build/
.DS_Store
docs/_site/
/docs/_site/
/docs/vendor/
codesign.conf
codesign.crt
codesign.key
+8
View File
@@ -0,0 +1,8 @@
retain_objc_accessible: true
schemes:
- Release
targets:
- alt-tab-macos
workspace: alt-tab-macos.xcworkspace
report_exclude:
- src/api-wrappers/PrivateApis.swift
+6
View File
@@ -0,0 +1,6 @@
# We can find the rules here.
# https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
--swiftversion 5.5
--maxwidth 120
+11
View File
@@ -0,0 +1,11 @@
# **/*.swift: Matches all .swift files in the current directory and all its subdirectories.
# src/**/*.swift: Matches all .swift files in the src directory and all its subdirectories.
# !node_modules/**: Excludes all files in the node_modules directory and all its subdirectories.
Pods/**
Generated/**
**/PrivateApis.swift
# We will open a new branch to deal with code format issues at the next release.
**/*
+36 -34
View File
@@ -1,34 +1,36 @@
branches:
only:
- master
- develop
os: osx
osx_image: xcode13.4
language: node_js
node_js:
- 10
addons:
ssh_known_hosts: github.com
homebrew:
packages:
- vitorgalvao/tiny-scripts/cask-repair
update: true
env:
global:
- IS_RELEASE=$(if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then echo true; fi)
- XCODE_BUILD_PATH="DerivedData/Build/Products/Release"
- VERSION_FILE="VERSION.txt"
- APP_NAME="$(awk -F ' = ' '/PRODUCT_NAME/ { print $2; }' < config/base.xcconfig)"
install: true # disable default install
script: scripts/travis.sh
before_deploy: if [[ $TRAVIS_TEST_RESULT == 1 ]]; then echo "build failed; exiting to prevent deploy phases"; sleep 2; exit 1; fi
deploy:
provider: releases
token:
secure: wZUHfOs+JQ0ikNMJ8BnM+8fuoQdhgOEQpNd01ueZxe760GAAjSbq1icME8qaYJJMk1G6n14jo9ecEhzxF0XV8hSdy4WtikK7l5SXiOx5Efuh/tNscmHggGk235eLwQD9pglePZsP3xJUB1BHEj260rypkjnSx5kVOfLUeB1ASeg4+KdxZd2vXjwel+YQf+dYbNUYtwoWqjiLbqT1ao+0q04MPgD2c9jb+HnsA94LUGb44lPXqfUJ+xG2LxBpcDW/q1JhFXL/AmJb0dOOEVzzrBqfWysPukgPMONexZse4c3BBqkMI52ZuF3OoEVAffofmuGKiR5OoyU/hW17xi9jGeb79r+Xt4RdaJAgwcn7/7GtemYKUJp8jhLGsy9XZJShENX7UHTkP3L4iyOrd2Wz+ctZWAebyNReLt985/xTaplpDiQrPcKAuQNBa4sgWk5rIsf7Z0tcBfZau/fQl9Q4100xTbwC+GeAbL0IyXG3kVzQGsGllXHr/2JM1sj7rmKA7lmDlp7xiOBCPscn2YCT87U+F6VbYoNvnxne0igQOUwrvX4qa4XlYrUXDeHcyKku0m8znY74nOu+m6LXkIt8r29pi6Xac0/ZGMwKZRUvKg/skamWPRoy9DAVd8TBQW1c91oYKmqDQRy0eWnD6X1djcKywJ7WlSQDejebJql0h+o=
file_glob: true
file: "$XCODE_BUILD_PATH/*.zip"
skip_cleanup: true
on:
repo: lwouis/alt-tab-macos
after_deploy: scripts/update_homebrew_cask.sh
#branches:
# only:
# - master
# - test-github-actions
#os: osx
#osx_image: xcode13.4
#language: node_js
#node_js:
# - 16
#addons:
# ssh_known_hosts: github.com
# homebrew:
# packages:
# - vitorgalvao/tiny-scripts/cask-repair
# update: true
#env:
# global:
# - IS_RELEASE=$(if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then echo true; fi)
# - XCODE_BUILD_PATH="DerivedData/Build/Products/Release"
# - VERSION_FILE="VERSION.txt"
# - APP_NAME="$(awk -F ' = ' '/PRODUCT_NAME/ { print $2; }' < config/base.xcconfig)"
# - BUILD_DIR="$TRAVIS_BUILD_DIR"
#install: true # disable default install
#script: scripts/print_env_vars.sh
#script: scripts/print_env.sh && scripts/pre_build_checks.sh && scripts/ci_cd.sh
#before_deploy: if [[ $TRAVIS_TEST_RESULT == 1 ]]; then echo "build failed; exiting to prevent deploy phases"; sleep 2; exit 1; fi
#deploy:
# provider: releases
# token:
# secure: wZUHfOs+JQ0ikNMJ8BnM+8fuoQdhgOEQpNd01ueZxe760GAAjSbq1icME8qaYJJMk1G6n14jo9ecEhzxF0XV8hSdy4WtikK7l5SXiOx5Efuh/tNscmHggGk235eLwQD9pglePZsP3xJUB1BHEj260rypkjnSx5kVOfLUeB1ASeg4+KdxZd2vXjwel+YQf+dYbNUYtwoWqjiLbqT1ao+0q04MPgD2c9jb+HnsA94LUGb44lPXqfUJ+xG2LxBpcDW/q1JhFXL/AmJb0dOOEVzzrBqfWysPukgPMONexZse4c3BBqkMI52ZuF3OoEVAffofmuGKiR5OoyU/hW17xi9jGeb79r+Xt4RdaJAgwcn7/7GtemYKUJp8jhLGsy9XZJShENX7UHTkP3L4iyOrd2Wz+ctZWAebyNReLt985/xTaplpDiQrPcKAuQNBa4sgWk5rIsf7Z0tcBfZau/fQl9Q4100xTbwC+GeAbL0IyXG3kVzQGsGllXHr/2JM1sj7rmKA7lmDlp7xiOBCPscn2YCT87U+F6VbYoNvnxne0igQOUwrvX4qa4XlYrUXDeHcyKku0m8znY74nOu+m6LXkIt8r29pi6Xac0/ZGMwKZRUvKg/skamWPRoy9DAVd8TBQW1c91oYKmqDQRy0eWnD6X1djcKywJ7WlSQDejebJql0h+o=
# file_glob: true
# file: "$XCODE_BUILD_PATH/*.zip"
# skip_cleanup: true
# on:
# repo: lwouis/alt-tab-macos
#after_deploy: scripts/after_deploy.sh
+8 -2
View File
@@ -6,6 +6,12 @@ verify_ssl = true
[dev-packages]
[packages]
fonttools = "*"
fonttools = "==4.8.1"
googletrans = "==3.0.0"
[scripts]
[requires]
python_version = "3.6"
[scripts]
font2png = "scripts/convert_font_to_png.sh"
translate = "python scripts/translate_i18n_tools.py resources/l10n --retry_translate"
Generated
+99 -1
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "8f11c81dff5e32871b7cb2314acb76fd1fb4a06fdb403a65bc6749fe3770eda6"
"sha256": "00c7c75d00988a027e56c90f3a0bbba05aec3b41b2f7fd9be24f3cb27bf15eaf"
},
"pipfile-spec": 6,
"requires": {},
@@ -14,13 +14,111 @@
]
},
"default": {
"certifi": {
"hashes": [
"sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8",
"sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"
],
"markers": "python_version >= '3.6'",
"version": "==2024.8.30"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"fonttools": {
"hashes": [
"sha256:596c0e8399c650dbf1048b7ca6fc7b12bd79a7db0981f3b87743de8c2b7512de",
"sha256:6d241d319ea9eae4c4ee27cbd1db8656dbd496dd9aa884401debfab3a4c7d82b"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==4.8.1"
},
"googletrans": {
"hashes": [
"sha256:44caeea42d91ff6ead5c2d49db2b88de66c45c2fe874c8ec03eb9b4ceb3a533d"
],
"index": "pypi",
"version": "==3.0.0"
},
"h11": {
"hashes": [
"sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1",
"sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"
],
"version": "==0.9.0"
},
"h2": {
"hashes": [
"sha256:61e0f6601fa709f35cdb730863b4e5ec7ad449792add80d1410d4174ed139af5",
"sha256:875f41ebd6f2c44781259005b157faed1a5031df3ae5aa7bcb4628a6c0782f14"
],
"version": "==3.2.0"
},
"hpack": {
"hashes": [
"sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89",
"sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"
],
"version": "==3.0.0"
},
"hstspreload": {
"hashes": [
"sha256:2ab4518495a132c4ae430c474afffd12938852c6eec68ce1368c8f7858dc3076",
"sha256:9c1b2d0313899d3ff9dac03ab39d53fed95c32eef9862e7eabee8dc07dfd589c"
],
"markers": "python_version >= '3.6'",
"version": "==2024.9.1"
},
"httpcore": {
"hashes": [
"sha256:9850fe97a166a794d7e920590d5ec49a05488884c9fc8b5dba8561effab0c2a0",
"sha256:ecc5949310d9dae4de64648a4ce529f86df1f232ce23dcfefe737c24d21dfbe9"
],
"markers": "python_version >= '3.6'",
"version": "==0.9.1"
},
"httpx": {
"hashes": [
"sha256:32d930858eab677bc29a742aaa4f096de259f1c78c68a90ad11f5c3c04f08335",
"sha256:3642bd13e90b80ba8a243a730275eb10a4c26ec96f5fc16b87e458d4ab21efae"
],
"markers": "python_version >= '3.6'",
"version": "==0.13.3"
},
"hyperframe": {
"hashes": [
"sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40",
"sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"
],
"version": "==5.2.0"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"rfc3986": {
"hashes": [
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
],
"version": "==1.5.0"
},
"sniffio": {
"hashes": [
"sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
"sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.1"
}
},
"develop": {}
+1
View File
@@ -7,4 +7,5 @@ target 'alt-tab-macos' do
pod 'ShortcutRecorder', :git => 'https://github.com/lwouis/ShortcutRecorder.git', :branch => 'alt-tab-current'
pod 'SwiftyMarkdown', '1.1.0'
pod 'AppCenter/Crashes', '4.3.0'
pod 'SwiftyBeaver', '1.9.0'
end
+5 -1
View File
@@ -5,6 +5,7 @@ PODS:
- LetsMove (1.24)
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyBeaver (1.9.0)
- SwiftyMarkdown (1.1.0)
DEPENDENCIES:
@@ -12,11 +13,13 @@ DEPENDENCIES:
- LetsMove (from `https://github.com/lwouis/LetsMove.git`, commit `7abf4daed1a25218f2b52f2dfd190aee5a50071c`)
- ShortcutRecorder (from `https://github.com/lwouis/ShortcutRecorder.git`, branch `alt-tab-current`)
- Sparkle (from `https://raw.githubusercontent.com/lwouis/Sparkle/fix-iframe-popup-1.24/Sparkle.podspec`)
- SwiftyBeaver (= 1.9.0)
- SwiftyMarkdown (= 1.1.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyBeaver
- SwiftyMarkdown
EXTERNAL SOURCES:
@@ -42,8 +45,9 @@ SPEC CHECKSUMS:
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
PODFILE CHECKSUM: db137cd4263235b552493949169a44f7e874a90f
PODFILE CHECKSUM: 6e94a2963d935455790ee55be0ce938834d1bba5
COCOAPODS: 1.9.1
+5 -1
View File
@@ -5,6 +5,7 @@ PODS:
- LetsMove (1.24)
- ShortcutRecorder (3.1)
- Sparkle (1.24.0)
- SwiftyBeaver (1.9.0)
- SwiftyMarkdown (1.1.0)
DEPENDENCIES:
@@ -12,11 +13,13 @@ DEPENDENCIES:
- LetsMove (from `https://github.com/lwouis/LetsMove.git`, commit `7abf4daed1a25218f2b52f2dfd190aee5a50071c`)
- ShortcutRecorder (from `https://github.com/lwouis/ShortcutRecorder.git`, branch `alt-tab-current`)
- Sparkle (from `https://raw.githubusercontent.com/lwouis/Sparkle/fix-iframe-popup-1.24/Sparkle.podspec`)
- SwiftyBeaver (= 1.9.0)
- SwiftyMarkdown (= 1.1.0)
SPEC REPOS:
trunk:
- AppCenter
- SwiftyBeaver
- SwiftyMarkdown
EXTERNAL SOURCES:
@@ -42,8 +45,9 @@ SPEC CHECKSUMS:
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
ShortcutRecorder: fdf620aca34101b0cba3b10fca815e0459254189
Sparkle: 1fa490bb4ab2a9b37cfb9ea4feec19dd33b81cd2
SwiftyBeaver: d39e7c376bf15e9fffca8263b8fd1f42ea56aa2d
SwiftyMarkdown: 033abc9dad9a37903f81aec5188cade436f40520
PODFILE CHECKSUM: db137cd4263235b552493949169a44f7e874a90f
PODFILE CHECKSUM: 6e94a2963d935455790ee55be0ce938834d1bba5
COCOAPODS: 1.9.1
+831 -612
View File
File diff suppressed because it is too large. Load diff
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+270
View File
@@ -0,0 +1,270 @@
<p align="center"><a href="https://swiftybeaver.com"><img src="https://cloud.githubusercontent.com/assets/564725/19889302/73b1ee84-a034-11e6-8753-2d060502397c.jpg" style="width: 888px;" alt="SwiftyBeaver"></a><br/><b>Colorful</b>, flexible, <b>lightweight</b> logging for Swift 3, Swift 4 & <b>Swift 5</b>.<br/>Great for <b>development & release</b> with support for Console, File & cloud platforms.<br/>Log <b>during release</b> to the conveniently built-in SwiftyBeaver Platform, the <b>dedicated Mac App</b> & <b>Elasticsearch</b>!<br/><br/><a href="http://docs.swiftybeaver.com">Docs</a> | <a href="https://swiftybeaver.com">Website</a> | <a href="https://twitter.com/SwiftyBeaver">Twitter</a> | <a href="#privacy">Privacy</a> | <a href="https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE">License</a><br/></p>
<p align="center"><a href="https://swift.org" target="_blank"><img src="https://img.shields.io/badge/Language-Swift%203,%204%20&%205-orange.svg" alt="Language Swift 2, 3, 4 & 5"></a> <a href="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver" target="_blank"><img src="https://circleci.com/gh/SwiftyBeaver/SwiftyBeaver/tree/master.svg?style=shield" alt="CircleCI"/></a><br/><p>
----
<br/>
### During Development: Colored Logging to Xcode Console
<img src="https://cloud.githubusercontent.com/assets/564725/18608323/ac065a98-7ce6-11e6-8e1b-2a062d54a1d5.png" width="608">
[Learn more](http://docs.swiftybeaver.com/article/9-log-to-xcode-console) about colored logging to Xcode 8 Console with Swift 3, 4 & 5. For Swift 2.3 [use this Gist](https://gist.github.com/skreutzberger/7c396573796473ed1be2c6d15cafed34). **No need to hack Xcode 8 anymore** to get color. You can even customize the log level word (ATTENTION instead of ERROR maybe?), the general amount of displayed data and if you want to use the 💜s or replace them with something else 😉
<br/>
### During Development: Colored Logging to File
<img src="https://cloud.githubusercontent.com/assets/564725/18608325/b7ecd4c2-7ce6-11e6-829b-7f8f9fe6ef2f.png" width="738">
[Learn more](http://docs.swiftybeaver.com/article/10-log-to-file) about logging to file which is great for Terminal.app fans or to store logs on disk.
<br/>
### On Release: Encrypted Logging to SwiftyBeaver Platform
<img src="https://cloud.githubusercontent.com/assets/564725/14281408/38d6a6ba-fb39-11e5-9584-34e3679bb1c5.jpg" width="700">
[Learn more](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform) about logging to the SwiftyBeaver Platform **during release!**
<br/>
### Browse, Search & Filter via Mac App
![swiftybeaver-demo1](https://cloud.githubusercontent.com/assets/564725/14846071/218c0646-0c62-11e6-92cb-e6e963b68724.gif)
Conveniently access your logs during development & release with our [free Mac App](https://swiftybeaver.com).
<br/>
### On Release: Enterprise-ready Logging to Your Elasticsearch & Kibana (on-premise)
<img src="https://user-images.githubusercontent.com/564725/34486363-dc501aec-efcf-11e7-92b2-1163cca9e7aa.jpg" width="700">
[Learn more](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker) about **legally compliant**, end-to-end encrypted logging to Elasticsearch with **SwiftyBeaver Enterprise**. Install via Docker or manual, fully-featured free trial included!
<br/>
### Google Cloud & More
You can fully customize your log format, turn it into JSON, or create your own destinations. For example our [Google Cloud Destination](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/Sources/GoogleCloudDestination.swift) is just another customized logging format which adds the powerful functionality of automatic server-side Swift logging when hosted on Google Cloud Platform.
<br/>
----
<br/>
<br/>
## Installation
- For **Swift 4 & 5** install the latest SwiftyBeaver version
- For **Swift 3** install SwiftyBeaver 1.8.4
- For **Swift 2** install SwiftyBeaver 0.7.0
<br/>
### Carthage
You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftyBeaver by adding that to your Cartfile:
Swift 4 & 5:
``` Swift
github "SwiftyBeaver/SwiftyBeaver"
```
Swift 3:
``` Swift
github "SwiftyBeaver/SwiftyBeaver" ~> 1.8.4
```
Swift 2:
``` Swift
github "SwiftyBeaver/SwiftyBeaver" ~> 0.7
```
<br/>
### Swift Package Manager
For [Swift Package Manager](https://swift.org/package-manager/) add the following package to your Package.swift file. Just Swift 4 & 5 are supported:
``` Swift
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .upToNextMajor(from: "1.9.0")),
```
<br/>
### CocoaPods
To use [CocoaPods](https://cocoapods.org) just add this to your Podfile:
Swift 4 & 5:
``` Swift
pod 'SwiftyBeaver'
```
Swift 3:
``` Ruby
target 'MyProject' do
use_frameworks!
# Pods for MyProject
pod 'SwiftyBeaver', '~> 1.8.4'
end
```
Swift 2:
``` Ruby
target 'MyProject' do
use_frameworks!
# Pods for MyProject
pod 'SwiftyBeaver', '~> 0.7'
end
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
# Configure Pod targets for Xcode 8 with Swift 2.3
config.build_settings['SWIFT_VERSION'] = '2.3'
end
end
```
<br/>
<br/>
## Usage
Add that near the top of your `AppDelegate.swift` to be able to use SwiftyBeaver in your whole project.
``` Swift
import SwiftyBeaver
let log = SwiftyBeaver.self
```
At the the beginning of your `AppDelegate:didFinishLaunchingWithOptions()` add the SwiftyBeaver log destinations (console, file, etc.), optionally adjust the [log format](http://docs.swiftybeaver.com//article/20-custom-format) and then you can already do the following log level calls globally:
``` Swift
// add log destinations. at least one is needed!
let console = ConsoleDestination() // log to Xcode Console
let file = FileDestination() // log to default swiftybeaver.log file
let cloud = SBPlatformDestination(appID: "foo", appSecret: "bar", encryptionKey: "123") // to cloud
// use custom format and set console output to short time, log level & message
console.format = "$DHH:mm:ss$d $L $M"
// or use this for JSON output: console.format = "$J"
// add the destinations to SwiftyBeaver
log.addDestination(console)
log.addDestination(file)
log.addDestination(cloud)
// Now let’s log!
log.verbose("not so important") // prio 1, VERBOSE in silver
log.debug("something to debug") // prio 2, DEBUG in green
log.info("a nice information") // prio 3, INFO in blue
log.warning("oh no, that won’t be good") // prio 4, WARNING in yellow
log.error("ouch, an error did occur!") // prio 5, ERROR in red
// log anything!
log.verbose(123)
log.info(-123.45678)
log.warning(Date())
log.error(["I", "like", "logs!"])
log.error(["name": "Mr Beaver", "address": "7 Beaver Lodge"])
// optionally add context to a log message
console.format = "$L: $M $X"
log.debug("age", 123) // "DEBUG: age 123"
log.info("my data", context: [1, "a", 2]) // "INFO: my data [1, \"a\", 2]"
```
<br/>
<br/>
## Server-side Swift
We ❤️ server-side Swift 4 & 5 and SwiftyBeaver supports it **out-of-the-box**! Try for yourself and run SwiftyBeaver inside a Ubuntu Docker container. Just install Docker and then go to your the project folder on macOS or Ubuntu and type:
```shell
# create docker image, build SwiftyBeaver and run unit tests
swift build --clean && docker build --rm -t swiftybeaver .
# optionally log into container to run Swift CLI and do more stuff
docker run --rm -it --privileged=true -v $PWD:/app swiftybeaver
```
Best: for the popular server-side Swift web framework [Vapor](https://github.com/vapor/vapor) you can use **[our Vapor logging provider](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor)** which makes server logging awesome again 🙌
<br/>
<br/>
## Documentation
**Getting Started:**
- [Features](http://docs.swiftybeaver.com/article/7-introduction)
- [Installation](http://docs.swiftybeaver.com/article/5-installation)
- [Basic Setup](http://docs.swiftybeaver.com/article/6-basic-setup)
**Logging Destinations:**
- [Colored Logging to Xcode Console](http://docs.swiftybeaver.com/article/9-log-to-xcode-console)
- [Colored Logging to File](http://docs.swiftybeaver.com/article/10-log-to-file)
- [Encrypted Logging & Analytics to SwiftyBeaver Platform](http://docs.swiftybeaver.com/article/11-log-to-swiftybeaver-platform)
- [Encrypted Logging & Analytics to Elasticsearch & Kibana](http://docs.swiftybeaver.com/article/34-enterprise-quick-start-via-docker)
**Advanced Topics:**
- [Custom Format & Context](http://docs.swiftybeaver.com/article/20-custom-format)
- [Filters](http://docs.swiftybeaver.com/article/21-filters)
**Stay Informed:**
- [Official Website](https://swiftybeaver.com))
- [On Twitter](https://twitter.com/SwiftyBeaver)
<br/>
<br/>
## Privacy
**SwiftyBeaver is not collecting any data without you as a developer knowing about it**. That's why it is **open-source** and developed in a simple way to be easy to inspect and check what it is actually doing under the hood.
The only sending to servers is done if you use the `SBPlatformDestination`. That destination is meant for production logging and on default it sends your logs plus additional device information **end-to-end encrypted** to our cloud service. Our cloud service **can not decrypt the data**.
Instead, you install our Mac App and that Mac App downloads the encrypted logs from the cloud and decrypts and shows them to you. Additionally, the Mac App stores all data that it downloads in a local SQLite database file on your computer so that you actually "physically" own your data.
The business model of the SwiftyBeaver cloud service is to provide the most secure logging solution in the market. On purpose we do not provide a web UI for you because it would require us to store your encryption key on our servers.
**Only you can see the logging and device data** which is sent from your users' devices. Our servers just see encrypted data and do not know your decryption key.
SwiftyBeaver is **fully GDPR compliant** due to its focus on encryption and transparency in what data is collected and also meets **Apple’s latest requirements** on the privacy of 3rd party frameworks.
Our Enterprise offering is an even more secure solution where you are not using anymore our cloud service and Mac App but you send your end-to-end encrypted logs directly to your own servers and you store them in your Elasticsearch cluster. The **Enterprise offering is used by health tech** and governmental institutions which require the highest level of privacy and security.
<br/>
<br/>
## End-to-End Encryption
SwiftyBeaver is using symmetric AES256CBC encryption in the `SBPlatformDestination` destination. No other officially supported destination uses encryption.
The encryption used in the `SBPlatformDestination` destination is end-to-end. The open-source SwiftyBeaver logging framework symmetrically encrypts all logging data on your client's device inside your app (iPhone, iPad, ...) before it is sent to the SwiftyBeaver Crypto Cloud. The decryption is done on your Mac which has the SwiftyBeaver Mac App installed. All logging data stays encrypted in the SwiftyBeaver Crypto Cloud due to the lack of the password.
You are using the encryption at your own risk. SwiftyBeaver’s authors and contributors do not take over any guarantee about the absence of potential security or cryptopgraphy issues, weaknesses, etc.; please also read the LICENSE file for details. Also if you are interested in cryptography in general, please have a look at the file AES256CBC.swift to learn more about the cryptographical implementation.
<br/>
<br/>
## License
SwiftyBeaver Framework is released under the [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE).
+788
View File
@@ -0,0 +1,788 @@
//
// AES256CBC.swift
// AES256CBC https://github.com/SwiftyBeaver/AES256CBC
//
// Created by Sebastian Kreutzberger on 2/9/16.
// Copyright © 2016 SwiftyBeaver. All rights reserved.
//
import Foundation
final class AES256CBC {
/// returns optional encrypted string via AES-256CBC
/// automatically generates and puts a random IV at first 16 chars
/// the password must be exactly 32 chars long for AES-256
class func encryptString(_ str: String, password: String) -> String? {
if !str.isEmpty && Data(password.utf8).count == 32 {
let iv = randomText(16)
let key = password
guard let encryptedString = try? aesEncrypt(str, key: key, iv: iv) else {
print("an error occured while encrypting")
return nil
}
return iv + encryptedString
}
return nil
}
/// returns optional decrypted string via AES-256CBC
/// IV need to be at first 16 chars, password must be 32 chars long
class func decryptString(_ str: String, password: String) -> String? {
if Data(str.utf8).count > 16 && Data(password.utf8).count == 32 {
// get AES initialization vector from first 16 chars
let iv = String(str.prefix(16))
let encryptedString = str.replacingOccurrences(of: iv, with: "",
options: String.CompareOptions.literal,
range: nil) // remove IV
guard let decryptedString = try? aesDecrypt(encryptedString, key: password, iv: iv) else {
print("an error occured while decrypting")
return nil
}
return decryptedString
}
return nil
}
/// returns random string (uppercase & lowercase, no spaces) of 32 characters length
/// which can be used as SHA-256 compatbile password
class func generatePassword() -> String {
return randomText(32)
}
/// returns random text of a defined length
public class func randomText(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map { _ in letters.randomElement()!})
}
/// returns encrypted string, IV must be 16 chars long
fileprivate class func aesEncrypt(_ str: String, key: String, iv: String) throws -> String {
let keyData = key.data(using: String.Encoding.utf8)!
let ivData = iv.data(using: String.Encoding.utf8)!
let data = str.data(using: String.Encoding.utf8)!
#if swift(>=5)
let enc = try Data(AESCipher(key: keyData.bytes,
iv: ivData.bytes).encrypt(bytes: data.bytes))
#else
let enc = try Data(bytes: AESCipher(key: keyData.bytes,
iv: ivData.bytes).encrypt(bytes: data.bytes))
#endif
return enc.base64EncodedString(options: [])
}
/// returns decrypted string, IV must be 16 chars long
fileprivate class func aesDecrypt(_ str: String, key: String, iv: String) throws -> String {
let keyData = key.data(using: String.Encoding.utf8)!
let ivData = iv.data(using: String.Encoding.utf8)!
if let data = Data(base64Encoded: str) {
#if swift(>=5)
let dec = try Data(AESCipher(key: keyData.bytes,
iv: ivData.bytes).decrypt(bytes: data.bytes))
#else
let dec = try Data(bytes: AESCipher(key: keyData.bytes,
iv: ivData.bytes).decrypt(bytes: data.bytes))
#endif
guard let decryptStr = String(data: dec, encoding: String.Encoding.utf8) else {
throw NSError(domain: "Invalid utf8 data", code: 0, userInfo: nil)
}
return decryptStr
} else {
return "error"
}
}
}
// swiftlint:disable line_length
// The following is an altered source version that only includes AES-CBC. The original software can be found at:
// https://github.com/krzyzanowskim/CryptoSwift
//
// This is the original copyright notice:
//
// Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
// This software is provided 'as-is', without any express or implied warranty.
//
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
//
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// - This notice may not be removed or altered from any source or binary distribution.
//
// swiftlint:enable line_length
// MARK: - AESCipher
private typealias Key = Array<UInt8>
final private class AESCipher {
enum Error: Swift.Error {
case blockSizeExceeded
case dataPaddingRequired
case invalidKeyOrInitializationVector
case invalidInitializationVector
}
enum Variant: Int {
case aes128 = 1, aes192, aes256
var Nk: Int { // Nk words
return [4, 6, 8][self.rawValue - 1]
}
var Nb: Int { // Nb words
return 4
}
var Nr: Int { // Nr
return Nk + 6
}
}
static let blockSize: Int = 16 // 128 /8
var variant: Variant {
switch self.key.count * 8 {
case 128:
return .aes128
case 192:
return .aes192
case 256:
return .aes256
default:
preconditionFailure("Unknown AES variant for given key.")
}
}
private let key: [UInt8]
private let iv: [UInt8]?
private let blockMode = CBCBlockMode()
private lazy var expandedKey: Array<Array<UInt32>> = self.expandKey(self.key, variant: self.variant)
private lazy var expandedKeyInv: Array<Array<UInt32>> = self.expandKeyInv(self.key, variant: self.variant)
private lazy var sBoxes:(sBox: [UInt32], invSBox: [UInt32]) = self.calculateSBox()
private lazy var sBox: [UInt32] = self.sBoxes.sBox
private lazy var sBoxInv: [UInt32] = self.sBoxes.invSBox
// Parameters for Linear Congruence Generators
private let Rcon: [UInt8] = [
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d]
// swiftlint:disable line_length
fileprivate let T0: Array<UInt32> = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c]
fileprivate let T0_INV: Array<UInt32> = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0]
fileprivate let T1: Array<UInt32> = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a]
fileprivate let T1_INV: Array<UInt32> = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042]
fileprivate let T2: Array<UInt32> = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16]
fileprivate let T2_INV: Array<UInt32> = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257]
fileprivate let T3: Array<UInt32> = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616]
fileprivate let T3_INV: Array<UInt32> = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8]
fileprivate var U1: Array<UInt32> = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d]
fileprivate var U2: Array<UInt32> = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3]
fileprivate var U3: Array<UInt32> = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397]
fileprivate var U4: Array<UInt32> = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746]
// swiftlint:enable line_length
init(key: [UInt8], iv: [UInt8]) throws {
self.key = key
self.iv = iv
}
/**
Encrypt message. If padding is necessary, then PKCS7 padding is added and needs to be removed after decryption.
- parameter message: Plaintext data
- parameter padding: Optional padding
- returns: Encrypted data
*/
func encrypt(bytes: [UInt8]) throws -> [UInt8] {
let finalBytes = PKCS7().add(bytes: bytes, blockSize: AESCipher.blockSize)
let blocks = finalBytes.chunks(size: AESCipher.blockSize)
return try blockMode.encrypt(blocks: blocks, iv: self.iv, cipherOperation: encrypt)
}
private func encrypt(block: [UInt8]) -> [UInt8]? {
let rounds = self.variant.Nr
let rk = self.expandedKey
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
var t = [UInt32](repeating: 0, count: 4)
for r in 0..<rounds - 1 {
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
let lb00 = T0[Int(t[0] & 0xFF)]
let lb01 = T1[Int((t[1] >> 8) & 0xFF)]
let lb02 = T2[Int((t[2] >> 16) & 0xFF)]
let lb03 = T3[Int(t[3] >> 24)]
b[0] = lb00 ^ lb01 ^ lb02 ^ lb03
let lb10 = T0[Int(t[1] & 0xFF)]
let lb11 = T1[Int((t[2] >> 8) & 0xFF)]
let lb12 = T2[Int((t[3] >> 16) & 0xFF)]
let lb13 = T3[Int(t[0] >> 24)]
b[1] = lb10 ^ lb11 ^ lb12 ^ lb13
let lb20 = T0[Int(t[2] & 0xFF)]
let lb21 = T1[Int((t[3] >> 8) & 0xFF)]
let lb22 = T2[Int((t[0] >> 16) & 0xFF)]
let lb23 = T3[Int(t[1] >> 24)]
b[2] = lb20 ^ lb21 ^ lb22 ^ lb23
let lb30 = T0[Int(t[3] & 0xFF)]
let lb31 = T1[Int((t[0] >> 8) & 0xFF)]
let lb32 = T2[Int((t[1] >> 16) & 0xFF)]
let lb33 = T3[Int(t[2] >> 24)]
b[3] = lb30 ^ lb31 ^ lb32 ^ lb33
}
// last round
let r = rounds - 1
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
// rounds
b[0] = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0]
b[1] = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1]
b[2] = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2]
b[3] = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3]
var out = [UInt8]()
out.reserveCapacity(b.count * 4)
for num in b {
out.append(UInt8(num & 0xFF))
out.append(UInt8((num >> 8) & 0xFF))
out.append(UInt8((num >> 16) & 0xFF))
out.append(UInt8((num >> 24) & 0xFF))
}
return out
}
func decrypt(bytes: [UInt8]) throws -> [UInt8] {
if (bytes.count % AESCipher.blockSize) != 0 {
throw Error.blockSizeExceeded
}
let blocks = bytes.chunks(size: AESCipher.blockSize)
return try PKCS7().remove(bytes: blockMode.decrypt(blocks: blocks, iv: self.iv, cipherOperation: decrypt), blockSize: AESCipher.blockSize)
}
private func decrypt(block: [UInt8]) -> [UInt8]? {
let rounds = self.variant.Nr
let rk = expandedKeyInv
var b = toUInt32Array(slice: block[block.startIndex..<block.endIndex])
var t = [UInt32](repeating: 0, count: 4)
for r in (2...rounds).reversed() {
t[0] = b[0] ^ rk[r][0]
t[1] = b[1] ^ rk[r][1]
t[2] = b[2] ^ rk[r][2]
t[3] = b[3] ^ rk[r][3]
let b00 = T0_INV[Int(t[0] & 0xFF)]
let b01 = T1_INV[Int((t[3] >> 8) & 0xFF)]
let b02 = T2_INV[Int((t[2] >> 16) & 0xFF)]
let b03 = T3_INV[Int(t[1] >> 24)]
b[0] = b00 ^ b01 ^ b02 ^ b03
let b10 = T0_INV[Int(t[1] & 0xFF)]
let b11 = T1_INV[Int((t[0] >> 8) & 0xFF)]
let b12 = T2_INV[Int((t[3] >> 16) & 0xFF)]
let b13 = T3_INV[Int(t[2] >> 24)]
b[1] = b10 ^ b11 ^ b12 ^ b13
let b20 = T0_INV[Int(t[2] & 0xFF)]
let b21 = T1_INV[Int((t[1] >> 8) & 0xFF)]
let b22 = T2_INV[Int((t[0] >> 16) & 0xFF)]
let b23 = T3_INV[Int(t[3] >> 24)]
b[2] = b20 ^ b21 ^ b22 ^ b23
let b30 = T0_INV[Int(t[3] & 0xFF)]
let b31 = T1_INV[Int((t[2] >> 8) & 0xFF)]
let b32 = T2_INV[Int((t[1] >> 16) & 0xFF)]
let b33 = T3_INV[Int(t[0] >> 24)]
b[3] = b30 ^ b31 ^ b32 ^ b33
}
// last round
t[0] = b[0] ^ rk[1][0]
t[1] = b[1] ^ rk[1][1]
t[2] = b[2] ^ rk[1][2]
t[3] = b[3] ^ rk[1][3]
// rounds
let lb00 = sBoxInv[Int(B0(t[0]))]
let lb01 = (sBoxInv[Int(B1(t[3]))] << 8)
let lb02 = (sBoxInv[Int(B2(t[2]))] << 16)
let lb03 = (sBoxInv[Int(B3(t[1]))] << 24)
b[0] = lb00 | lb01 | lb02 | lb03 ^ rk[0][0]
let lb10 = sBoxInv[Int(B0(t[1]))]
let lb11 = (sBoxInv[Int(B1(t[0]))] << 8)
let lb12 = (sBoxInv[Int(B2(t[3]))] << 16)
let lb13 = (sBoxInv[Int(B3(t[2]))] << 24)
b[1] = lb10 | lb11 | lb12 | lb13 ^ rk[0][1]
let lb20 = sBoxInv[Int(B0(t[2]))]
let lb21 = (sBoxInv[Int(B1(t[1]))] << 8)
let lb22 = (sBoxInv[Int(B2(t[0]))] << 16)
let lb23 = (sBoxInv[Int(B3(t[3]))] << 24)
b[2] = lb20 | lb21 | lb22 | lb23 ^ rk[0][2]
let lb30 = sBoxInv[Int(B0(t[3]))]
let lb31 = (sBoxInv[Int(B1(t[2]))] << 8)
let lb32 = (sBoxInv[Int(B2(t[1]))] << 16)
let lb33 = (sBoxInv[Int(B3(t[0]))] << 24)
b[3] = lb30 | lb31 | lb32 | lb33 ^ rk[0][3]
var out = [UInt8]()
out.reserveCapacity(b.count * 4)
for num in b {
out.append(UInt8(num & 0xFF))
out.append(UInt8((num >> 8) & 0xFF))
out.append(UInt8((num >> 16) & 0xFF))
out.append(UInt8((num >> 24) & 0xFF))
}
return out
}
fileprivate func expandKeyInv(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
let rounds = variant.Nr
var rk2: Array<Array<UInt32>> = expandKey(key, variant: variant)
for r in 1..<rounds {
var w: UInt32
w = rk2[r][0]
let u1 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u2 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][0] = u1 ^ u2
w = rk2[r][1]
let u11 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u12 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][1] = u11 ^ u12
w = rk2[r][2]
let u22 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u23 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][2] = u22 ^ u23
w = rk2[r][3]
let u33 = U1[Int(B0(w))] ^ U2[Int(B1(w))]
let u34 = U3[Int(B2(w))] ^ U4[Int(B3(w))]
rk2[r][3] = u33 ^ u34
}
return rk2
}
fileprivate func expandKey(_ key: Key, variant: Variant) -> Array<Array<UInt32>> {
func convertExpandedKey(_ expanded: Array<UInt8>) -> Array<Array<UInt32>> {
var arr = Array<UInt32>()
for idx in stride(from: expanded.startIndex, to: expanded.endIndex, by: 4) {
let four = Array(expanded[idx..<idx.advanced(by: 4)].reversed())
let num = UInt32(bytes: four)
arr.append(num)
}
var allarr = Array<Array<UInt32>>()
for idx in stride(from: arr.startIndex, to: arr.endIndex, by: 4) {
allarr.append(Array(arr[idx..<idx.advanced(by: 4)]))
}
return allarr
}
/*
* Function used in the Key Expansion routine that takes a four-byte
* input word and applies an S-box to each of the four bytes to
* produce an output word.
*/
func subWord(_ word: Array<UInt8>) -> Array<UInt8> {
var result = word
for i in 0..<4 {
result[i] = UInt8(sBox[Int(word[i])])
}
return result
}
var w = Array<UInt8>(repeating: 0, count: variant.Nb * (variant.Nr + 1) * 4)
for i in 0..<variant.Nk {
for wordIdx in 0..<4 {
w[(4*i)+wordIdx] = key[(4*i)+wordIdx]
}
}
var tmp: Array<UInt8>
for i in variant.Nk..<variant.Nb * (variant.Nr + 1) {
tmp = Array<UInt8>(repeating: 0, count: 4)
for wordIdx in 0..<4 {
tmp[wordIdx] = w[4*(i-1)+wordIdx]
}
if (i % variant.Nk) == 0 {
tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: MemoryLayout<UInt32>.size))
tmp[0] = tmp.first! ^ Rcon[i/variant.Nk]
} else if variant.Nk > 6 && (i % variant.Nk) == 4 {
tmp = subWord(tmp)
}
// xor array of bytes
for wordIdx in 0..<4 {
w[4*i+wordIdx] = w[4*(i-variant.Nk)+wordIdx]^tmp[wordIdx]
}
}
return convertExpandedKey(w)
}
}
extension AESCipher {
fileprivate func B0(_ x: UInt32) -> UInt32 {
return x & 0xFF
}
fileprivate func B1(_ x: UInt32) -> UInt32 {
return (x >> 8) & 0xFF
}
fileprivate func B2(_ x: UInt32) -> UInt32 {
return (x >> 16) & 0xFF
}
fileprivate func B3(_ x: UInt32) -> UInt32 {
return (x >> 24) & 0xFF
}
fileprivate func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 {
var result: UInt32 = 0
result |= UInt32(B1(T0[Int(x0 & 255)]))
result |= UInt32(B1(T0[Int((x1 >> 8) & 255)])) << 8
result |= UInt32(B1(T0[Int((x2 >> 16) & 255)])) << 16
result |= UInt32(B1(T0[Int(x3 >> 24)])) << 24
return result
}
fileprivate func calculateSBox() -> (sBox: Array<UInt32>, invSBox: Array<UInt32>) {
var sbox = Array<UInt32>(repeating: 0, count: 256)
var invsbox = sbox
sbox[0] = 0x63
var p: UInt8 = 1, q: UInt8 = 1
repeat {
#if swift(>=4.0)
p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
#else
p = p ^ (UInt8(truncatingBitPattern: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1B : 0))
#endif
q ^= q << 1
q ^= q << 2
q ^= q << 4
q ^= (q & 0x80) == 0x80 ? 0x09 : 0
let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4)
sbox[Int(p)] = UInt32(s)
invsbox[Int(s)] = UInt32(p)
} while (p != 1)
return (sBox: sbox, invSBox: invsbox)
}
}
// MARK: Foundation
extension AESCipher {
convenience init(key: String, iv: String) throws {
guard let kkey = key.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes,
// swiftlint:disable conditional_binding_cascade
let iiv = iv.data(using: String.Encoding.utf8, allowLossyConversion: false)?.bytes else {
// swiftlint:disable conditional_binding_cascade
throw Error.invalidKeyOrInitializationVector
}
try self.init(key: kkey, iv: iiv)
}
}
// MARK: - CBC
// I have no better name for that
private typealias CipherOperationOnBlock = ([UInt8]) -> [UInt8]?
private struct CBCBlockMode {
enum BlockError: Swift.Error {
case MissingInitializationVector
}
func encrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
precondition(!blocks.isEmpty)
guard let iv = iv else {
throw BlockError.MissingInitializationVector
}
var out: [UInt8] = [UInt8]()
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
var prevCiphertext = iv // for the first time prevCiphertext = iv
for plaintext in blocks {
if let encrypted = cipherOperation(xor(prevCiphertext, plaintext)) {
out.append(contentsOf: encrypted)
prevCiphertext = encrypted
}
}
return out
}
func decrypt(blocks: [[UInt8]], iv: [UInt8]?, cipherOperation: CipherOperationOnBlock) throws -> [UInt8] {
precondition(!blocks.isEmpty)
guard let iv = iv else {
throw BlockError.MissingInitializationVector
}
var out: [UInt8] = [UInt8]()
out.reserveCapacity(blocks.count * blocks[blocks.startIndex].count)
var prevCiphertext = iv // for the first time prevCiphertext = iv
for ciphertext in blocks {
if let decrypted = cipherOperation(ciphertext) { // decrypt
out.append(contentsOf: xor(prevCiphertext, decrypted))
}
prevCiphertext = ciphertext
}
return out
}
}
// MARK: - Padding
private struct PKCS7 {
init() {
}
func add(bytes: [UInt8], blockSize: Int) -> [UInt8] {
let padding = UInt8(blockSize - (bytes.count % blockSize))
var withPadding = bytes
if padding == 0 {
// If the original data is a multiple of N bytes, then an extra block of bytes with value N is added.
for _ in 0..<blockSize {
withPadding.append(contentsOf: [UInt8(blockSize)])
}
} else {
// The value of each added byte is the number of bytes that are added
for _ in 0..<padding {
withPadding.append(contentsOf: [UInt8(padding)])
}
}
return withPadding
}
func remove(bytes: [UInt8], blockSize: Int?) -> [UInt8] {
let lastByte = bytes.last!
let padding = Int(lastByte) // last byte
let finalLength = bytes.count - padding
if finalLength < 0 {
return bytes
}
if padding >= 1 {
return Array(bytes[0..<finalLength])
}
return bytes
}
}
// MARK: - Utils
private func xor(_ a: Array<UInt8>, _ b: Array<UInt8>) -> Array<UInt8> {
var xored = Array<UInt8>(repeating: 0, count: min(a.count, b.count))
for i in 0..<xored.count {
xored[i] = a[i] ^ b[i]
}
return xored
}
private func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 {
return ((value << by) & 0xFF) | (value >> (8 - by))
}
private func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 {
return ((value << by) & 0xFFFFFFFF) | (value >> (32 - by))
}
private protocol BitshiftOperationsType {
static func << (lhs: Self, rhs: Self) -> Self
}
private protocol ByteConvertible {
init(_ value: UInt8)
init(truncatingBitPattern: UInt64)
}
#if swift(>=4.0)
#else
extension UInt32: BitshiftOperationsType, ByteConvertible { }
#endif
fileprivate extension UInt32 {
init<T: Collection>(bytes: T) where T.Iterator.Element == UInt8, T.Index == Int {
self = bytes.toInteger()
}
func bytes(totalBytes: Int = MemoryLayout<UInt32>.size) -> Array<UInt8> {
return arrayOfBytes(value: self, length: totalBytes)
}
}
private func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
var result = Array<UInt32>()
result.reserveCapacity(16)
for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
var val: UInt32 = 0
val |= slice.count > 3 ? UInt32(slice[idx.advanced(by: 3)]) << 24 : 0
val |= slice.count > 2 ? UInt32(slice[idx.advanced(by: 2)]) << 16 : 0
val |= slice.count > 1 ? UInt32(slice[idx.advanced(by: 1)]) << 8 : 0
val |= !slice.isEmpty ? UInt32(slice[idx]) : 0
result.append(val)
}
return result
}
/// Array of bytes, little-endian representation. Don't use if not necessary.
/// I found this method slow
private func arrayOfBytes<T>(value: T, length: Int? = nil) -> Array<UInt8> {
let totalBytes = length ?? MemoryLayout<T>.size
let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
valuePointer.pointee = value
let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
var bytes = Array<UInt8>(repeating: 0, count: totalBytes)
for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
}
#if swift(>=4.1)
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
#else
valuePointer.deinitialize()
valuePointer.deallocate(capacity: 1)
#endif
return bytes
}
fileprivate extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
#if swift(>=4.0)
func toInteger<T>() -> T where T: FixedWidthInteger {
if self.isEmpty {
return 0
}
let size = MemoryLayout<T>.size
var bytes = self.reversed()
if bytes.count < MemoryLayout<T>.size {
let paddingCount = MemoryLayout<T>.size - bytes.count
if paddingCount > 0 {
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
}
}
if size == 1 {
return T(truncatingIfNeeded: UInt64(bytes[0]))
}
var result: T = 0
for byte in bytes.reversed() {
result = result << 8 | T(byte)
}
return result
}
#else
func toInteger<T: Integer>() -> T where T: ByteConvertible, T: BitshiftOperationsType {
if self.isEmpty {
return 0
}
var bytes = self.reversed()
if bytes.count < MemoryLayout<T>.size {
let paddingCount = MemoryLayout<T>.size - bytes.count
if paddingCount > 0 {
bytes += Array<UInt8>(repeating: 0, count: paddingCount)
}
}
if MemoryLayout<T>.size == 1 {
return T(truncatingBitPattern: UInt64(bytes[0]))
}
var result: T = 0
for byte in bytes.reversed() {
result = result << 8 | T(byte)
}
return result
}
#endif
}
fileprivate extension Array {
/** split in chunks with given chunk size */
func chunks(size chunksize: Int) -> Array<Array<Element>> {
var words = Array<Array<Element>>()
words.reserveCapacity(self.count / chunksize)
for idx in stride(from: chunksize, through: self.count, by: chunksize) {
words.append(Array(self[idx - chunksize..<idx])) // slow for large table
}
let reminder = self.suffix(self.count % chunksize)
if !reminder.isEmpty {
words.append(Array(reminder))
}
return words
}
}
fileprivate extension Data {
var bytes: Array<UInt8> {
return Array(self)
}
}
+165
View File
@@ -0,0 +1,165 @@
//
// Base64.swift
// SwiftyBeaver (macOS)
//
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
#if os(Linux)
import Foundation
struct InvalidBase64: Error {}
struct Base64 {
static func decode(_ string: String) throws -> [UInt8] {
return try decode([UInt8](string.utf8))
}
/// Decodes a Base64 encoded String into Data
///
/// - throws: If the string isn't base64 encoded
static func decode(_ string: [UInt8]) throws -> [UInt8] {
let lookupTable: [UInt8] = [
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
]
let remainder = string.count % 4
let length = (string.count - remainder) / 4
var decoded = [UInt8]()
decoded.reserveCapacity(length)
var index = 0
var i0: UInt8 = 0
var i1: UInt8 = 0
var i2: UInt8 = 0
var i3: UInt8 = 0
while index &+ 4 < string.count {
i0 = lookupTable[numericCast(string[index])]
i1 = lookupTable[numericCast(string[index &+ 1])]
i2 = lookupTable[numericCast(string[index &+ 2])]
i3 = lookupTable[numericCast(string[index &+ 3])]
if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 {
throw InvalidBase64()
}
decoded.append(i0 << 2 | i1 >> 4)
decoded.append(i1 << 4 | i2 >> 2)
decoded.append(i2 << 6 | i3)
index += 4
}
if string.count &- index > 1 {
i0 = lookupTable[numericCast(string[index])]
i1 = lookupTable[numericCast(string[index &+ 1])]
if i1 > 63 {
guard string[index] == 61 else {
throw InvalidBase64()
}
return decoded
}
if i2 > 63 {
guard string[index &+ 2] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i0 << 2 | i1 >> 4)
if string.count &- index > 2 {
i2 = lookupTable[numericCast(string[index &+ 2])]
if i2 > 63 {
guard string[index &+ 2] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i1 << 4 | i2 >> 2)
if string.count &- index > 3 {
i3 = lookupTable[numericCast(string[index &+ 3])]
if i3 > 63 {
guard string[index &+ 3] == 61 else {
throw InvalidBase64()
}
return decoded
}
decoded.append(i2 << 6 | i3)
}
}
}
return decoded
}
static func encode(_ data: [UInt8]) -> String {
let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var encoded: String = ""
func appendCharacterFromBase(_ character: Int) {
encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)])
}
func byte(_ index: Int) -> Int {
return Int(data[index])
}
let decodedBytes = data.map { Int($0) }
var i = 0
while i < decodedBytes.count - 2 {
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6))
appendCharacterFromBase(byte(i + 2) & 0x3F)
i += 3
}
if i < decodedBytes.count {
appendCharacterFromBase((byte(i) >> 2) & 0x3F)
if i == decodedBytes.count - 1 {
appendCharacterFromBase(((byte(i) & 0x3) << 4))
encoded.append("=")
} else {
appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
appendCharacterFromBase(((byte(i + 1) & 0xF) << 2))
}
encoded.append("=")
}
return encoded
}
}
#endif
+510
View File
@@ -0,0 +1,510 @@
//
// BaseDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
import Dispatch
// store operating system / platform
#if os(iOS)
let OS = "iOS"
#elseif os(OSX)
let OS = "OSX"
#elseif os(watchOS)
let OS = "watchOS"
#elseif os(tvOS)
let OS = "tvOS"
#elseif os(Linux)
let OS = "Linux"
#elseif os(FreeBSD)
let OS = "FreeBSD"
#elseif os(Windows)
let OS = "Windows"
#elseif os(Android)
let OS = "Android"
#else
let OS = "Unknown"
#endif
/// destination which all others inherit from. do not directly use
open class BaseDestination: Hashable, Equatable {
/// output format pattern, see documentation for syntax
open var format = "$DHH:mm:ss.SSS$d $C$L$c $N.$F:$l - $M"
/// runs in own serial background thread for better performance
open var asynchronously = true
/// do not log any message which has a lower level than this one
open var minLevel = SwiftyBeaver.Level.verbose
/// set custom log level words for each level
open var levelString = LevelString()
/// set custom log level colors for each level
open var levelColor = LevelColor()
public struct LevelString {
public var verbose = "VERBOSE"
public var debug = "DEBUG"
public var info = "INFO"
public var warning = "WARNING"
public var error = "ERROR"
}
// For a colored log level word in a logged line
// empty on default
public struct LevelColor {
public var verbose = "" // silver
public var debug = "" // green
public var info = "" // blue
public var warning = "" // yellow
public var error = "" // red
}
var reset = ""
var escape = ""
var filters = [FilterType]()
let formatter = DateFormatter()
let startDate = Date()
// each destination class must have an own hashValue Int
#if swift(>=4.2)
public func hash(into hasher: inout Hasher) {
hasher.combine(defaultHashValue)
}
#else
lazy public var hashValue: Int = self.defaultHashValue
#endif
open var defaultHashValue: Int {return 0}
// each destination instance must have an own serial queue to ensure serial output
// GCD gives it a prioritization between User Initiated and Utility
var queue: DispatchQueue? //dispatch_queue_t?
var debugPrint = false // set to true to debug the internal filter logic of the class
public init() {
let uuid = NSUUID().uuidString
let queueLabel = "swiftybeaver-queue-" + uuid
queue = DispatchQueue(label: queueLabel, target: queue)
}
/// send / store the formatted log message to the destination
/// returns the formatted log message for processing by inheriting method
/// and for unit tests (nil if error)
open func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String,
function: String, line: Int, context: Any? = nil) -> String? {
if format.hasPrefix("$J") {
return messageToJSON(level, msg: msg, thread: thread,
file: file, function: function, line: line, context: context)
} else {
return formatMessage(format, level: level, msg: msg, thread: thread,
file: file, function: function, line: line, context: context)
}
}
public func execute(synchronously: Bool, block: @escaping () -> Void) {
guard let queue = queue else {
fatalError("Queue not set")
}
if synchronously {
queue.sync(execute: block)
} else {
queue.async(execute: block)
}
}
public func executeSynchronously<T>(block: @escaping () throws -> T) rethrows -> T {
guard let queue = queue else {
fatalError("Queue not set")
}
return try queue.sync(execute: block)
}
////////////////////////////////
// MARK: Format
////////////////////////////////
/// returns (padding length value, offset in string after padding info)
private func parsePadding(_ text: String) -> (Int, Int) {
// look for digits followed by a alpha character
var s: String!
var sign: Int = 1
if text.firstChar == "-" {
sign = -1
s = String(text.suffix(from: text.index(text.startIndex, offsetBy: 1)))
} else {
s = text
}
let numStr = s.prefix { $0 >= "0" && $0 <= "9" }
if let num = Int(String(numStr)) {
return (sign * num, (sign == -1 ? 1 : 0) + numStr.count)
} else {
return (0, 0)
}
}
private func paddedString(_ text: String, _ toLength: Int, truncating: Bool = false) -> String {
if toLength > 0 {
// Pad to the left of the string
if text.count > toLength {
// Hm... better to use suffix or prefix?
return truncating ? String(text.suffix(toLength)) : text
} else {
return "".padding(toLength: toLength - text.count, withPad: " ", startingAt: 0) + text
}
} else if toLength < 0 {
// Pad to the right of the string
let maxLength = truncating ? -toLength : max(-toLength, text.count)
return text.padding(toLength: maxLength, withPad: " ", startingAt: 0)
} else {
return text
}
}
/// returns the log message based on the format pattern
func formatMessage(_ format: String, level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String {
var text = ""
// Prepend a $I for 'ignore' or else the first character is interpreted as a format character
// even if the format string did not start with a $.
let phrases: [String] = ("$I" + format).components(separatedBy: "$")
for phrase in phrases where !phrase.isEmpty {
let (padding, offset) = parsePadding(phrase)
let formatCharIndex = phrase.index(phrase.startIndex, offsetBy: offset)
let formatChar = phrase[formatCharIndex]
let rangeAfterFormatChar = phrase.index(formatCharIndex, offsetBy: 1)..<phrase.endIndex
let remainingPhrase = phrase[rangeAfterFormatChar]
switch formatChar {
case "I": // ignore
text += remainingPhrase
case "L":
text += paddedString(levelWord(level), padding) + remainingPhrase
case "M":
text += paddedString(msg, padding) + remainingPhrase
case "T":
text += paddedString(thread, padding) + remainingPhrase
case "N":
// name of file without suffix
text += paddedString(fileNameWithoutSuffix(file), padding) + remainingPhrase
case "n":
// name of file with suffix
text += paddedString(fileNameOfFile(file), padding) + remainingPhrase
case "F":
text += paddedString(function, padding) + remainingPhrase
case "l":
text += paddedString(String(line), padding) + remainingPhrase
case "D":
// start of datetime format
#if swift(>=3.2)
text += paddedString(formatDate(String(remainingPhrase)), padding)
#else
text += paddedString(formatDate(remainingPhrase), padding)
#endif
case "d":
text += remainingPhrase
case "U":
text += paddedString(uptime(), padding) + remainingPhrase
case "Z":
// start of datetime format in UTC timezone
#if swift(>=3.2)
text += paddedString(formatDate(String(remainingPhrase), timeZone: "UTC"), padding)
#else
text += paddedString(formatDate(remainingPhrase, timeZone: "UTC"), padding)
#endif
case "z":
text += remainingPhrase
case "C":
// color code ("" on default)
text += escape + colorForLevel(level) + remainingPhrase
case "c":
text += reset + remainingPhrase
case "X":
// add the context
if let cx = context {
text += paddedString(String(describing: cx).trimmingCharacters(in: .whitespacesAndNewlines), padding) + remainingPhrase
} else {
text += paddedString("", padding) + remainingPhrase
}
default:
text += phrase
}
}
// right trim only
return text.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
}
/// returns the log payload as optional JSON string
func messageToJSON(_ level: SwiftyBeaver.Level, msg: String,
thread: String, file: String, function: String, line: Int, context: Any? = nil) -> String? {
var dict: [String: Any] = [
"timestamp": Date().timeIntervalSince1970,
"level": level.rawValue,
"message": msg,
"thread": thread,
"file": file,
"function": function,
"line": line
]
if let cx = context {
dict["context"] = cx
}
return jsonStringFromDict(dict)
}
/// returns the string of a level
func levelWord(_ level: SwiftyBeaver.Level) -> String {
var str = ""
switch level {
case .debug:
str = levelString.debug
case .info:
str = levelString.info
case .warning:
str = levelString.warning
case .error:
str = levelString.error
default:
// Verbose is default
str = levelString.verbose
}
return str
}
/// returns color string for level
func colorForLevel(_ level: SwiftyBeaver.Level) -> String {
var color = ""
switch level {
case .debug:
color = levelColor.debug
case .info:
color = levelColor.info
case .warning:
color = levelColor.warning
case .error:
color = levelColor.error
default:
color = levelColor.verbose
}
return color
}
/// returns the filename of a path
func fileNameOfFile(_ file: String) -> String {
let fileParts = file.components(separatedBy: "/")
if let lastPart = fileParts.last {
return lastPart
}
return ""
}
/// returns the filename without suffix (= file ending) of a path
func fileNameWithoutSuffix(_ file: String) -> String {
let fileName = fileNameOfFile(file)
if !fileName.isEmpty {
let fileNameParts = fileName.components(separatedBy: ".")
if let firstPart = fileNameParts.first {
return firstPart
}
}
return ""
}
/// returns a formatted date string
/// optionally in a given abbreviated timezone like "UTC"
func formatDate(_ dateFormat: String, timeZone: String = "") -> String {
if !timeZone.isEmpty {
formatter.timeZone = TimeZone(abbreviation: timeZone)
}
formatter.dateFormat = dateFormat
//let dateStr = formatter.string(from: NSDate() as Date)
let dateStr = formatter.string(from: Date())
return dateStr
}
/// returns a uptime string
func uptime() -> String {
let interval = Date().timeIntervalSince(startDate)
let hours = Int(interval) / 3600
let minutes = Int(interval / 60) - Int(hours * 60)
let seconds = Int(interval) - (Int(interval / 60) * 60)
let milliseconds = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
return String(format: "%0.2d:%0.2d:%0.2d.%03d", arguments: [hours, minutes, seconds, milliseconds])
}
/// returns the json-encoded string value
/// after it was encoded by jsonStringFromDict
func jsonStringValue(_ jsonString: String?, key: String) -> String {
guard let str = jsonString else {
return ""
}
// remove the leading {"key":" from the json string and the final }
let offset = key.length + 5
let endIndex = str.index(str.startIndex,
offsetBy: str.length - 2)
let range = str.index(str.startIndex, offsetBy: offset)..<endIndex
#if swift(>=3.2)
return String(str[range])
#else
return str[range]
#endif
}
/// turns dict into JSON-encoded string
func jsonStringFromDict(_ dict: [String: Any]) -> String? {
var jsonString: String?
// try to create JSON string
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
jsonString = String(data: jsonData, encoding: .utf8)
} catch {
print("SwiftyBeaver could not create JSON from dict.")
}
return jsonString
}
////////////////////////////////
// MARK: Filters
////////////////////////////////
/// Add a filter that determines whether or not a particular message will be logged to this destination
public func addFilter(_ filter: FilterType) {
filters.append(filter)
}
/// Remove a filter from the list of filters
public func removeFilter(_ filter: FilterType) {
#if swift(>=5)
let index = filters.firstIndex {
return ObjectIdentifier($0) == ObjectIdentifier(filter)
}
#else
let index = filters.index {
return ObjectIdentifier($0) == ObjectIdentifier(filter)
}
#endif
guard let filterIndex = index else {
return
}
filters.remove(at: filterIndex)
}
/// Answer whether the destination has any message filters
/// returns boolean and is used to decide whether to resolve
/// the message before invoking shouldLevelBeLogged
func hasMessageFilters() -> Bool {
return !getFiltersTargeting(Filter.TargetType.Message(.Equals([], true)),
fromFilters: self.filters).isEmpty
}
/// checks if level is at least minLevel or if a minLevel filter for that path does exist
/// returns boolean and can be used to decide if a message should be logged or not
func shouldLevelBeLogged(_ level: SwiftyBeaver.Level, path: String,
function: String, message: String? = nil) -> Bool {
if filters.isEmpty {
if level.rawValue >= minLevel.rawValue {
if debugPrint {
print("filters are empty and level >= minLevel")
}
return true
} else {
if debugPrint {
print("filters are empty and level < minLevel")
}
return false
}
}
let filterCheckResult = FilterValidator.validate(input: .init(filters: self.filters, level: level, path: path, function: function, message: message))
// Exclusion filters match if they do NOT meet the filter condition (see Filter.apply(_:) method)
switch filterCheckResult[.excluded] {
case .some(.someFiltersMatch):
// Exclusion filters are present and at least one of them matches the log entry
if debugPrint {
print("filters are not empty and message was excluded")
}
return false
case .some(.allFiltersMatch), .some(.noFiltersMatchingType), .none: break
}
// If required filters exist, we should validate or invalidate the log if all of them pass or not
switch filterCheckResult[.required] {
case .some(.allFiltersMatch): return true
case .some(.someFiltersMatch): return false
case .some(.noFiltersMatchingType), .none: break
}
let checkLogLevel: () -> Bool = {
// Check if the log message's level matches or exceeds the minLevel of the destination
return level.rawValue >= self.minLevel.rawValue
}
// Non-required filters should only be applied if the log entry matches the filter condition (e.g. path)
switch filterCheckResult[.nonRequired] {
case .some(.allFiltersMatch): return true
case .some(.noFiltersMatchingType), .none: return checkLogLevel()
case .some(.someFiltersMatch(let partialMatchData)):
if partialMatchData.fullMatchCount > 0 {
// The log entry matches at least one filter condition and the destination's log level
return true
} else if partialMatchData.conditionMatchCount > 0 {
// The log entry matches at least one filter condition, but does not match or exceed the destination's log level
return false
} else {
// There is no filter with a matching filter condition. Check the destination's log level
return checkLogLevel()
}
}
}
func getFiltersTargeting(_ target: Filter.TargetType, fromFilters: [FilterType]) -> [FilterType] {
return fromFilters.filter { filter in
return filter.getTarget() == target
}
}
/**
Triggered by main flush() method on each destination. Runs in background thread.
Use for destinations that buffer log items, implement this function to flush those
buffers to their final destination (web server...)
*/
func flush() {
// no implementation in base destination needed
}
}
public func == (lhs: BaseDestination, rhs: BaseDestination) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
+72
View File
@@ -0,0 +1,72 @@
//
// ConsoleDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
public class ConsoleDestination: BaseDestination {
/// use NSLog instead of print, default is false
public var useNSLog = false
/// uses colors compatible to Terminal instead of Xcode, default is false
public var useTerminalColors: Bool = false {
didSet {
if useTerminalColors {
// use Terminal colors
reset = "\u{001b}[0m"
escape = "\u{001b}[38;5;"
levelColor.verbose = "251m" // silver
levelColor.debug = "35m" // green
levelColor.info = "38m" // blue
levelColor.warning = "178m" // yellow
levelColor.error = "197m" // red
} else {
// use colored Emojis for better visual distinction
// of log level for Xcode 8
levelColor.verbose = "💜 " // silver
levelColor.debug = "💚 " // green
levelColor.info = "💙 " // blue
levelColor.warning = "💛 " // yellow
levelColor.error = "❤️ " // red
}
}
}
override public var defaultHashValue: Int { return 1 }
public override init() {
super.init()
levelColor.verbose = "💜 " // silver
levelColor.debug = "💚 " // green
levelColor.info = "💙 " // blue
levelColor.warning = "💛 " // yellow
levelColor.error = "❤️ " // red
}
// print to Xcode Console. uses full base class functionality
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
if let str = formattedString {
if useNSLog {
#if os(Linux)
print(str)
#else
NSLog("%@", str)
#endif
} else {
print(str)
}
}
return formattedString
}
}
+35
View File
@@ -0,0 +1,35 @@
//
// Extensions.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 13.12.17.
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
extension String {
/// cross-Swift compatible characters count
var length: Int {
return self.count
}
/// cross-Swift-compatible first character
var firstChar: Character? {
return self.first
}
/// cross-Swift-compatible last character
var lastChar: Character? {
return self.last
}
/// cross-Swift-compatible index
func find(_ char: Character) -> Index? {
#if swift(>=5)
return self.firstIndex(of: char)
#else
return self.index(of: char)
#endif
}
}
+169
View File
@@ -0,0 +1,169 @@
//
// FileDestination.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 05.12.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
public class FileDestination: BaseDestination {
public var logFileURL: URL?
public var syncAfterEachWrite: Bool = false
public var colored: Bool = false {
didSet {
if colored {
// bash font color, first value is intensity, second is color
// see http://bit.ly/1Otu3Zr & for syntax http://bit.ly/1Tp6Fw9
// uses the 256-color table from http://bit.ly/1W1qJuH
reset = "\u{001b}[0m"
escape = "\u{001b}[38;5;"
levelColor.verbose = "251m" // silver
levelColor.debug = "35m" // green
levelColor.info = "38m" // blue
levelColor.warning = "178m" // yellow
levelColor.error = "197m" // red
} else {
reset = ""
escape = ""
levelColor.verbose = ""
levelColor.debug = ""
levelColor.info = ""
levelColor.warning = ""
levelColor.error = ""
}
}
}
override public var defaultHashValue: Int {return 2}
let fileManager = FileManager.default
public init(logFileURL: URL? = nil) {
if let logFileURL = logFileURL {
self.logFileURL = logFileURL
super.init()
return
}
// platform-dependent logfile directory default
var baseURL: URL?
#if os(OSX)
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
// try to use ~/Library/Caches/APP NAME instead of ~/Library/Caches
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
do {
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
try fileManager.createDirectory(at: appURL,
withIntermediateDirectories: true, attributes: nil)
baseURL = appURL
}
} catch {
print("Warning! Could not create folder /Library/Caches/\(appName)")
}
}
}
#else
#if os(Linux)
baseURL = URL(fileURLWithPath: "/var/cache")
#else
// iOS, watchOS, etc. are using the caches directory
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
}
#endif
#endif
if let baseURL = baseURL {
self.logFileURL = baseURL.appendingPathComponent("swiftybeaver.log", isDirectory: false)
}
super.init()
}
// append to file. uses full base class functionality
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line, context: context)
if let str = formattedString {
_ = saveToFile(str: str)
}
return formattedString
}
/// appends a string as line to a file.
/// returns boolean about success
func saveToFile(str: String) -> Bool {
guard let url = logFileURL else { return false }
let line = str + "\n"
guard let data = line.data(using: String.Encoding.utf8) else { return false }
do {
if fileManager.fileExists(atPath: url.path) == false {
let directoryURL = url.deletingLastPathComponent()
if fileManager.fileExists(atPath: directoryURL.path) == false {
try fileManager.createDirectory(
at: directoryURL,
withIntermediateDirectories: true
)
}
fileManager.createFile(atPath: url.path, contents: nil)
#if os(iOS) || os(watchOS)
if #available(iOS 10.0, watchOS 3.0, *) {
var attributes = try fileManager.attributesOfItem(atPath: url.path)
attributes[FileAttributeKey.protectionKey] = FileProtectionType.none
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
}
#endif
}
write(data: data, to: url)
return true
} catch {
print("SwiftyBeaver File Destination could not write to file \(url).")
return false
}
}
private func write(data: Data, to url: URL) {
let coordinator = NSFileCoordinator(filePresenter: nil)
var error: NSError?
coordinator.coordinate(writingItemAt: url, error: &error) { url in
do {
let fileHandle = try FileHandle(forWritingTo: url)
fileHandle.seekToEndOfFile()
fileHandle.write(data)
if syncAfterEachWrite {
fileHandle.synchronizeFile()
}
fileHandle.closeFile()
} catch {
print("SwiftyBeaver File Destination could not write to file \(url).")
return
}
}
if let error = error {
print("Failed writing file with error: \(String(describing: error))")
}
}
/// deletes log file.
/// returns true if file was removed or does not exist, false otherwise
public func deleteLogFile() -> Bool {
guard let url = logFileURL, fileManager.fileExists(atPath: url.path) == true else { return true }
do {
try fileManager.removeItem(at: url)
return true
} catch {
print("SwiftyBeaver File Destination could not remove file \(url).")
return false
}
}
}
+283
View File
@@ -0,0 +1,283 @@
//
// Filter.swift
// SwiftyBeaver
//
// Created by Jeff Roberts on 5/31/16.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
/// FilterType is a protocol that describes something that determines
/// whether or not a message gets logged. A filter answers a Bool when it
/// is applied to a value. If the filter passes, it shall return true,
/// false otherwise.
///
/// A filter must contain a target, which identifies what it filters against
/// A filter can be required meaning that all required filters against a specific
/// target must pass in order for the message to be logged.
public protocol FilterType : class {
func apply(_ value: Any) -> Bool
func getTarget() -> Filter.TargetType
func isRequired() -> Bool
func isExcluded() -> Bool
func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool
}
/// Filters is syntactic sugar used to easily construct filters
public class Filters {
public static let Path = PathFilterFactory.self
public static let Function = FunctionFilterFactory.self
public static let Message = MessageFilterFactory.self
}
/// Filter is an abstract base class for other filters
public class Filter {
public enum TargetType {
case Path(Filter.ComparisonType)
case Function(Filter.ComparisonType)
case Message(Filter.ComparisonType)
}
public enum ComparisonType {
case StartsWith([String], Bool)
case Contains([String], Bool)
case Excludes([String], Bool)
case EndsWith([String], Bool)
case Equals([String], Bool)
case Custom((String) -> Bool)
}
let targetType: Filter.TargetType
let required: Bool
let minLevel: SwiftyBeaver.Level
public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
self.targetType = target
self.required = required
self.minLevel = minLevel
}
public func getTarget() -> Filter.TargetType {
return self.targetType
}
public func isRequired() -> Bool {
return self.required
}
public func isExcluded() -> Bool {
return false
}
/// returns true of set minLevel is >= as given level
public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool {
//print("checking if given level \(level) >= \(minLevel)")
return level.rawValue >= minLevel.rawValue
}
}
/// CompareFilter is a FilterType that can filter based upon whether a target
/// starts with, contains or ends with a specific string. CompareFilters can be
/// case sensitive.
public class CompareFilter: Filter, FilterType {
private var filterComparisonType: Filter.ComparisonType?
override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
super.init(target, required: required, minLevel: minLevel)
let comparisonType: Filter.ComparisonType?
switch self.getTarget() {
case let .Function(comparison):
comparisonType = comparison
case let .Path(comparison):
comparisonType = comparison
case let .Message(comparison):
comparisonType = comparison
/*default:
comparisonType = nil*/
}
self.filterComparisonType = comparisonType
}
public func apply(_ value: Any) -> Bool {
guard let value = value as? String else {
return false
}
guard let filterComparisonType = self.filterComparisonType else {
return false
}
let matches: Bool
switch filterComparisonType {
case let .Contains(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.contains(string) :
value.lowercased().contains(string.lowercased())
}.isEmpty
case let .Excludes(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? !value.contains(string) :
!value.lowercased().contains(string.lowercased())
}.isEmpty
case let .StartsWith(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.hasPrefix(string) :
value.lowercased().hasPrefix(string.lowercased())
}.isEmpty
case let .EndsWith(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value.hasSuffix(string) :
value.lowercased().hasSuffix(string.lowercased())
}.isEmpty
case let .Equals(strings, caseSensitive):
matches = !strings.filter { string in
return caseSensitive ? value == string :
value.lowercased() == string.lowercased()
}.isEmpty
case let .Custom(predicate):
matches = predicate(value)
}
return matches
}
override public func isExcluded() -> Bool {
guard let filterComparisonType = self.filterComparisonType else { return false }
switch filterComparisonType {
case .Excludes:
return true
default:
return false
}
}
}
// Syntactic sugar for creating a function comparison filter
public class FunctionFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
// Syntactic sugar for creating a message comparison filter
public class MessageFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
// Syntactic sugar for creating a path comparison filter
public class PathFilterFactory {
public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func contains(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func excludes(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
}
public static func equals(_ strings: String..., caseSensitive: Bool = false,
required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
}
public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel)
}
}
extension Filter.TargetType: Equatable {
}
// The == does not compare associated values for each enum. Instead == evaluates to true
// if both enums are the same "types", ignoring the associated values of each enum
public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
switch (lhs, rhs) {
case (.Path, .Path):
return true
case (.Function, .Function):
return true
case (.Message, .Message):
return true
default:
return false
}
}
+129
View File
@@ -0,0 +1,129 @@
//
// FilterValidator.swift
// SwiftyBeaver (iOS)
//
// Created by Felix Lisczyk on 07.07.19.
// Copyright © 2019 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
/// FilterValidator is a utility class used by BaseDestination.
/// It encapsulates the filtering logic for excluded, required
/// and non-required filters.
///
/// FilterValidator evaluates a set of filters for a single log
/// entry. It determines if these filters apply to the log entry
/// based on their condition (path, function, message) and their
/// minimum log level.
struct FilterValidator {
// These are the different filter types that the user can set
enum ValidationType: CaseIterable {
case excluded
case required
case nonRequired
func apply(to filters: [FilterType]) -> [FilterType] {
switch self {
case .excluded:
return filters.filter { $0.isExcluded() }
case .required:
return filters.filter { $0.isRequired() && !$0.isExcluded() }
case .nonRequired:
return filters.filter { !$0.isRequired() && !$0.isExcluded() }
}
}
}
// Wrapper object for input parameters
struct Input {
let filters: [FilterType]
let level: SwiftyBeaver.Level
let path: String
let function: String
let message: String?
}
// Result wrapper object
enum Result {
case allFiltersMatch // All filters fully match the log entry (condition + minimum log level)
case someFiltersMatch(PartialMatchData) // Only some filters fully match the log entry (condition + minimum log level)
case noFiltersMatchingType // There are no filters set for a particular type (excluded, required, nonRequired)
struct PartialMatchData {
let fullMatchCount: Int // Number of filters that match both the condition and the minimum log level of the log entry
let conditionMatchCount: Int // Number of filters that match ONLY the condition of the log entry (path, function, message)
let logLevelMatchCount: Int // Number of filters that match ONLY the minimum log level of the log entry
}
}
static func validate(input: Input, for types: [ValidationType] = ValidationType.allCases) -> [ValidationType: Result] {
var results = [ValidationType: Result]()
for type in types {
let filtersToValidate = type.apply(to: input.filters)
if filtersToValidate.isEmpty {
// There are no filters set for this particular type
results[type] = .noFiltersMatchingType
} else {
var fullMatchCount: Int = 0
var conditionMatchCount: Int = 0
var logLevelMatchCount: Int = 0
for filter in filtersToValidate {
let filterMatchesCondition = self.filterMatchesCondition(filter, level: input.level, path: input.path, function: input.function, message: input.message)
let filterMatchesMinLogLevel = self.filterMatchesMinLogLevel(filter, level: input.level)
switch (filterMatchesCondition, filterMatchesMinLogLevel) {
// Filter matches both the condition and the minimum log level
case (true, true): fullMatchCount += 1
// Filter matches only the condition (path, function, message)
case (true, false): conditionMatchCount += 1
// Filter matches only the minimum log level
case (false, true): logLevelMatchCount += 1
// Filter does not match the condition nor the minimum log level
case (false, false): break
}
}
if filtersToValidate.count == fullMatchCount {
// All filters fully match the log entry
results[type] = .allFiltersMatch
} else {
// Only some filters match the log entry
results[type] = .someFiltersMatch(.init(fullMatchCount: fullMatchCount, conditionMatchCount: conditionMatchCount, logLevelMatchCount: logLevelMatchCount))
}
}
}
return results
}
private static func filterMatchesCondition(_ filter: FilterType, level: SwiftyBeaver.Level,
path: String, function: String, message: String?) -> Bool {
let passes: Bool
switch filter.getTarget() {
case .Path(_):
passes = filter.apply(path)
case .Function(_):
passes = filter.apply(function)
case .Message(_):
guard let message = message else {
return false
}
passes = filter.apply(message)
}
return passes
}
private static func filterMatchesMinLogLevel(_ filter: FilterType, level: SwiftyBeaver.Level) -> Bool {
return filter.reachedMinLevel(level)
}
}
+97
View File
@@ -0,0 +1,97 @@
//
// GoogleCloudDestination.swift
// SwiftyBeaver
//
// Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
//
import Foundation
public final class GoogleCloudDestination: BaseDestination {
private let serviceName: String
public init(serviceName: String) {
self.serviceName = serviceName
super.init()
}
override public var asynchronously: Bool {
get {
return false
}
set {
return
}
}
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
let reportLocation: [String: Any] = ["filePath": file, "lineNumber": line, "functionName": function]
var gcpContext: [String: Any] = ["reportLocation": reportLocation]
if let context = context as? [String: Any] {
if let httpRequestContext = context["httpRequest"] as? [String: Any] {
gcpContext["httpRequest"] = httpRequestContext
}
if let user = context["user"] as? String {
gcpContext["user"] = user
}
}
let gcpJSON: [String: Any] = [
"serviceContext": [
"service": serviceName
],
"message": msg,
"severity": level.severity,
"context": gcpContext
]
let finalLogString: String
do {
finalLogString = try jsonString(obj: gcpJSON)
} catch {
let uncrashableLogString = "{\"context\":{\"reportLocation\":{\"filePath\": \"\(file)\"" +
",\"functionName\":\"\(function)\"" +
",\"lineNumber\":\(line)},\"severity\"" +
":\"CRITICAL\",\"message\":\"Error encoding " +
"JSON log entry. You may be losing log messages!\"}"
finalLogString = uncrashableLogString.description
}
print(finalLogString)
return finalLogString
}
private func jsonString(obj: Dictionary<String, Any>) throws -> String {
let json = try JSONSerialization.data(withJSONObject: obj, options: [])
guard let string = String(data: json, encoding: .utf8) else {
throw GCPError.serialization
}
return string
}
}
///
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
extension SwiftyBeaver.Level {
/// Verbose is reported as Debug to GCP.
/// Recommend you don't bother using it.
var severity: String {
switch self {
// There is only one level below "Debug": "Default", which becomes "Any" and is considered as a potential error as well
case .verbose: return "DEBUG"
case .debug: return "DEBUG"
case .info: return "INFO"
case .warning: return "WARNING"
case .error: return "ERROR"
}
}
}
private enum GCPError: Error {
case serialization
}
+625
View File
@@ -0,0 +1,625 @@
//
// SBPlatformDestination
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger on 22.01.16.
// Copyright © 2016 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
// platform-dependent import frameworks to get device details
// valid values for os(): OSX, iOS, watchOS, tvOS, Linux
// in Swift 3 the following were added: FreeBSD, Windows, Android
#if os(iOS) || os(tvOS) || os(watchOS)
import UIKit
var DEVICE_MODEL: String {
get {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
}
#else
let DEVICE_MODEL = ""
#endif
#if os(iOS) || os(tvOS)
var DEVICE_NAME = UIDevice.current.name
#else
// under watchOS UIDevice is not existing, http://apple.co/26ch5J1
let DEVICE_NAME = ""
#endif
public class SBPlatformDestination: BaseDestination {
public var appID = ""
public var appSecret = ""
public var encryptionKey = ""
public var analyticsUserName = "" // user email, ID, name, etc.
public var analyticsUUID: String { return uuid }
// when to send to server
public struct SendingPoints {
public var verbose = 0
public var debug = 1
public var info = 5
public var warning = 8
public var error = 10
public var threshold = 10 // send to server if points reach that value
}
public var sendingPoints = SendingPoints()
public var showNSLog = false // executes toNSLog statements to debug the class
var points = 0
public var serverURL = URL(string: "https://api.swiftybeaver.com/api/entries/") // optional
public var entriesFileURL = URL(fileURLWithPath: "") // not optional
public var sendingFileURL = URL(fileURLWithPath: "")
public var analyticsFileURL = URL(fileURLWithPath: "")
private let minAllowedThreshold = 1 // over-rules SendingPoints.Threshold
private let maxAllowedThreshold = 1000 // over-rules SendingPoints.Threshold
private var sendingInProgress = false
private var initialSending = true
// analytics
var uuid = ""
// destination
override public var defaultHashValue: Int {return 3}
let fileManager = FileManager.default
let isoDateFormatter = DateFormatter()
/// init platform with default internal filenames
public init(appID: String, appSecret: String, encryptionKey: String,
serverURL: URL? = URL(string: "https://api.swiftybeaver.com/api/entries/"),
entriesFileName: String = "sbplatform_entries.json",
sendingfileName: String = "sbplatform_entries_sending.json",
analyticsFileName: String = "sbplatform_analytics.json") {
super.init()
self.serverURL = serverURL
self.appID = appID
self.appSecret = appSecret
self.encryptionKey = encryptionKey
// setup where to write the json files
var baseURL: URL?
#if os(OSX)
if let url = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
baseURL = url
// try to use ~/Library/Application Support/APP NAME instead of ~/Library/Application Support
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String {
do {
if let appURL = baseURL?.appendingPathComponent(appName, isDirectory: true) {
try fileManager.createDirectory(at: appURL,
withIntermediateDirectories: true, attributes: nil)
baseURL = appURL
}
} catch {
// it is too early in the class lifetime to be able to use toNSLog()
print("Warning! Could not create folder ~/Library/Application Support/\(appName).")
}
}
}
#else
#if os(tvOS)
// tvOS can just use the caches directory
if let url = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first {
baseURL = url
}
#elseif os(Linux)
// Linux is using /var/cache
let baseDir = "/var/cache/"
entriesFileURL = URL(fileURLWithPath: baseDir + entriesFileName)
sendingFileURL = URL(fileURLWithPath: baseDir + sendingfileName)
analyticsFileURL = URL(fileURLWithPath: baseDir + analyticsFileName)
#else
// iOS and watchOS are using the app’s document directory
if let url = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
baseURL = url
}
#endif
#endif
#if os(Linux)
// get, update loaded and save analytics data to file on start
let dict = analytics(analyticsFileURL, update: true)
_ = saveDictToFile(dict, url: analyticsFileURL)
#else
if let baseURL = baseURL {
// is just set for everything but not Linux
entriesFileURL = baseURL.appendingPathComponent(entriesFileName,
isDirectory: false)
sendingFileURL = baseURL.appendingPathComponent(sendingfileName,
isDirectory: false)
analyticsFileURL = baseURL.appendingPathComponent(analyticsFileName,
isDirectory: false)
// get, update loaded and save analytics data to file on start
let dict = analytics(analyticsFileURL, update: true)
_ = saveDictToFile(dict, url: analyticsFileURL)
}
#endif
}
// append to file, each line is a JSON dict
override public func send(_ level: SwiftyBeaver.Level, msg: String, thread: String,
file: String, function: String, line: Int, context: Any? = nil) -> String? {
var jsonString: String?
let dict: [String: Any] = [
"timestamp": Date().timeIntervalSince1970,
"level": level.rawValue,
"message": msg,
"thread": thread,
"fileName": file.components(separatedBy: "/").last!,
"function": function,
"line": line]
jsonString = jsonStringFromDict(dict)
if let str = jsonString {
toNSLog("saving '\(msg)' to \(entriesFileURL)")
_ = saveToFile(str, url: entriesFileURL)
//toNSLog(entriesFileURL.path!)
// now decide if the stored log entries should be sent to the server
// add level points to current points amount and send to server if threshold is hit
let newPoints = sendingPointsForLevel(level)
points += newPoints
toNSLog("current sending points: \(points)")
if (points >= sendingPoints.threshold && points >= minAllowedThreshold) || points > maxAllowedThreshold {
toNSLog("\(points) points is >= threshold")
// above threshold, send to server
sendNow()
} else if initialSending {
initialSending = false
// first logging at this session
// send if json file still contains old log entries
if let logEntries = logsFromFile(entriesFileURL) {
let lines = logEntries.count
if lines > 1 {
var msg = "initialSending: \(points) points is below threshold "
msg += "but json file already has \(lines) lines."
toNSLog(msg)
sendNow()
}
}
}
}
return jsonString
}
// MARK: Send-to-Server Logic
/// does a (manual) sending attempt of all unsent log entries to SwiftyBeaver Platform
public func sendNow() {
if sendFileExists() {
toNSLog("reset points to 0")
points = 0
} else {
if !renameJsonToSendFile() {
return
}
}
if !sendingInProgress {
sendingInProgress = true
//let (jsonString, lines) = logsFromFile(sendingFileURL)
var lines = 0
guard let logEntries = logsFromFile(sendingFileURL) else {
sendingInProgress = false
return
}
lines = logEntries.count
if lines > 0 {
var payload = [String: Any]()
// merge device and analytics dictionaries
let deviceDetailsDict = deviceDetails()
var analyticsDict = analytics(analyticsFileURL)
for key in deviceDetailsDict.keys {
analyticsDict[key] = deviceDetailsDict[key]
}
payload["device"] = analyticsDict
payload["entries"] = logEntries
if let str = jsonStringFromDict(payload) {
//toNSLog(str) // uncomment to see full payload
toNSLog("Encrypting \(lines) log entries ...")
if let encryptedStr = encrypt(str) {
var msg = "Sending \(lines) encrypted log entries "
msg += "(\(encryptedStr.length) chars) to server ..."
toNSLog(msg)
sendToServerAsync(encryptedStr) { ok, _ in
self.toNSLog("Sent \(lines) encrypted log entries to server, received ok: \(ok)")
if ok {
_ = self.deleteFile(self.sendingFileURL)
}
self.sendingInProgress = false
self.points = 0
}
}
}
} else {
sendingInProgress = false
}
}
}
/// sends a string to the SwiftyBeaver Platform server, returns ok if status 200 and HTTP status
func sendToServerAsync(_ str: String?, complete: @escaping (_ ok: Bool, _ status: Int) -> Void) {
let timeout = 10.0
if let payload = str, let queue = self.queue, let serverURL = serverURL {
// create operation queue which uses current serial queue of destination
let operationQueue = OperationQueue()
operationQueue.underlyingQueue = queue
let session = URLSession(configuration:
URLSessionConfiguration.default,
delegate: nil, delegateQueue: operationQueue)
toNSLog("assembling request ...")
// assemble request
var request = URLRequest(url: serverURL,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: timeout)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// basic auth header (just works on Linux for Swift 3.1+, macOS is fine)
guard let credentials = "\(appID):\(appSecret)".data(using: String.Encoding.utf8) else {
toNSLog("Error! Could not set basic auth header")
return complete(false, 0)
}
#if os(Linux)
let base64Credentials = Base64.encode([UInt8](credentials))
#else
let base64Credentials = credentials.base64EncodedString(options: [])
#endif
request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
//toNSLog("\nrequest:")
//print(request)
// POST parameters
let params = ["payload": payload]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
} catch {
toNSLog("Error! Could not create JSON for server payload.")
return complete(false, 0)
}
toNSLog("sending params: \(params)")
toNSLog("sending ...")
sendingInProgress = true
// send request async to server on destination queue
let task = session.dataTask(with: request) { _, response, error in
var ok = false
var status = 0
self.toNSLog("received response from server")
if let error = error {
// an error did occur
self.toNSLog("Error! Could not send entries to server. \(error)")
} else {
if let response = response as? HTTPURLResponse {
status = response.statusCode
if status == 200 {
// all went well, entries were uploaded to server
ok = true
} else {
// status code was not 200
var msg = "Error! Sending entries to server failed "
msg += "with status code \(status)"
self.toNSLog(msg)
}
}
}
return complete(ok, status)
}
task.resume()
session.finishTasksAndInvalidate()
//while true {} // commenting this line causes a crash on Linux unit tests?!?
}
}
/// returns sending points based on level
func sendingPointsForLevel(_ level: SwiftyBeaver.Level) -> Int {
switch level {
case .debug:
return sendingPoints.debug
case .info:
return sendingPoints.info
case .warning:
return sendingPoints.warning
case .error:
return sendingPoints.error
default:
return sendingPoints.verbose
}
}
// MARK: File Handling
/// appends a string as line to a file.
/// returns boolean about success
func saveToFile(_ str: String, url: URL, overwrite: Bool = false) -> Bool {
do {
if fileManager.fileExists(atPath: url.path) == false || overwrite {
// create file if not existing
let line = str + "\n"
try line.write(to: url, atomically: true, encoding: String.Encoding.utf8)
} else {
// append to end of file
let fileHandle = try FileHandle(forWritingTo: url)
_ = fileHandle.seekToEndOfFile()
let line = str + "\n"
if let data = line.data(using: String.Encoding.utf8) {
fileHandle.write(data)
fileHandle.closeFile()
}
}
return true
} catch {
toNSLog("Error! Could not write to file \(url).")
return false
}
}
func sendFileExists() -> Bool {
return fileManager.fileExists(atPath: sendingFileURL.path)
}
func renameJsonToSendFile() -> Bool {
do {
try fileManager.moveItem(at: entriesFileURL, to: sendingFileURL)
return true
} catch {
toNSLog("SwiftyBeaver Platform Destination could not rename json file.")
return false
}
}
/// returns optional array of log dicts from a file which has 1 json string per line
func logsFromFile(_ url: URL) -> [[String: Any]]? {
var lines = 0
do {
// try to read file, decode every JSON line and put dict from each line in array
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
let linesArray = fileContent.components(separatedBy: "\n")
var dicts = [[String: Any]()] // array of dictionaries
for lineJSON in linesArray {
lines += 1
if lineJSON.firstChar == "{" && lineJSON.lastChar == "}" {
// try to parse json string into dict
if let data = lineJSON.data(using: .utf8) {
do {
if let dict = try JSONSerialization.jsonObject(with: data,
options: .mutableContainers) as? [String: Any] {
if !dict.isEmpty {
dicts.append(dict)
}
}
} catch {
var msg = "Error! Could not parse "
msg += "line \(lines) in file \(url)."
toNSLog(msg)
}
}
}
}
dicts.removeFirst()
return dicts
} catch {
toNSLog("Error! Could not read file \(url).")
}
return nil
}
/// returns AES-256 CBC encrypted optional string
func encrypt(_ str: String) -> String? {
return AES256CBC.encryptString(str, password: encryptionKey)
}
/// Delete file to get started again
func deleteFile(_ url: URL) -> Bool {
do {
try FileManager.default.removeItem(at: url)
return true
} catch {
toNSLog("Warning! Could not delete file \(url).")
}
return false
}
// MARK: Device & Analytics
// returns dict with device details. Amount depends on platform
func deviceDetails() -> [String: String] {
var details = [String: String]()
details["os"] = OS
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
// becomes for example 10.11.2 for El Capitan
var osVersionStr = String(osVersion.majorVersion)
osVersionStr += "." + String(osVersion.minorVersion)
osVersionStr += "." + String(osVersion.patchVersion)
details["osVersion"] = osVersionStr
details["hostName"] = ProcessInfo.processInfo.hostName
details["deviceName"] = ""
details["deviceModel"] = ""
if DEVICE_NAME != "" {
details["deviceName"] = DEVICE_NAME
}
if DEVICE_MODEL != "" {
details["deviceModel"] = DEVICE_MODEL
}
return details
}
/// returns (updated) analytics dict, optionally loaded from file.
func analytics(_ url: URL, update: Bool = false) -> [String: Any] {
var dict = [String: Any]()
let now = NSDate().timeIntervalSince1970
uuid = NSUUID().uuidString
dict["uuid"] = uuid
dict["firstStart"] = now
dict["lastStart"] = now
dict["starts"] = 1
dict["userName"] = analyticsUserName
dict["firstAppVersion"] = appVersion()
dict["appVersion"] = appVersion()
dict["firstAppBuild"] = appBuild()
dict["appBuild"] = appBuild()
if let loadedDict = dictFromFile(analyticsFileURL) {
if let val = loadedDict["firstStart"] as? Double {
dict["firstStart"] = val
}
if let val = loadedDict["lastStart"] as? Double {
if update {
dict["lastStart"] = now
} else {
dict["lastStart"] = val
}
}
if let val = loadedDict["starts"] as? Int {
if update {
dict["starts"] = val + 1
} else {
dict["starts"] = val
}
}
if let val = loadedDict["uuid"] as? String {
dict["uuid"] = val
uuid = val
}
if let val = loadedDict["userName"] as? String {
if update && !analyticsUserName.isEmpty {
dict["userName"] = analyticsUserName
} else {
if !val.isEmpty {
dict["userName"] = val
}
}
}
if let val = loadedDict["firstAppVersion"] as? String {
dict["firstAppVersion"] = val
}
if let val = loadedDict["firstAppBuild"] as? Int {
dict["firstAppBuild"] = val
}
}
return dict
}
/// Returns the current app version string (like 1.2.5) or empty string on error
func appVersion() -> String {
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
return version
}
return ""
}
/// Returns the current app build as integer (like 563, always incrementing) or 0 on error
func appBuild() -> Int {
if let version = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
if let intVersion = Int(version) {
return intVersion
}
}
return 0
}
/// returns optional dict from a json encoded file
func dictFromFile(_ url: URL) -> [String: Any]? {
do {
let fileContent = try String(contentsOfFile: url.path, encoding: .utf8)
if let data = fileContent.data(using: .utf8) {
return try JSONSerialization.jsonObject(with: data,
options: .mutableContainers) as? [String: Any]
}
} catch {
toNSLog("SwiftyBeaver Platform Destination could not read file \(url)")
}
return nil
}
// turns dict into JSON and saves it to file
func saveDictToFile(_ dict: [String: Any], url: URL) -> Bool {
let jsonString = jsonStringFromDict(dict)
if let str = jsonString {
toNSLog("saving '\(str)' to \(url)")
return saveToFile(str, url: url, overwrite: true)
}
return false
}
// MARK: Debug Helpers
/// log String to toNSLog. Used to debug the class logic
func toNSLog(_ str: String) {
if showNSLog {
#if os(Linux)
print("SBPlatform: \(str)")
#else
NSLog("SBPlatform: \(str)")
#endif
}
}
/// returns the current thread name
class func threadName() -> String {
#if os(Linux)
// on 9/30/2016 not yet implemented in server-side Swift:
// > import Foundation
// > Thread.isMainThread
return ""
#else
if Thread.isMainThread {
return ""
} else {
let threadName = Thread.current.name
if let threadName = threadName, !threadName.isEmpty {
return threadName
} else {
return String(format: "%p", Thread.current)
}
}
#endif
}
}
+212
View File
@@ -0,0 +1,212 @@
//
// SwiftyBeaver.swift
// SwiftyBeaver
//
// Created by Sebastian Kreutzberger (Twitter @skreutzb) on 28.11.15.
// Copyright © 2015 Sebastian Kreutzberger
// Some rights reserved: http://opensource.org/licenses/MIT
//
import Foundation
open class SwiftyBeaver {
/// version string of framework
public static let version = "1.9.0" // UPDATE ON RELEASE!
/// build number of framework
public static let build = 1900 // version 1.6.2 -> 1620, UPDATE ON RELEASE!
public enum Level: Int {
case verbose = 0
case debug = 1
case info = 2
case warning = 3
case error = 4
}
// a set of active destinations
public private(set) static var destinations = Set<BaseDestination>()
// MARK: Destination Handling
/// returns boolean about success
@discardableResult
open class func addDestination(_ destination: BaseDestination) -> Bool {
if destinations.contains(destination) {
return false
}
destinations.insert(destination)
return true
}
/// returns boolean about success
@discardableResult
open class func removeDestination(_ destination: BaseDestination) -> Bool {
if destinations.contains(destination) == false {
return false
}
destinations.remove(destination)
return true
}
/// if you need to start fresh
open class func removeAllDestinations() {
destinations.removeAll()
}
/// returns the amount of destinations
open class func countDestinations() -> Int {
return destinations.count
}
/// returns the current thread name
class func threadName() -> String {
#if os(Linux)
// on 9/30/2016 not yet implemented in server-side Swift:
// > import Foundation
// > Thread.isMainThread
return ""
#else
if Thread.isMainThread {
return ""
} else {
let name = __dispatch_queue_get_label(nil)
return String(cString: name, encoding: .utf8) ?? Thread.current.description
}
#endif
}
// MARK: Levels
/// log something generally unimportant (lowest priority)
open class func verbose(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .verbose, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .verbose, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which help during debugging (low priority)
open class func debug(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .debug, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .debug, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which you are really interested but which is not an issue or error (normal priority)
open class func info(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .info, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .info, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which may cause big trouble soon (high priority)
open class func warning(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .warning, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .warning, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// log something which will keep you awake at night (highest priority)
open class func error(_ message: @autoclosure () -> Any, _
file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
custom(level: .error, message: message(), file: file, function: function, line: line, context: context)
#else
custom(level: .error, message: message, file: file, function: function, line: line, context: context)
#endif
}
/// custom logging to manually adjust values, should just be used by other frameworks
public class func custom(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
file: String = #file, function: String = #function, line: Int = #line, context: Any? = nil) {
#if swift(>=5)
dispatch_send(level: level, message: message(), thread: threadName(),
file: file, function: function, line: line, context: context)
#else
dispatch_send(level: level, message: message, thread: threadName(),
file: file, function: function, line: line, context: context)
#endif
}
/// internal helper which dispatches send to dedicated queue if minLevel is ok
class func dispatch_send(level: SwiftyBeaver.Level, message: @autoclosure () -> Any,
thread: String, file: String, function: String, line: Int, context: Any?) {
var resolvedMessage: String?
for dest in destinations {
guard let queue = dest.queue else {
continue
}
resolvedMessage = resolvedMessage == nil && dest.hasMessageFilters() ? "\(message())" : resolvedMessage
if dest.shouldLevelBeLogged(level, path: file, function: function, message: resolvedMessage) {
// try to convert msg object to String and put it on queue
let msgStr = resolvedMessage == nil ? "\(message())" : resolvedMessage!
let f = stripParams(function: function)
if dest.asynchronously {
queue.async {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
} else {
queue.sync {
_ = dest.send(level, msg: msgStr, thread: thread, file: file, function: f, line: line, context: context)
}
}
}
}
}
/**
DEPRECATED & NEEDS COMPLETE REWRITE DUE TO SWIFT 3 AND GENERAL INCORRECT LOGIC
Flush all destinations to make sure all logging messages have been written out
Returns after all messages flushed or timeout seconds
- returns: true if all messages flushed, false if timeout or error occurred
*/
public class func flush(secondTimeout: Int64) -> Bool {
/*
guard let grp = dispatch_group_create() else { return false }
for dest in destinations {
if let queue = dest.queue {
dispatch_group_enter(grp)
queue.asynchronously(execute: {
dest.flush()
grp.leave()
})
}
}
let waitUntil = DispatchTime.now(dispatch_time_t(DISPATCH_TIME_NOW), secondTimeout * 1000000000)
return dispatch_group_wait(grp, waitUntil) == 0
*/
return true
}
/// removes the parameters from a function because it looks weird with a single param
class func stripParams(function: String) -> String {
var f = function
if let indexOfBrace = f.find("(") {
#if swift(>=4.0)
f = String(f[..<indexOfBrace])
#else
f = f.substring(to: indexOfBrace)
#endif
}
f += "()"
return f
}
}
@@ -435,6 +435,31 @@ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
## SwiftyBeaver
The MIT License (MIT)
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftyMarkdown
The MIT License (MIT)
@@ -468,6 +468,37 @@ POSSIBILITY OF SUCH DAMAGE.
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2015 Sebastian Kreutzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftyBeaver</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 Simon Fairbairn
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -201,6 +201,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
install_framework "${PODS_ROOT}/Sparkle/Sparkle.framework"
install_dsym "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
@@ -208,6 +209,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/ShortcutRecorder/ShortcutRecorder.framework"
install_framework "${PODS_ROOT}/Sparkle/Sparkle.framework"
install_dsym "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyBeaver/SwiftyBeaver.framework"
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
@@ -1,9 +1,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' @loader_path/../Frameworks
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
@@ -1,9 +1,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenter.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/AppCenter/AppCenter-SDK-Apple/AppCenterCrashes.xcframework/macos-arm64_x86_64" "${PODS_ROOT}/Sparkle"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/LetsMove/LetsMove.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ShortcutRecorder/ShortcutRecorder.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver/SwiftyBeaver.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyMarkdown/SwiftyMarkdown.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' @loader_path/../Frameworks
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_LDFLAGS = $(inherited) -l"c++" -l"sqlite3" -l"z" -framework "AppCenter" -framework "AppCenterCrashes" -framework "AppKit" -framework "Carbon" -framework "Cocoa" -framework "Foundation" -framework "LetsMove" -framework "ShortcutRecorder" -framework "Sparkle" -framework "SwiftyBeaver" -framework "SwiftyMarkdown" -framework "SystemConfiguration"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.9.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_SwiftyBeaver : NSObject
@end
@implementation PodsDummy_SwiftyBeaver
@end
@@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
@@ -0,0 +1,16 @@
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double SwiftyBeaverVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftyBeaverVersionString[];
@@ -0,0 +1,11 @@
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
@@ -0,0 +1,6 @@
framework module SwiftyBeaver {
umbrella header "SwiftyBeaver-umbrella.h"
export *
module * { export * }
}
@@ -0,0 +1,11 @@
CODE_SIGN_IDENTITY =
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyBeaver
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyBeaver
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
File diff suppressed because it is too large. Load diff
@@ -39,6 +39,7 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
+168
View File
@@ -4,6 +4,174 @@
<title>alt-tab-macos</title>
<language>en</language>
<item>
<title>Version 7.0.2</title>
<pubDate>Sat, 12 Oct 2024 11:09:58 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v7.0.2/AltTab-7.0.2.zip"
sparkle:version="7.0.2"
sparkle:shortVersionString="7.0.2"
sparkle:edSignature="VVXljkCyzO/m5wy0lYnSsKS1oG0tU2gl4l+ECGvYwP094uaCg5v/G3jmgqEUZAD9uYRfyEqwBgX9XbO93Nn/Dg==" length="12231229"
type="application/octet-stream"/>
</item>
<item>
<title>Version 7.0.1</title>
<pubDate>Fri, 11 Oct 2024 10:14:05 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v7.0.1/AltTab-7.0.1.zip"
sparkle:version="7.0.1"
sparkle:shortVersionString="7.0.1"
sparkle:edSignature="CIz9O/SprxfNFIlGfSVFqYtOtVin+HX4RKjrQGGxEEMOcEa4oAJfpRgdBK9JwLt6dE3gYGxXkIgdLi9yFuDWBQ==" length="12230956"
type="application/octet-stream"/>
</item>
<item>
<title>Version 7.0.0</title>
<pubDate>Wed, 09 Oct 2024 21:13:05 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v7.0.0/AltTab-7.0.0.zip"
sparkle:version="7.0.0"
sparkle:shortVersionString="7.0.0"
sparkle:edSignature="PQtCdRCEZzor+RmfDYJBHbo01KufDdjerj8qTqvBXEAYoZiO1kbNefIl5a0K9rCw0mR78W1Q238dly1Ntu3WDA==" length="12230839"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.73.0</title>
<pubDate>Tue, 08 Oct 2024 20:34:05 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.73.0/AltTab-6.73.0.zip"
sparkle:version="6.73.0"
sparkle:shortVersionString="6.73.0"
sparkle:edSignature="NmSwmx/aU3AtbLRZkWg6/C+4Tp789XmOXpoytTfVRZZmIcxV225L/nkMzAfbd+tMfTQdJDfgSTvZPU64xc6yCg==" length="8880124"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.72.0</title>
<pubDate>Sun, 14 Jul 2024 17:02:37 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.72.0/AltTab-6.72.0.zip"
sparkle:version="6.72.0"
sparkle:shortVersionString="6.72.0"
sparkle:edSignature="L+ys+mrftNTjcFQpuPKvyvxX/PO6ztISIDfROGmU87v3TYUtGpDGC4QQ86lV1RenqLWsy6AKtepJMWMhPA3DBg==" length="8868472"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.71.0</title>
<pubDate>Sat, 06 Jul 2024 10:30:07 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.71.0/AltTab-6.71.0.zip"
sparkle:version="6.71.0"
sparkle:shortVersionString="6.71.0"
sparkle:edSignature="fCfTaZ5RxDDU6MFZuKfg6Oen+qcDd1SC5w9i3Ljc1EspNcJ/wNMOB5u/uNW8q++eFKoS9n/CZuSknR/Jjy4kBA==" length="8866807"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.70.1</title>
<pubDate>Sat, 01 Jun 2024 11:38:14 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.70.1/AltTab-6.70.1.zip"
sparkle:version="6.70.1"
sparkle:shortVersionString="6.70.1"
sparkle:edSignature="4D0dquvbg4oxdPo8iuOa9a3E7zMfDmPOlpTvjWolJtTphkGbMeIwK5ILJT1A8U12tYkxKI86P9Vjg6HjDu9CCA==" length="8863665"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.70.0</title>
<pubDate>Wed, 29 May 2024 22:15:40 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.70.0/AltTab-6.70.0.zip"
sparkle:version="6.70.0"
sparkle:shortVersionString="6.70.0"
sparkle:edSignature="qCFGllhtXeM8zhJp2fh1rPjVmWMNfFBYMejoA7/P1XfB5Emlom2iPXdBa/VJXboHRxGUwkXgpLWfXl6s4JeJAA==" length="8863597"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.69.0</title>
<pubDate>Sun, 12 May 2024 20:18:45 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.69.0/AltTab-6.69.0.zip"
sparkle:version="6.69.0"
sparkle:shortVersionString="6.69.0"
sparkle:edSignature="m6NG6masZWjemss8ewXxPR7ckOMtb6M99Ndi99eEuwhEQIJI1Ln+9IPC4wlc2Bk5S9BIeecoQC/SCOmi3mKmCg==" length="8865881"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.68.0</title>
<pubDate>Wed, 03 Apr 2024 19:30:49 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.68.0/AltTab-6.68.0.zip"
sparkle:version="6.68.0"
sparkle:shortVersionString="6.68.0"
sparkle:edSignature="sqN882pVmxWHNC8RYz5obDAzB3Fj1UwdgJTHueE11weUL1ipEm4D82Y0SX1mpDSSGQLjcUyMl2uhmuGEDqGvDg==" length="8864924"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.67.0</title>
<pubDate>Fri, 15 Mar 2024 17:33:19 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.67.0/AltTab-6.67.0.zip"
sparkle:version="6.67.0"
sparkle:shortVersionString="6.67.0"
sparkle:edSignature="KboOkWGh9f+cFLEL1SroK3hfshHHaPuI7+V52okCLrnzsfUITh+FMlr+gVaq6jgRziCUuIxkSjuzA6CYqsj8Bw==" length="8856370"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.66.0</title>
<pubDate>Sun, 03 Mar 2024 12:03:26 +0000</pubDate>
<sparkle:minimumSystemVersion>10.12</sparkle:minimumSystemVersion>
<sparkle:releaseNotesLink>https://alt-tab-macos.netlify.app/changelog-bare</sparkle:releaseNotesLink>
<enclosure
url="https://github.com/lwouis/alt-tab-macos/releases/download/v6.66.0/AltTab-6.66.0.zip"
sparkle:version="6.66.0"
sparkle:shortVersionString="6.66.0"
sparkle:edSignature="ah92OaNaHEs/B4JZS2zGfVBgDfMIjjdrTot/dYuJ/H0+Ac8N1boaS02b2YdgspYz/16+4C/6oRI7ASniMTMRCw==" length="8851556"
type="application/octet-stream"/>
</item>
<item>
<title>Version 6.65.0</title>
<pubDate>Mon, 29 Jan 2024 17:14:02 +0000</pubDate>
+2 -2
View File
@@ -3,7 +3,7 @@
PRODUCT_NAME = AltTab
PRODUCT_BUNDLE_IDENTIFIER = com.lwouis.alt-tab-macos
MACOSX_DEPLOYMENT_TARGET = 10.12
SWIFT_VERSION = 5.0
SWIFT_VERSION = 5.8
INFOPLIST_FILE = Info.plist
CODE_SIGN_ENTITLEMENTS = alt_tab_macos.entitlements
FRAMEWORK_SEARCH_PATHS[config=*] = $(inherited) /System/Library/PrivateFrameworks // for SkyLight.framework
@@ -13,4 +13,4 @@ INFOPLIST_OUTPUT_FORMAT = binary
PLIST_FILE_OUTPUT_FORMAT = binary
COCOAPODS_PARALLEL_CODE_SIGN = YES // codesign pods faster
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
SWIFT_OBJC_BRIDGING_HEADER = alt-tab-macos-Bridging-Header.h
SWIFT_OBJC_BRIDGING_HEADER = alt-tab-macos-Bridging-Header.h
+4 -1
View File
@@ -3,7 +3,7 @@
These third-party libraries are used:
**ShortcutRecorder**
[Website](https://github.com/Kentzo/ShortcutRecorder) - [CC 4.0 license](https://github.com/Kentzo/ShortcutRecorder/blob/master/LICENSE.txt)
[Website](https://github.com/Kentzo/ShortcutRecorder) - [CC 4.0 License](https://github.com/Kentzo/ShortcutRecorder/blob/master/LICENSE.txt)
**Sparkle**
[Website](https://github.com/sparkle-project/Sparkle) - [License](https://github.com/sparkle-project/Sparkle/blob/master/LICENSE)
@@ -16,3 +16,6 @@ These third-party libraries are used:
**AppCenter**
[Website](https://github.com/microsoft/appcenter-sdk-apple) - [Custom](https://github.com/microsoft/appcenter-sdk-apple/blob/develop/LICENSE)
**SwiftyBeaver**
[Website](https://github.com/SwiftyBeaver/SwiftyBeaver) - [MIT License](https://github.com/SwiftyBeaver/SwiftyBeaver/blob/master/LICENSE)
+142
View File
@@ -1,5 +1,147 @@
# Release notes
## [7.0.2](https://github.com/lwouis/alt-tab-macos/compare/v7.0.1...v7.0.2) (2024-10-12)
### Bug Fixes
* avoid crashing in some cases ([d46d903](https://github.com/lwouis/alt-tab-macos/commit/d46d903))
* block titles in preferences in rtl languages ([ea5574b](https://github.com/lwouis/alt-tab-macos/commit/ea5574b))
* preferences panel icons wouldnt show on old macos versions ([9258acf](https://github.com/lwouis/alt-tab-macos/commit/9258acf))
* show titles > application name, in thumbnails style (closes [#3667](https://github.com/lwouis/alt-tab-macos/issues/3667)) ([b4e97ea](https://github.com/lwouis/alt-tab-macos/commit/b4e97ea))
## [7.0.1](https://github.com/lwouis/alt-tab-macos/compare/v7.0.0...v7.0.1) (2024-10-11)
### Bug Fixes
* avoid crashing in rare cases ([9980ec6](https://github.com/lwouis/alt-tab-macos/commit/9980ec6))
# [7.0.0](https://github.com/lwouis/alt-tab-macos/compare/v6.73.0...v7.0.0) (2024-10-09)
### Features
* major rehaul of the preferences! ([6d97cbd](https://github.com/lwouis/alt-tab-macos/commit/6d97cbd)), closes [#351](https://github.com/lwouis/alt-tab-macos/issues/351)
### BREAKING CHANGES
* The old preferences panel has been replaced with a brand new one
Preferences are now much simpler, full of visual illustrations, and should provide a much better experience. Pick between 3 styles (thumbnails, app-icons, titles), sizes, dark/light themes, high visibility options, and more!
# [6.73.0](https://github.com/lwouis/alt-tab-macos/compare/v6.72.0...v6.73.0) (2024-10-08)
### Bug Fixes
* closing alttab windows gives focus to previous app (closes [#3577](https://github.com/lwouis/alt-tab-macos/issues/3577)) ([0e6f200](https://github.com/lwouis/alt-tab-macos/commit/0e6f200))
* detect passwords app (closes [#3545](https://github.com/lwouis/alt-tab-macos/issues/3545)) ([d0cd206](https://github.com/lwouis/alt-tab-macos/commit/d0cd206))
* dragging files onto windowless apps was inconsistent ([0a9fe9b](https://github.com/lwouis/alt-tab-macos/commit/0a9fe9b))
* finder would sometimes not be listed (closes [#3350](https://github.com/lwouis/alt-tab-macos/issues/3350)) ([eba5e42](https://github.com/lwouis/alt-tab-macos/commit/eba5e42))
* focusing an app could open another version of it ([9d5f11a](https://github.com/lwouis/alt-tab-macos/commit/9d5f11a))
* preview window could remain after focusing an app (closes [#3505](https://github.com/lwouis/alt-tab-macos/issues/3505)) ([e890a60](https://github.com/lwouis/alt-tab-macos/commit/e890a60))
### Features
* add kannada and malayalam and localizations ([5e27701](https://github.com/lwouis/alt-tab-macos/commit/5e27701))
# [6.72.0](https://github.com/lwouis/alt-tab-macos/compare/v6.71.0...v6.72.0) (2024-07-14)
### Bug Fixes
* preferences window quit button supports right-to-left languages ([d3dfd54](https://github.com/lwouis/alt-tab-macos/commit/d3dfd54)), closes [#3487](https://github.com/lwouis/alt-tab-macos/issues/3487)
### Features
* don't show caps-lock indicator as a window (closes [#3171](https://github.com/lwouis/alt-tab-macos/issues/3171)) ([2e15732](https://github.com/lwouis/alt-tab-macos/commit/2e15732))
* improve da/he localizations ([3d98b64](https://github.com/lwouis/alt-tab-macos/commit/3d98b64))
# [6.71.0](https://github.com/lwouis/alt-tab-macos/compare/v6.70.1...v6.71.0) (2024-07-06)
### Bug Fixes
* restore default cmd+tab shortcut when alt-tab crashes ([2c47b4e](https://github.com/lwouis/alt-tab-macos/commit/2c47b4e))
* showing permissions window would crash on macos < 10.15 ([bb5215c](https://github.com/lwouis/alt-tab-macos/commit/bb5215c)), closes [#3437](https://github.com/lwouis/alt-tab-macos/issues/3437)
* traffic light icons could appear half-transparent (closes [#2892](https://github.com/lwouis/alt-tab-macos/issues/2892)) ([1eef1e0](https://github.com/lwouis/alt-tab-macos/commit/1eef1e0))
### Features
* improve fr/he/it/ja/pt/sv localizations ([35521a5](https://github.com/lwouis/alt-tab-macos/commit/35521a5))
* show de-fullscreen button on fullscreen windows ([ad1e8d0](https://github.com/lwouis/alt-tab-macos/commit/ad1e8d0))
## [6.70.1](https://github.com/lwouis/alt-tab-macos/compare/v6.70.0...v6.70.1) (2024-06-01)
### Bug Fixes
* prevent crash introduced in v6.70.0 (closes [#3392](https://github.com/lwouis/alt-tab-macos/issues/3392)) ([e16a3d4](https://github.com/lwouis/alt-tab-macos/commit/e16a3d4))
# [6.70.0](https://github.com/lwouis/alt-tab-macos/compare/v6.69.0...v6.70.0) (2024-05-29)
### Bug Fixes
* detect safari fullscreen windows better (closes [#3384](https://github.com/lwouis/alt-tab-macos/issues/3384)) ([c3006c6](https://github.com/lwouis/alt-tab-macos/commit/c3006c6))
### Features
* add utm-app to default blacklist ([523bcbb](https://github.com/lwouis/alt-tab-macos/commit/523bcbb))
# [6.69.0](https://github.com/lwouis/alt-tab-macos/compare/v6.68.0...v6.69.0) (2024-05-12)
### Bug Fixes
* only show standard windows of preview.app (closes [#3351](https://github.com/lwouis/alt-tab-macos/issues/3351)) ([6231977](https://github.com/lwouis/alt-tab-macos/commit/6231977))
### Features
* update fi, ja, ko, pt-br, th localizations ([14d287a](https://github.com/lwouis/alt-tab-macos/commit/14d287a))
# [6.68.0](https://github.com/lwouis/alt-tab-macos/compare/v6.67.0...v6.68.0) (2024-04-03)
### Bug Fixes
* preview.app window may not show when opening many docs ([31b5a3d](https://github.com/lwouis/alt-tab-macos/commit/31b5a3d)), closes [#3276](https://github.com/lwouis/alt-tab-macos/issues/3276)
* preview.app windows would sometimes not show (closes [#3275](https://github.com/lwouis/alt-tab-macos/issues/3275)) ([785cf9c](https://github.com/lwouis/alt-tab-macos/commit/785cf9c))
### Features
* improve ar/nb/nn/pl/tr/uk localizations ([67a6d81](https://github.com/lwouis/alt-tab-macos/commit/67a6d81))
* new thai and norwegian localizations (closes [#3260](https://github.com/lwouis/alt-tab-macos/issues/3260)) ([d9aa319](https://github.com/lwouis/alt-tab-macos/commit/d9aa319))
# [6.67.0](https://github.com/lwouis/alt-tab-macos/compare/v6.66.0...v6.67.0) (2024-03-15)
### Features
* add icelandic localization ([7df1432](https://github.com/lwouis/alt-tab-macos/commit/7df1432))
* improve ru/cn localizations ([c9fe54e](https://github.com/lwouis/alt-tab-macos/commit/c9fe54e))
* support autocad non-native windows ([#3219](https://github.com/lwouis/alt-tab-macos/issues/3219)) ([bbaef6c](https://github.com/lwouis/alt-tab-macos/commit/bbaef6c))
# [6.66.0](https://github.com/lwouis/alt-tab-macos/compare/v6.65.0...v6.66.0) (2024-03-03)
### Bug Fixes
* alttab would crash after menubar icon was actioned with voiceover ([f7fdf3f](https://github.com/lwouis/alt-tab-macos/commit/f7fdf3f)), closes [#3211](https://github.com/lwouis/alt-tab-macos/issues/3211)
### Features
* improved ca/de/es/ko/nl/sv/vi/zh localizations ([f4ad2ea](https://github.com/lwouis/alt-tab-macos/commit/f4ad2ea))
# [6.65.0](https://github.com/lwouis/alt-tab-macos/compare/v6.64.0...v6.65.0) (2024-01-29)
+46 -10
View File
@@ -5,6 +5,7 @@ They helped [develop the app](https://github.com/lwouis/alt-tab-macos/graphs/con
* [adamnemecek](https://github.com/adamnemecek)
* [AfzalivE](https://github.com/AfzalivE)
* [akx](https://github.com/akx)
* [Allsochen](https://github.com/Allsochen)
* [ayroblu](https://github.com/ayroblu)
* [Calinou](https://github.com/Calinou)
* [damonpam](https://github.com/damonpam)
@@ -13,20 +14,19 @@ They helped [develop the app](https://github.com/lwouis/alt-tab-macos/graphs/con
* [gingerr](https://github.com/gingerr)
* [GrzegorzKazana](https://github.com/GrzegorzKazana)
* [hughlilly](https://github.com/hughlilly)
* [i0ntempest](https://github.com/i0ntempest)
* [karbassi](https://github.com/karbassi)
* [L1cardo](https://github.com/L1cardo)
* [lwouis](https://github.com/lwouis)
* [metacodes](https://github.com/metacodes)
* [mnin](https://github.com/mnin)
* [MrPaschenko](https://github.com/MrPaschenko)
* [nella17](https://github.com/nella17)
* [notlmn](https://github.com/notlmn)
* [phungtuanhoang1996](https://github.com/phungtuanhoang1996)
* [rbnis](https://github.com/rbnis)
* [Rjevski](https://github.com/Rjevski)
* [ryenus](https://github.com/ryenus)
* [s4na](https://github.com/s4na)
* [samdenty](https://github.com/samdenty)
* [ShlomoCode](https://github.com/ShlomoCode)
* [skolj](https://github.com/skolj)
* [xanathar](https://github.com/xanathar)
* [xconverge](https://github.com/xconverge)
@@ -38,16 +38,25 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* Aamirsuleman
* Aarni Koskela
* Abdulelah
* Abdur-Rahman Bilal
* Aden Aziz
* Admin
* Aindriú Mac Giolla Eoin
* Ajaykumarmkaju
* Aku
* Albert Abdilim
* Alex
* Alexander Kilian
* Alexey Eremeev
* Ali Gokmen
* Ali. tas103
* Allen Guan
* Alonso Chen
* Ameng
* Anders
* Andreas Wagner
* Andrew Vader
* antoon334
* Anurag Roy
* anushree b
* Arman
@@ -57,42 +66,49 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* blanorama
* bluefirex
* bovirus
* BV Joshi
* caduellery
* Caner İlhan
* catcore
* Christian Keilmann
* Chun Fei Lung
* codecantata
* Cong Tri
* Dan
* Dan84
* Darius Rosendahl
* Darko
* David R
* David Süle
* Didier Deschrijver
* Dzhuneyt
* EDUARDO
* edu_sombra
* Eliezer Shpigelman
* Emil Pedersen
* Eric WANTZ
* Ersagun Kuruca
* EUKA
* Eukarya
* Eyelly wu
* fabifabulousity
* fil
* Filipe
* Frangarciasalomon
* Frank
* Friendship1
* Fukuda
* Gezimos
* Giacomo Volpi
* Giang
* Github
* Gkostov
* Grzegorz Kazana
* Guillaume
* Gumpanath Puttarassu
* hann-solo
* Haoshuai Xu
* Hjörtur Hjartarson
* Hokuto Kato
* Huandngoc
* Huynh Sang
* Ialiendeg
* Ida Bomholt Dyrholm Jacobsen
* Igor Aradski
@@ -114,17 +130,25 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* Kagurazaka Tsuki
* kal
* kant
* Kawarimidoll+git
* kazuki sakamoto
* Kevinsevinche
* Kinyagulovrr
* Klara
* Kristófer Fannar Björnsson
* Kushnee5
* Kuuchi
* L
* Lakshman Kolappan
* Lasse Mattila
* Lcwhhh
* Lester
* Loïc
* Loic. peron
* LostInCompilation
* Lumaxis
* lwouis
* Malo
* Maplevantablack
* Marc Pla
* Marcus Billman
@@ -132,11 +156,14 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* Marko McLion
* Martin Mitka
* Martin. mitka
* Martinjnilsen
* Masayoshi Motojima
* Masih
* Matheus
* Max
* Maximilian Falk
* MaximilianFreitag
* Mehmet Ali Ertorer
* Meriç Bağlayan
* Michael
* Milos M
* Mohammad Al Zouabi
@@ -149,12 +176,13 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* Nils Fahldieck
* Nilton Souza
* Nmolham
* Nuriddin Islamov
* Ori
* Paulius
* Pehovorka
* Peterkim0620
* Petr Kolář
* Piotr Budny
* ponchik
* Quentin. douarre
* Raphaël
* Rasmus
@@ -162,8 +190,11 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* raz
* Razvan
* rbnis
* rei fujise
* Roccobot
* Rodrigo Schneider
* Ron Nuss
* sa-ha
* sawtooth
* Selcuk Dursun
* Sergey
@@ -172,16 +203,18 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* SheNeVmerla
* Shivam Bansal
* shlomo
* Sippawit Thammawiset
* Sjur Moshagen
* Sjur.n. moshagen
* Stan Smits
* stas
* Stefan
* Stefan Lange
* Svetoslav Stefanov
* Thomas Wölk
* Thorsten
* Tomoa Nozawa
* Umutakkaya1996
* Vadym
* Vegard
* Vijayant Saini
* Vincent Orback
* Vlad
@@ -190,6 +223,9 @@ They helped [localize the app](https://poeditor.com/join/project/8AOEZ0eAZE):
* Whatsmine-HaoshuaiXu
* Wilhelm Wolfgang Gärtner
* Wowpapa3232
* wtwt0119!!
* Yongsung Yoon
* Yorben
* Yossi Zahn
* ysaito
* Yukai
+148 -136
View File
@@ -1,126 +1,133 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.2.2)
activesupport (6.1.7.10)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
concurrent-ruby (1.1.6)
dnsruby (1.61.3)
addressable (~> 2.5)
em-websocket (0.5.1)
commonmarker (0.23.10)
concurrent-ruby (1.3.4)
dnsruby (1.72.2)
simpleidn (~> 0.2.1)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.12.0)
ffi (>= 1.3.0)
http_parser.rb (~> 0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.7.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
ffi (1.12.2)
execjs (2.10.0)
faraday (2.8.1)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.17.0)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (204)
github-pages-health-check (= 1.16.1)
jekyll (= 3.8.5)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.6)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.13.0)
gemoji (4.1.0)
github-pages (231)
github-pages-health-check (= 1.18.2)
jekyll (= 3.9.5)
jekyll-avatar (= 0.8.0)
jekyll-coffeescript (= 1.2.2)
jekyll-commonmark-ghpages (= 0.4.0)
jekyll-default-layout (= 0.1.5)
jekyll-feed (= 0.17.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.13.0)
jekyll-mentions (= 1.5.1)
jekyll-github-metadata (= 2.16.1)
jekyll-include-cache (= 0.2.1)
jekyll-mentions (= 1.6.0)
jekyll-optional-front-matter (= 0.3.2)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.3.0)
jekyll-redirect-from (= 0.15.0)
jekyll-redirect-from (= 0.16.0)
jekyll-relative-links (= 0.6.1)
jekyll-remote-theme (= 0.4.1)
jekyll-remote-theme (= 0.4.3)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.6.1)
jekyll-seo-tag (= 2.8.0)
jekyll-sitemap (= 1.4.0)
jekyll-swiss (= 1.0.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.1)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.4)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-theme-architect (= 0.2.0)
jekyll-theme-cayman (= 0.2.0)
jekyll-theme-dinky (= 0.2.0)
jekyll-theme-hacker (= 0.2.0)
jekyll-theme-leap-day (= 0.2.0)
jekyll-theme-merlot (= 0.2.0)
jekyll-theme-midnight (= 0.2.0)
jekyll-theme-minimal (= 0.2.0)
jekyll-theme-modernist (= 0.2.0)
jekyll-theme-primer (= 0.6.0)
jekyll-theme-slate (= 0.2.0)
jekyll-theme-tactile (= 0.2.0)
jekyll-theme-time-machine (= 0.2.0)
jekyll-titles-from-headings (= 0.5.3)
jemoji (= 0.11.1)
kramdown (= 1.17.0)
liquid (= 4.0.3)
jemoji (= 0.13.0)
kramdown (= 2.4.0)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.10.4, < 2.0)
rouge (= 3.13.0)
nokogiri (>= 1.13.6, < 2.0)
rouge (= 3.30.0)
terminal-table (~> 1.4)
github-pages-health-check (1.16.1)
github-pages-health-check (1.18.2)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (~> 3.0)
octokit (>= 4, < 8)
public_suffix (>= 3.0, < 6.0)
typhoeus (~> 1.3)
html-pipeline (2.12.3)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
http_parser.rb (0.8.0)
i18n (1.14.6)
concurrent-ruby (~> 1.0)
jekyll (3.8.5)
jekyll (3.9.5)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (~> 1.14)
kramdown (>= 1.17, < 3)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.7.0)
jekyll-avatar (0.8.0)
jekyll (>= 3.0, < 5.0)
jekyll-coffeescript (1.1.1)
jekyll-coffeescript (1.2.2)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.3.1)
commonmarker (~> 0.14)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark-ghpages (0.1.6)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1.2)
rouge (>= 2.0, < 4.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.13.0)
coffee-script-source (~> 1.12)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.4.0)
commonmarker (~> 0.23.7)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.5)
jekyll (>= 3.0, < 5.0)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.13.0)
jekyll-github-metadata (2.16.1)
jekyll (>= 3.4, < 5.0)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.5.1)
octokit (>= 4, < 7, != 4.4.0)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-mentions (1.6.0)
html-pipeline (~> 2.3)
jekyll (>= 3.7, < 5.0)
jekyll-optional-front-matter (0.3.2)
@@ -128,115 +135,120 @@ GEM
jekyll-paginate (1.1.0)
jekyll-readme-index (0.3.0)
jekyll (>= 3.0, < 5.0)
jekyll-redirect-from (0.15.0)
jekyll-redirect-from (0.16.0)
jekyll (>= 3.3, < 5.0)
jekyll-relative-links (0.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-remote-theme (0.4.1)
jekyll-remote-theme (0.4.3)
addressable (~> 2.0)
jekyll (>= 3.5, < 5.0)
rubyzip (>= 1.3.0)
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
rubyzip (>= 1.3.0, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.6.1)
jekyll (>= 3.3, < 5.0)
jekyll-seo-tag (2.8.0)
jekyll (>= 3.8, < 5.0)
jekyll-sitemap (1.4.0)
jekyll (>= 3.7, < 5.0)
jekyll-swiss (1.0.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-theme-architect (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-theme-cayman (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-theme-dinky (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.1)
jekyll (~> 3.5)
jekyll-theme-hacker (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-theme-leap-day (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-theme-merlot (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-theme-midnight (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-theme-minimal (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-theme-modernist (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.4)
jekyll-theme-primer (0.6.0)
jekyll (> 3.5, < 5.0)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-theme-slate (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-theme-tactile (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-theme-time-machine (0.2.0)
jekyll (> 3.5, < 5.0)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.3)
jekyll (>= 3.3, < 5.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
jemoji (0.11.1)
gemoji (~> 3.0)
jemoji (0.13.0)
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
kramdown (1.17.0)
liquid (4.0.3)
listen (3.2.1)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
mini_portile2 (2.4.0)
mini_portile2 (2.8.7)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.14.0)
multipart-post (2.1.1)
nokogiri (1.10.9)
mini_portile2 (~> 2.4.0)
octokit (4.18.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
minitest (5.25.1)
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (3.1.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.1)
public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rouge (3.13.0)
ruby-enum (0.8.0)
i18n
rubyzip (2.3.0)
rexml (3.3.9)
rouge (3.30.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.2)
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
faraday (>= 0.17.3, < 3)
simpleidn (0.2.3)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.3.1)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
unicode-display_width (1.7.0)
zeitwerk (2.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (1.8.0)
zeitwerk (2.6.18)
PLATFORMS
ruby
+17 -28
View File
@@ -4,33 +4,28 @@ permalink: /
# Introduction
[![Screenshot](public/demo/frontpage.jpg)](public/demo/frontpage.jpg)
[<img src="public/demo/frontpage.jpg" alt="Screenshot showing AltTab" width="1920" height="1080" style="width:100%; height:auto;" />](public/demo/frontpage.jpg)
**AltTab** brings the power of Windows’s "alt-tab" window switcher to macOS.
Alternative Styles
| [<img loading="lazy" src="public/demo/app-icons-style.jpg" alt="Screenshot: App Icons Style" width="513" height="241" style="width:100%; height:auto;" />](public/demo/app-icons-style.jpg) | [<img loading="lazy" src="public/demo/titles-style.jpg" alt="Screenshot: Titles Style" width="766" height="248" style="width:100%; height:auto;" />](public/demo/titles-style.jpg) |
## Installation
* [**Download the latest release**]({{ site.github.latest_release.assets[0].browser_download_url }})
* Alternatively, you can use [homebrew](https://brew.sh/): `brew install --cask alt-tab`
## Features
* Switch focus to any window
* Minimize, close, fullscreen any window
* Hide, quit any app
* Customize AltTab appearance (e.g. show app badges, Space numbers, increase icon, thumbnail, title size, etc)
* Custom trigger shortcuts with almost any key
* Minimize, close, fullscreen any window; Hide, quit any app
* Customize AltTab appearance (pick one of 3 styles, then customize further)
* Custom trigger shortcuts with almost any keys
* Blacklist apps you don’t want to list or trigger AltTab from
* Dark Mode
* Light and Dark Appearance
* Drag-and-drop things on top of window thumbnails
* Right-to-left languages and UI
* Accessibility: VoiceOver, sticky keys, reduced transparency, etc
## Installation
[**Download the latest release**]({{ site.github.latest_release.assets[0].browser_download_url }})
Alternatively, you can use [homebrew](https://brew.sh/):
| Homebrew 2.7 and above | Homebrew 2.5 and 2.6 | Homebrew 2.4 and below |
|------------------------|----------------------|------------------------------------|
| `brew install --cask alt-tab` | `brew install alt-tab` | `brew cask install alt-tab` |
## Compatibility
* __macOS version:__ from 10.12 to latest
@@ -38,7 +33,7 @@ Alternatively, you can use [homebrew](https://brew.sh/):
## Localization
AltTab is available in: Bahasa Indonesia, Català, Dansk, Deutsch, Eesti keel, English, Español, Français, Galego, Hrvatski, Italiano, Kurdî, Lëtzebuergesch, Limba română, Magyar, Nederlands, Norsk, Oʻzbekcha, Polski, Português, Português (Brasil), Shqip, Slovenčina, Slovenščina, Suomi, Svenska, Tiếng Việt, Türkçe, Čeština, Ελληνικά, Български, Русский язык, Српски / Srpski, українська мова, עִבְרִית ,العربية ,فارسی, हिन्दी, বাংলা, தமிழ், 日本語, 简体中文, 繁體中文, 한국어
AltTab is available in many languages: Bahasa Indonesia, Català, Dansk, Deutsch, Eesti keel, English, Español, Français, Gaeilge, Galego, Hrvatski, Italiano, Kurdî, Limba română, Lëtzebuergesch, Magyar, Nederlands, Norsk Bokmål, Norsk Nynorsk, Oʻzbekcha, Polski, Português, Português (Brasil), Shqip, Slovenčina, Slovenščina, Suomi, Svenska, Tiếng Việt, Türkçe, Íslenska, Čeština, Ελληνικά, Български, Русский, Српски / Srpski, Українська, עִבְרִית, العربية, فارسی, हिन्दी, বাংলা, தமிழ், ಕನ್ನಡ, മലയാളം, ภาษาไทย, 日本語, 简体中文, 繁體中文, 한국어
[Contribute your own language easily!](https://poeditor.com/join/project/8AOEZ0eAZE)
@@ -51,9 +46,9 @@ AltTab is available in: Bahasa Indonesia, Català, Dansk, Deutsch, Eesti keel, E
Change the shortcut keys, switch to a Windows theme and more, using the Preferences window:
| [![Screenshot1](public/demo/preferences-appearance.jpg)](public/demo/preferences-appearance.jpg) | [![Screenshot 2](public/demo/preferences-controls.jpg)](public/demo/preferences-controls.jpg) |
| [![Screenshot3](public/demo/preferences-blacklist.jpg)](public/demo/preferences-blacklist.jpg) | [![Screenshot 4](public/demo/preferences-policies.jpg)](public/demo/preferences-policies.jpg) |
| [![Screenshot5](public/demo/preferences-general.jpg)](public/demo/preferences-general.jpg) | |
| [<img loading="lazy" src="public/demo/preferences-general.jpg" alt="Screenshot: General preferences" width="672" height="354" style="width:100%; height:auto;" />](public/demo/preferences-general.jpg) | [<img loading="lazy" src="public/demo/preferences-controls.jpg" alt="Screenshot: Controls preferences" width="677" height="688" style="width:100%; height:auto;" />](public/demo/preferences-controls.jpg) |
| [<img loading="lazy" src="public/demo/preferences-appearance.jpg" alt="Screenshot: Appearance preferences" width="672" height="735" style="width:100%; height:auto;" />](public/demo/preferences-appearance.jpg) | [<img loading="lazy" src="public/demo/preferences-appearance-details.jpg" alt="Screenshot: Appearance preferences (details)" width="672" height="934" style="width:100%; height:auto;" />](public/demo/preferences-appearance-details.jpg) |
| [<img loading="lazy" src="public/demo/preferences-blacklist.jpg" alt="Screenshot: Blacklist preferences" width="672" height="655" style="width:100%; height:auto;" />](public/demo/preferences-blacklist.jpg) | [<img loading="lazy" src="public/demo/preferences-policies.jpg" alt="Screenshot: Policies preferences" width="672" height="353" style="width:100%; height:auto;" />](public/demo/preferences-policies.jpg) |
## Alternatives
@@ -72,12 +67,6 @@ Before building my own app, I looked around at similar apps. However, none was c
There are also related apps which don’t really overlap in functionality, but target similar needs: [Swish](https://highlyopinionated.co/swish/), [Hookshot](https://hookshot.app/), [Magnet](https://magnet.crowdcafe.com/), [Spectacle](https://www.spectacleapp.com/), [Rectangle](https://github.com/rxhanson/Rectangle), [yabai](https://github.com/koekeishiya/yabai), [LayAuto](https://layautoapp.com/), [OptimalLayout](http://most-advantageous.com/optimal-layout/), [BetterTouchTool](https://folivora.ai/), [BetterSnapTool](https://folivora.ai/bettersnaptool), [Moom](https://manytricks.com/moom/), [uBar](https://brawersoftware.com/products/ubar).
## More screenshots
| 1 row | 2 rows | Windows theme |
|-------|---------|-------|
| [![Screenshot](public/demo/1-row.jpg)](public/demo/1-row.jpg) | [![Screenshot](public/demo/2-rows.jpg)](public/demo/2-rows.jpg) | [![Screenshot](public/demo/windows-theme.jpg)](public/demo/windows-theme.jpg) |
## License
AltTab is under the [GPL-3.0 license](https://github.com/lwouis/alt-tab-macos/blob/master/LICENCE.md).
+2 -1
View File
@@ -139,7 +139,8 @@ nav {
word-break: break-word;
img {
max-width: 100%;
width: 100%;
height: auto;
}
code, pre, tt {
+1 -2
View File
@@ -37,8 +37,7 @@
<header>
<div>
<a href="{{ site.baseurl }}"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100"
height="100"/></a>
<a href="{{ site.baseurl }}"><img src="{{ site.baseurl }}public/android-chrome-256x256.png" width="100" height="100"/></a>
<div>
<h1><a href="{{ site.baseurl }}">{{ site.title }}</a></h1>
<p>{{ site.github.project_tagline }}</p>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

+9048 -5601
View File
File diff suppressed because it is too large. Load diff
+14 -2
View File
@@ -6,9 +6,19 @@
"license": "MIT",
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.swift": [
"node scripts/swiftformat.js --lint"
]
},
"scripts": {
"format": "node scripts/swiftformat.js",
"format:check": "node scripts/swiftformat.js --lint"
},
"devDependencies": {
"@commitlint/cli": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
@@ -17,7 +27,9 @@
"@semantic-release/git": "^7.0.16",
"fontkit": "^1.8.0",
"husky": "^3.0.4",
"lint-staged": "^13.0.0",
"marked": "^0.8.0",
"semantic-release": "^15.13.24"
"semantic-release": "^15.13.24",
"glob": "^10.0.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff. Show more