diff --git a/.chglog/CHANGELOG.tpl.m4 b/.chglog/CHANGELOG.tpl.m4
new file mode 100644
index 0000000..c64d4c4
--- /dev/null
+++ b/.chglog/CHANGELOG.tpl.m4
@@ -0,0 +1,161 @@
+dnl
+
+
+dnl change comment marker (#,\n) -> (/*,*/)
+changecom(/*,*/)
+
+dnl
+dnl Template to emit the commits' refs
+dnl
+define(
+ CHGLOG_COMMIT_REFS,
+{{- if .Refs }}
+{{- range .Refs }}[(#{{ .Ref }})]({{ $.Info.RepositoryURL }}/-/issues/{{ .Ref }}){{ end -}}
+{{ end }})
+
+
+dnl
+dnl Template to emit the subject
+dnl
+define(
+ CHGLOG_COMMIT_SUBJECT,
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }})
+
+
+dnl
+dnl Template to emit the subject with references
+dnl
+define(
+ CHGLOG_COMMIT_SUBJECT_WITH_REFS,
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+CHGLOG_COMMIT_REFS)
+
+
+dnl
+dnl Template to emit the commits' mentions
+dnl
+define(
+ CHGLOG_COMMIT_MENTIONS,
+{{ if .Mentions }}
+dnl need 4 spaces
+ Mentions: {{ .Mentions }}
+{{ end -}})
+
+
+dnl
+dnl Template to emit all signers in a commit
+dnl
+define(
+ CHGLOG_COMMIT_SIGNER_ENTRIES,
+{{ if .Signers }}
+dnl need 4 spaces
+ Signed Off By:
+{{- range .Signers }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}})
+
+
+dnl
+dnl Template to emit all co-autors in a commit
+dnl
+define(
+ CHGLOG_COMMIT_COAUTHORS_ENTRIES,
+{{ if .CoAuthors }}
+dnl need 4 spaces
+ Co-authored by:
+{{- range .CoAuthors }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}})
+
+
+dnl
+dnl Template to emit the commits' notes
+dnl
+define(
+ CHGLOG_COMMIT_NOTES,
+{{ if .Notes }}
+dnl need 4 spaces
+{{- range .Notes }}
+ **{{ .Title }}**: {{ .Body }}
+{{ end -}}
+{{ end -}})
+
+
+dnl
+dnl Template to emit a commit entry
+dnl
+define(
+ CHGLOG_COMMITGROUP_ENTRY,
+### {{ .Title }}
+{{ range .Commits -}}
+dnl CHGLOG_COMMIT_SUBJECT
+CHGLOG_COMMIT_SUBJECT_WITH_REFS
+CHGLOG_COMMIT_MENTIONS
+CHGLOG_COMMIT_SIGNER_ENTRIES
+CHGLOG_COMMIT_COAUTHORS_ENTRIES
+CHGLOG_COMMIT_NOTES
+{{ end }})
+
+
+
+dnl
+dnl The template to emit the full CHANGELOG
+dnl
+{{- if .Info.Title }}
+# CHANGELOG for *{{ .Info.Title }}*
+{{- end }}
+
+{{ if .Versions -}}
+
+## [Unreleased]
+
+{{ if .Unreleased.CommitGroups -}}
+{{ range .Unreleased.CommitGroups -}}
+CHGLOG_COMMITGROUP_ENTRY
+{{ end -}}
+{{ end -}}
+{{ end -}} dnl {{ if .Versions -}}
+
+{{ range .Versions }}
+
+## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
+{{ range .CommitGroups -}}
+CHGLOG_COMMITGROUP_ENTRY
+{{ end -}} dnl {{ range .CommitGroups -}}
+
+{{- if .RevertCommits -}}
+### Reverts
+{{ range .RevertCommits -}}
+- {{ .Revert.Header }}
+{{ end }} dnl {{ range .RevertCommits -}}
+{{ end -}} dnl {{- if .RevertCommits -}}
+
+{{- if .MergeCommits -}}
+### Merge Requests
+{{ range .MergeCommits -}}
+- {{ .Header }}
+{{ end }} dnl {{ range .MergeCommits -}}
+{{ end -}} dnl {{- if .MergeCommits -}}
+
+{{- if .NoteGroups -}}
+{{ range .NoteGroups -}}
+### {{ .Title }}
+{{ range .Notes }}
+{{ .Body }}
+{{ end }} dnl {{ range .Notes }}
+{{ end -}} dnl {{ range .NoteGroups -}}
+{{ end -}} dnl {{- if .NoteGroups -}}
+{{ end -}} dnl {{ range .Versions }}
+
+
+dnl create links into the gitlab view of the repository
+{{- if .Versions }}
+[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
+{{ range .Versions -}}
+{{ if .Tag.Previous -}}
+[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md
new file mode 100644
index 0000000..58655dc
--- /dev/null
+++ b/.chglog/CHANGELOG.tpl.md
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{{- if .Info.Title }}
+# CHANGELOG for *{{ .Info.Title }}*
+{{- end }}
+
+{{ if .Versions -}}
+
+## [Unreleased]
+
+{{ if .Unreleased.CommitGroups -}}
+{{ range .Unreleased.CommitGroups -}}
+### {{ .Title }}
+{{ range .Commits -}}
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+{{- if .Refs }}
+{{- range .Refs }}[(#{{ .Ref }})]({{ $.Info.RepositoryURL }}/-/issues/{{ .Ref }}){{ end -}}
+{{ end }}
+{{ if .Mentions }}
+ Mentions: {{ .Mentions }}
+{{ end -}}
+{{ if .Signers }}
+ Signed Off By:
+{{- range .Signers }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}}
+{{ if .CoAuthors }}
+ Co-authored by:
+{{- range .CoAuthors }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}}
+{{ if .Notes }}
+{{- range .Notes }}
+ **{{ .Title }}**: {{ .Body }}
+{{ end -}}
+{{ end -}}
+{{ end }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
+{{ range .Versions }}
+
+## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
+{{ range .CommitGroups -}}
+### {{ .Title }}
+{{ range .Commits -}}
+- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
+{{- if .Refs }}
+{{- range .Refs }}[(#{{ .Ref }})]({{ $.Info.RepositoryURL }}/-/issues/{{ .Ref }}){{ end -}}
+{{ end }}
+{{ if .Mentions }}
+ Mentions: {{ .Mentions }}
+{{ end -}}
+{{ if .Signers }}
+ Signed Off By:
+{{- range .Signers }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}}
+{{ if .CoAuthors }}
+ Co-authored by:
+{{- range .CoAuthors }}
+ - {{ .Name }} ({{ .Email }})
+{{ end -}}
+{{ end -}}
+{{ if .Notes }}
+{{- range .Notes }}
+ **{{ .Title }}**: {{ .Body }}
+{{ end -}}
+{{ end -}}
+{{ end }}
+{{ end -}}
+{{- if .RevertCommits -}}
+### Reverts
+{{ range .RevertCommits -}}
+- {{ .Revert.Header }}
+{{ end }} {{ end -}}
+{{- if .MergeCommits -}}
+### Merge Requests
+{{ range .MergeCommits -}}
+- {{ .Header }}
+{{ end }} {{ end -}}
+{{- if .NoteGroups -}}
+{{ range .NoteGroups -}}
+### {{ .Title }}
+{{ range .Notes }}
+{{ .Body }}
+{{ end }} {{ end -}} {{ end -}} {{ end -}}
+
+{{- if .Versions }}
+[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
+{{ range .Versions -}}
+{{ if .Tag.Previous -}}
+[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
+{{ end -}}
+{{ end -}}
+{{ end -}}
diff --git a/.chglog/Makefile b/.chglog/Makefile
new file mode 100644
index 0000000..892f9a4
--- /dev/null
+++ b/.chglog/Makefile
@@ -0,0 +1,20 @@
+# Project: Nitroxomat
+# File name: .chglog/Makefile
+# Purpose: generate the CHANGELOG.tpl.md from a template
+# Author: Boris Boesler
+# Modified by:
+# Created: 08.06.2022
+# Copyright: (c) 2022 Boris Boesler
+
+
+# tools
+M4 = m4
+
+# files
+CLTemplateTemplate = CHANGELOG.tpl.m4
+CLTemplate = CHANGELOG.tpl.md
+
+all: $(CLTemplate)
+
+%.md: %.m4
+ $(M4) $^ > $@
diff --git a/.chglog/config.yml b/.chglog/config.yml
new file mode 100644
index 0000000..7635331
--- /dev/null
+++ b/.chglog/config.yml
@@ -0,0 +1,62 @@
+style: none
+template: CHANGELOG.tpl.md
+info:
+ title: Nitroxomat
+ repository_url: https://github.com/borisboesler/Nitroxomat.git
+options:
+ sort: date
+ commits:
+ filters:
+ Type:
+ - docs
+ - chore
+ - feat
+ - fix
+ - perf
+ - refactor
+ - style
+ - test
+
+ commit_groups:
+ group_by: Type
+ sort_by: Title
+ title_maps:
+ docs: Documention
+ chore: Chores
+ feat: Features
+ fix: Bug Fixes
+ perf: Performance Improvements
+ refactor: Code Refactoring
+ style: Reformating
+ test: Tests
+
+ header:
+ pattern: "^(\\w*)(?:\\((.+)\\))?\\:\\s(.+)$"
+ pattern_maps:
+ - Type
+ - Scope
+ - Subject
+
+ issues:
+ prefix:
+ - "#"
+
+ refs:
+ actions:
+ - Closes
+ - Fixes
+
+ merges:
+ pattern: "^Merge branch ('.*'.*) into (.*)$"
+ pattern_maps:
+ - Source
+ - Ref
+
+ reverts:
+ pattern: "^Revert \"([\\s\\S]*)\"$"
+ pattern_maps:
+ - Header
+
+ notes:
+ keywords:
+ - BREAKING CHANGE
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8476fee
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+# copy from
+# https://github.com/petervanderdoes/gitflow-avh/blob/develop/.gitattributes
+
+* text=auto
+
+*.awk text eol=lf
+*.sed text eol=lf
+*.sh text eol=lf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..53800b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,141 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/swift,xcode,macos
+# Edit at https://www.toptal.com/developers/gitignore?templates=swift,xcode,macos
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Swift ###
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## User settings
+xcuserdata/
+
+## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
+*.xcscmblueprint
+*.xccheckout
+
+## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+build/
+DerivedData/
+*.moved-aside
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+
+## Obj-C/Swift specific
+*.hmap
+
+## App packaging
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+# Packages/
+# Package.pins
+# Package.resolved
+# *.xcodeproj
+# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
+# hence it is not needed unless you have added a package configuration file to your project
+# .swiftpm
+
+.build/
+
+# CocoaPods
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+# Pods/
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
+
+# Carthage
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build/
+
+# Accio dependency management
+Dependencies/
+.accio/
+
+# fastlane
+# It is recommended to not store the screenshots in the git repo.
+# Instead, use fastlane to re-generate the screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/#source-control
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots/**/*.png
+fastlane/test_output
+
+# Code Injection
+# After new code Injection tools there's a generated folder /iOSInjectionProject
+# https://github.com/johnno1962/injectionforxcode
+
+iOSInjectionProject/
+
+### Xcode ###
+# Xcode
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+
+
+
+## Gcc Patch
+/*.gcno
+
+### Xcode Patch ###
+*.xcodeproj/*
+!*.xcodeproj/project.pbxproj
+!*.xcodeproj/xcshareddata/
+!*.xcworkspace/contents.xcworkspacedata
+**/xcshareddata/WorkspaceSettings.xcsettings
+
+# End of https://www.toptal.com/developers/gitignore/api/swift,xcode,macos
+
+#
+# added
+#
+
+.history
diff --git a/.gitlint b/.gitlint
new file mode 100644
index 0000000..c4d2122
--- /dev/null
+++ b/.gitlint
@@ -0,0 +1,134 @@
+# Edit this file as you like.
+#
+# All these sections are optional. Each section with the exception of [general] represents
+# one rule and each key in it is an option for that specific rule.
+#
+# Rules and sections can be referenced by their full name or by id. For example
+# section "[body-max-line-length]" could also be written as "[B1]". Full section names are
+# used in here for clarity.
+#
+# [general]
+# Ignore certain rules, this example uses both full name and id
+# ignore=title-trailing-punctuation, T3
+
+# verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this
+# verbosity = 2
+
+# By default gitlint will ignore merge, revert, fixup and squash commits.
+# ignore-merge-commits=true
+# ignore-revert-commits=true
+# ignore-fixup-commits=true
+# ignore-squash-commits=true
+
+# Ignore any data send to gitlint via stdin
+# ignore-stdin=true
+
+# Fetch additional meta-data from the local repository when manually passing a
+# commit message to gitlint via stdin or --commit-msg. Disabled by default.
+# staged=true
+
+# Hard fail when the target commit range is empty. Note that gitlint will
+# already fail by default on invalid commit ranges. This option is specifically
+# to tell gitlint to fail on *valid but empty* commit ranges.
+# Disabled by default.
+# fail-without-commits=true
+
+# Enable debug mode (prints more output). Disabled by default.
+# debug=true
+
+# Enable community contributed rules
+# See http://jorisroovers.github.io/gitlint/contrib_rules for details
+# contrib=contrib-title-conventional-commits,CC1
+
+# Set the extra-path where gitlint will search for user defined rules
+# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
+# extra-path=examples/
+
+# This is an example of how to configure the "title-max-length" rule and
+# set the line-length it enforces to 50
+# [title-max-length]
+# line-length=50
+
+# Conversely, you can also enforce minimal length of a title with the
+# "title-min-length" rule:
+# [title-min-length]
+# min-length=5
+
+# [title-must-not-contain-word]
+# Comma-separated list of words that should not occur in the title. Matching is case
+# insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING"
+# will not cause a violation, but "WIP: my title" will.
+# words=wip
+
+# [title-match-regex]
+# python-style regex that the commit-msg title must match
+# Note that the regex can contradict with other rules if not used correctly
+# (e.g. title-must-not-contain-word).
+# regex=^US[0-9]*
+
+# [body-max-line-length]
+# line-length=72
+
+# [body-min-length]
+# min-length=5
+
+# [body-is-missing]
+# Whether to ignore this rule on merge commits (which typically only have a title)
+# default = True
+# ignore-merge-commits=false
+
+# [body-changed-file-mention]
+# List of files that need to be explicitly mentioned in the body when they are changed
+# This is useful for when developers often erroneously edit certain files or git submodules.
+# By specifying this rule, developers can only change the file when they explicitly reference
+# it in the commit message.
+# files=gitlint-core/gitlint/rules.py,README.md
+
+# [body-match-regex]
+# python-style regex that the commit-msg body must match.
+# E.g. body must end in My-Commit-Tag: foo
+# regex=My-Commit-Tag: foo$
+
+# [author-valid-email]
+# python-style regex that the commit author email address must match.
+# For example, use the following regex if you only want to allow email addresses from foo.com
+# regex=[^@]+@foo.com
+
+# [ignore-by-title]
+# Ignore certain rules for commits of which the title matches a regex
+# E.g. Match commit titles that start with "Release"
+# regex=^Release(.*)
+
+# Ignore certain rules, you can reference them by their id or by their full name
+# Use 'all' to ignore all rules
+# ignore=T1,body-min-length
+
+# [ignore-by-body]
+# Ignore certain rules for commits of which the body has a line that matches a regex
+# E.g. Match bodies that have a line that that contain "release"
+# regex=(.*)release(.*)
+#
+# Ignore certain rules, you can reference them by their id or by their full name
+# Use 'all' to ignore all rules
+# ignore=T1,body-min-length
+
+# [ignore-body-lines]
+# Ignore certain lines in a commit body that match a regex.
+# E.g. Ignore all lines that start with 'Co-Authored-By'
+# regex=^Co-Authored-By
+
+# [ignore-by-author-name]
+# Ignore certain rules for commits of which the author name matches a regex
+# E.g. Match commits made by dependabot
+# regex=(.*)dependabot(.*)
+#
+# Ignore certain rules, you can reference them by their id or by their full name
+# Use 'all' to ignore all rules
+# ignore=T1,body-min-length
+
+# This is a contrib rule - a community contributed rule. These are disabled by default.
+# You need to explicitly enable them one-by-one by adding them to the "contrib" option
+# under [general] section above.
+# [contrib-title-conventional-commits]
+# Specify allowed commit types. For details see: https://www.conventionalcommits.org/
+# types = bugfix,user-story,epic
diff --git a/.swiftformat b/.swiftformat
new file mode 100644
index 0000000..807f64a
--- /dev/null
+++ b/.swiftformat
@@ -0,0 +1,65 @@
+--allman false
+--assetliterals visual-width
+--beforemarks
+--binarygrouping 4,8
+--categorymark "MARK: %c"
+--classthreshold 0
+--closingparen balanced
+--commas always
+--conflictmarkers reject
+--decimalgrouping 3,6
+--elseposition same-line
+--enumthreshold 0
+--exponentcase lowercase
+--exponentgrouping disabled
+--extensionacl on-extension
+--extensionlength 0
+--extensionmark "MARK: - %t + %c"
+--fractiongrouping disabled
+--fragment false
+--funcattributes preserve
+--groupedextension "MARK: %c"
+--guardelse auto
+--header ignore
+--hexgrouping 4,8
+--hexliteralcase uppercase
+--ifdef outdent
+--importgrouping alpha
+--indent 2
+--indentcase false
+--lifecycle
+--linebreaks lf
+--markextensions always
+--marktypes always
+--maxwidth none
+--modifierorder
+--nevertrailing
+--nospaceoperators
+--nowrapoperators
+--octalgrouping 4,8
+--operatorfunc spaced
+--organizetypes class,enum,struct
+--patternlet hoist
+--ranges spaced
+--redundanttype inferred
+--self remove
+--selfrequired
+--semicolons inline
+--shortoptionals always
+--smarttabs enabled
+--stripunusedargs always
+--structthreshold 0
+--tabwidth unspecified
+--trailingclosures
+--trimwhitespace always
+--typeattributes preserve
+--typemark "MARK: - %t"
+--varattributes preserve
+--voidtype void
+--wraparguments preserve
+--wrapcollections preserve
+--wrapconditions preserve
+--wrapparameters preserve
+--wrapreturntype preserve
+--xcodeindentation disabled
+--yodaswap always
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e2467b6
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,15 @@
+
+# CHANGELOG for *Nitroxomat*
+
+
+## [Unreleased]
+
+
+
+## [Version/1.0.0] - 2022-06-08
+
+
+## Version/0.0.0 - 2022-06-08
+
+[Unreleased]: https://github.com/borisboesler/Nitroxomat.git/compare/Version/1.0.0...HEAD
+[Version/1.0.0]: https://github.com/borisboesler/Nitroxomat.git/compare/Version/0.0.0...Version/1.0.0
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index fdddb29..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-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 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.
-
-For more information, please refer to
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..d50fc7c
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,4 @@
+# License
+
+![License](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)
+This work is licensed under a [Creative Commons Attribution-Non Commercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/).
diff --git a/Nitroxomat.xcodeproj/project.pbxproj b/Nitroxomat.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..98f9312
--- /dev/null
+++ b/Nitroxomat.xcodeproj/project.pbxproj
@@ -0,0 +1,696 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 55;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 25AF4CAB285106350030FC09 /* GasMixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CAA285106350030FC09 /* GasMixture.swift */; };
+ 25AF4CAD285106B20030FC09 /* PPO2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CAC285106B20030FC09 /* PPO2View.swift */; };
+ 25AF4CAF285106EA0030FC09 /* FO2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CAE285106EA0030FC09 /* FO2View.swift */; };
+ 25AF4CB1285107150030FC09 /* MODView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CB0285107150030FC09 /* MODView.swift */; };
+ 25AF4CB3285107370030FC09 /* EADView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CB2285107370030FC09 /* EADView.swift */; };
+ 25AF4CB5285107630030FC09 /* LegalNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CB4285107630030FC09 /* LegalNoticeView.swift */; };
+ 25AF4CB7285107850030FC09 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AF4CB6285107850030FC09 /* AboutView.swift */; };
+ 25AF4CBB285109CA0030FC09 /* ObjcExceptionBridging in Frameworks */ = {isa = PBXBuildFile; productRef = 25AF4CBA285109CA0030FC09 /* ObjcExceptionBridging */; };
+ 25AF4CBD285109CA0030FC09 /* XCGLogger in Frameworks */ = {isa = PBXBuildFile; productRef = 25AF4CBC285109CA0030FC09 /* XCGLogger */; };
+ 25F9763A2851023C00510973 /* NitroxomatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F976392851023C00510973 /* NitroxomatApp.swift */; };
+ 25F9763C2851023C00510973 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F9763B2851023C00510973 /* ContentView.swift */; };
+ 25F9763E2851023E00510973 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25F9763D2851023E00510973 /* Assets.xcassets */; };
+ 25F976412851023E00510973 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 25F976402851023E00510973 /* Preview Assets.xcassets */; };
+ 25F9764B2851023E00510973 /* NitroxomatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F9764A2851023E00510973 /* NitroxomatTests.swift */; };
+ 25F976552851023E00510973 /* NitroxomatUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F976542851023E00510973 /* NitroxomatUITests.swift */; };
+ 25F976572851023E00510973 /* NitroxomatUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F976562851023E00510973 /* NitroxomatUITestsLaunchTests.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 25F976472851023E00510973 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 25F9762E2851023C00510973 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 25F976352851023C00510973;
+ remoteInfo = Nitroxomat;
+ };
+ 25F976512851023E00510973 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 25F9762E2851023C00510973 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 25F976352851023C00510973;
+ remoteInfo = Nitroxomat;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 25AF4CAA285106350030FC09 /* GasMixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GasMixture.swift; sourceTree = ""; };
+ 25AF4CAC285106B20030FC09 /* PPO2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPO2View.swift; sourceTree = ""; };
+ 25AF4CAE285106EA0030FC09 /* FO2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FO2View.swift; sourceTree = ""; };
+ 25AF4CB0285107150030FC09 /* MODView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MODView.swift; sourceTree = ""; };
+ 25AF4CB2285107370030FC09 /* EADView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EADView.swift; sourceTree = ""; };
+ 25AF4CB4285107630030FC09 /* LegalNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalNoticeView.swift; sourceTree = ""; };
+ 25AF4CB6285107850030FC09 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; };
+ 25AF4CB8285107E20030FC09 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
+ 25F976362851023C00510973 /* Nitroxomat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nitroxomat.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 25F976392851023C00510973 /* NitroxomatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NitroxomatApp.swift; sourceTree = ""; };
+ 25F9763B2851023C00510973 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 25F9763D2851023E00510973 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 25F976402851023E00510973 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 25F976462851023E00510973 /* NitroxomatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NitroxomatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 25F9764A2851023E00510973 /* NitroxomatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NitroxomatTests.swift; sourceTree = ""; };
+ 25F976502851023E00510973 /* NitroxomatUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NitroxomatUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 25F976542851023E00510973 /* NitroxomatUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NitroxomatUITests.swift; sourceTree = ""; };
+ 25F976562851023E00510973 /* NitroxomatUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NitroxomatUITestsLaunchTests.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 25F976332851023C00510973 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 25AF4CBB285109CA0030FC09 /* ObjcExceptionBridging in Frameworks */,
+ 25AF4CBD285109CA0030FC09 /* XCGLogger in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F976432851023E00510973 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F9764D2851023E00510973 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 25AF4CA8285105A10030FC09 /* Models */ = {
+ isa = PBXGroup;
+ children = (
+ 25AF4CAA285106350030FC09 /* GasMixture.swift */,
+ );
+ path = Models;
+ sourceTree = "";
+ };
+ 25AF4CA9285105D80030FC09 /* Views */ = {
+ isa = PBXGroup;
+ children = (
+ 25AF4CAC285106B20030FC09 /* PPO2View.swift */,
+ 25AF4CAE285106EA0030FC09 /* FO2View.swift */,
+ 25AF4CB0285107150030FC09 /* MODView.swift */,
+ 25AF4CB2285107370030FC09 /* EADView.swift */,
+ 25AF4CB4285107630030FC09 /* LegalNoticeView.swift */,
+ 25AF4CB6285107850030FC09 /* AboutView.swift */,
+ );
+ path = Views;
+ sourceTree = "";
+ };
+ 25F9762D2851023C00510973 = {
+ isa = PBXGroup;
+ children = (
+ 25F976382851023C00510973 /* Nitroxomat */,
+ 25F976492851023E00510973 /* NitroxomatTests */,
+ 25F976532851023E00510973 /* NitroxomatUITests */,
+ 25F976372851023C00510973 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 25F976372851023C00510973 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 25F976362851023C00510973 /* Nitroxomat.app */,
+ 25F976462851023E00510973 /* NitroxomatTests.xctest */,
+ 25F976502851023E00510973 /* NitroxomatUITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 25F976382851023C00510973 /* Nitroxomat */ = {
+ isa = PBXGroup;
+ children = (
+ 25AF4CB8285107E20030FC09 /* Info.plist */,
+ 25AF4CA9285105D80030FC09 /* Views */,
+ 25AF4CA8285105A10030FC09 /* Models */,
+ 25F976392851023C00510973 /* NitroxomatApp.swift */,
+ 25F9763B2851023C00510973 /* ContentView.swift */,
+ 25F9763D2851023E00510973 /* Assets.xcassets */,
+ 25F9763F2851023E00510973 /* Preview Content */,
+ );
+ path = Nitroxomat;
+ sourceTree = "";
+ };
+ 25F9763F2851023E00510973 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 25F976402851023E00510973 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 25F976492851023E00510973 /* NitroxomatTests */ = {
+ isa = PBXGroup;
+ children = (
+ 25F9764A2851023E00510973 /* NitroxomatTests.swift */,
+ );
+ path = NitroxomatTests;
+ sourceTree = "";
+ };
+ 25F976532851023E00510973 /* NitroxomatUITests */ = {
+ isa = PBXGroup;
+ children = (
+ 25F976542851023E00510973 /* NitroxomatUITests.swift */,
+ 25F976562851023E00510973 /* NitroxomatUITestsLaunchTests.swift */,
+ );
+ path = NitroxomatUITests;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 25F976352851023C00510973 /* Nitroxomat */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 25F9765A2851023E00510973 /* Build configuration list for PBXNativeTarget "Nitroxomat" */;
+ buildPhases = (
+ 25AF4CBE28510A480030FC09 /* ShellScript */,
+ 25F976322851023C00510973 /* Sources */,
+ 25F976332851023C00510973 /* Frameworks */,
+ 25F976342851023C00510973 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Nitroxomat;
+ packageProductDependencies = (
+ 25AF4CBA285109CA0030FC09 /* ObjcExceptionBridging */,
+ 25AF4CBC285109CA0030FC09 /* XCGLogger */,
+ );
+ productName = Nitroxomat;
+ productReference = 25F976362851023C00510973 /* Nitroxomat.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 25F976452851023E00510973 /* NitroxomatTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 25F9765D2851023E00510973 /* Build configuration list for PBXNativeTarget "NitroxomatTests" */;
+ buildPhases = (
+ 25F976422851023E00510973 /* Sources */,
+ 25F976432851023E00510973 /* Frameworks */,
+ 25F976442851023E00510973 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 25F976482851023E00510973 /* PBXTargetDependency */,
+ );
+ name = NitroxomatTests;
+ productName = NitroxomatTests;
+ productReference = 25F976462851023E00510973 /* NitroxomatTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 25F9764F2851023E00510973 /* NitroxomatUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 25F976602851023E00510973 /* Build configuration list for PBXNativeTarget "NitroxomatUITests" */;
+ buildPhases = (
+ 25F9764C2851023E00510973 /* Sources */,
+ 25F9764D2851023E00510973 /* Frameworks */,
+ 25F9764E2851023E00510973 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 25F976522851023E00510973 /* PBXTargetDependency */,
+ );
+ name = NitroxomatUITests;
+ productName = NitroxomatUITests;
+ productReference = 25F976502851023E00510973 /* NitroxomatUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 25F9762E2851023C00510973 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1320;
+ LastUpgradeCheck = 1320;
+ TargetAttributes = {
+ 25F976352851023C00510973 = {
+ CreatedOnToolsVersion = 13.2.1;
+ };
+ 25F976452851023E00510973 = {
+ CreatedOnToolsVersion = 13.2.1;
+ TestTargetID = 25F976352851023C00510973;
+ };
+ 25F9764F2851023E00510973 = {
+ CreatedOnToolsVersion = 13.2.1;
+ TestTargetID = 25F976352851023C00510973;
+ };
+ };
+ };
+ buildConfigurationList = 25F976312851023C00510973 /* Build configuration list for PBXProject "Nitroxomat" */;
+ compatibilityVersion = "Xcode 13.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 25F9762D2851023C00510973;
+ packageReferences = (
+ 25AF4CB9285109CA0030FC09 /* XCRemoteSwiftPackageReference "XCGLogger" */,
+ );
+ productRefGroup = 25F976372851023C00510973 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 25F976352851023C00510973 /* Nitroxomat */,
+ 25F976452851023E00510973 /* NitroxomatTests */,
+ 25F9764F2851023E00510973 /* NitroxomatUITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 25F976342851023C00510973 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 25F976412851023E00510973 /* Preview Assets.xcassets in Resources */,
+ 25F9763E2851023E00510973 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F976442851023E00510973 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F9764E2851023E00510973 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 25AF4CBE28510A480030FC09 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\n# for debugging/inspection purposses:\n# for line in $(printenv); do echo $line; done\n\n# for a RELEASE our current HEAD has a tag. we use this tag as version,\n# assuming that the tag format is release/x.y.z[bla]. during development\n# this number is quite a mess. after building the version is correct\nrelease=$(basename $(git describe --tags --always))\n\n# to be conform with some apple requirements (see above bundle version) we use release + hash\n#\U0013 build=$release-$(git log --oneline HEAD^1.. | awk '{print $1 }')\n# or\n# build=$(git describe --tags --always --dirty)\n# build=`git log -n1 --date=short --format=\"%ad (%h)\"`\nbuild=$(git describe --dirty)\n\n# the plist file to be modified\nplist_path=$INFOPLIST_FILE\n\n# the build configuration (Debug|Release)\n#echo $CONFIGURATION\n\n# PlistBuddy executable\nPLISTBUDDY=/usr/libexec/PlistBuddy\n\n# set the \"short version\" to $build\n#$PLISTBUDDY -c \"Add :CFBundleShortVersionString string $release\" \"$plist_path\"\n$PLISTBUDDY -c \"Set :CFBundleShortVersionString $release\" \"$plist_path\"\n\n# if this is a RELEASE build then set CFBundleVersion to $release, else to build\ncase $CONFIGURATION in\n Release) $PLISTBUDDY -c \"Set :CFBundleVersion $release\" \"$plist_path\";;\n *) $PLISTBUDDY -c \"Set :CFBundleVersion $build\" \"$plist_path\";;\nesac\n\n# end-of-file\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 25F976322851023C00510973 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 25F9763A2851023C00510973 /* NitroxomatApp.swift in Sources */,
+ 25AF4CAB285106350030FC09 /* GasMixture.swift in Sources */,
+ 25F9763C2851023C00510973 /* ContentView.swift in Sources */,
+ 25AF4CAD285106B20030FC09 /* PPO2View.swift in Sources */,
+ 25AF4CAF285106EA0030FC09 /* FO2View.swift in Sources */,
+ 25AF4CB3285107370030FC09 /* EADView.swift in Sources */,
+ 25AF4CB1285107150030FC09 /* MODView.swift in Sources */,
+ 25AF4CB5285107630030FC09 /* LegalNoticeView.swift in Sources */,
+ 25AF4CB7285107850030FC09 /* AboutView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F976422851023E00510973 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 25F9764B2851023E00510973 /* NitroxomatTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 25F9764C2851023E00510973 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 25F976552851023E00510973 /* NitroxomatUITests.swift in Sources */,
+ 25F976572851023E00510973 /* NitroxomatUITestsLaunchTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 25F976482851023E00510973 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 25F976352851023C00510973 /* Nitroxomat */;
+ targetProxy = 25F976472851023E00510973 /* PBXContainerItemProxy */;
+ };
+ 25F976522851023E00510973 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 25F976352851023C00510973 /* Nitroxomat */;
+ targetProxy = 25F976512851023E00510973 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 25F976582851023E00510973 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 25F976592851023E00510973 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 25F9765B2851023E00510973 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Nitroxomat/Preview Content\"";
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Nitroxomat/Info.plist;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 25F9765C2851023E00510973 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Nitroxomat/Preview Content\"";
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Nitroxomat/Info.plist;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 25F9765E2851023E00510973 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat.NitroxomatTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nitroxomat.app/Nitroxomat";
+ };
+ name = Debug;
+ };
+ 25F9765F2851023E00510973 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat.NitroxomatTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nitroxomat.app/Nitroxomat";
+ };
+ name = Release;
+ };
+ 25F976612851023E00510973 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat.NitroxomatUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = Nitroxomat;
+ };
+ name = Debug;
+ };
+ 25F976622851023E00510973 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = Y9CRDMAX8T;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.borisboesler.iOS.Nitroxomat.NitroxomatUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = Nitroxomat;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 25F976312851023C00510973 /* Build configuration list for PBXProject "Nitroxomat" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 25F976582851023E00510973 /* Debug */,
+ 25F976592851023E00510973 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 25F9765A2851023E00510973 /* Build configuration list for PBXNativeTarget "Nitroxomat" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 25F9765B2851023E00510973 /* Debug */,
+ 25F9765C2851023E00510973 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 25F9765D2851023E00510973 /* Build configuration list for PBXNativeTarget "NitroxomatTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 25F9765E2851023E00510973 /* Debug */,
+ 25F9765F2851023E00510973 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 25F976602851023E00510973 /* Build configuration list for PBXNativeTarget "NitroxomatUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 25F976612851023E00510973 /* Debug */,
+ 25F976622851023E00510973 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 25AF4CB9285109CA0030FC09 /* XCRemoteSwiftPackageReference "XCGLogger" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/DaveWoodCom/XCGLogger.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 7.0.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 25AF4CBA285109CA0030FC09 /* ObjcExceptionBridging */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 25AF4CB9285109CA0030FC09 /* XCRemoteSwiftPackageReference "XCGLogger" */;
+ productName = ObjcExceptionBridging;
+ };
+ 25AF4CBC285109CA0030FC09 /* XCGLogger */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 25AF4CB9285109CA0030FC09 /* XCRemoteSwiftPackageReference "XCGLogger" */;
+ productName = XCGLogger;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 25F9762E2851023C00510973 /* Project object */;
+}
diff --git a/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Debug].xcscheme b/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Debug].xcscheme
new file mode 100644
index 0000000..3ed522b
--- /dev/null
+++ b/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Debug].xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Release].xcscheme b/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Release].xcscheme
new file mode 100644
index 0000000..d78f118
--- /dev/null
+++ b/Nitroxomat.xcodeproj/xcshareddata/xcschemes/Nitroxomat [Release].xcscheme
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Nitroxomat/Assets.xcassets/AccentColor.colorset/Contents.json b/Nitroxomat/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/Nitroxomat/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Contents.json b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..ff44d2b
--- /dev/null
+++ b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,116 @@
+{
+ "images" : [
+ {
+ "filename" : "Nitroxomat_40.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "Nitroxomat_60.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "Nitroxomat_58.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "Nitroxomat_87.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "Nitroxomat_80.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "Nitroxomat_120-1.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "Nitroxomat_120.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "Nitroxomat_180.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "Nitroxomat_20.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "Nitroxomat_40-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "Nitroxomat_29.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "Nitroxomat_58-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "Nitroxomat_40-2.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "Nitroxomat_80-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "Nitroxomat_76.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "Nitroxomat_152.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "Nitroxomat_167.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "filename" : "Nitroxomat.png",
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat.png
new file mode 100644
index 0000000..ddf2dc6
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120-1.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120-1.png
new file mode 100644
index 0000000..a21203e
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120-1.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120.png
new file mode 100644
index 0000000..a21203e
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_120.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_152.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_152.png
new file mode 100644
index 0000000..0140263
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_152.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_167.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_167.png
new file mode 100644
index 0000000..a6ddd67
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_167.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_180.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_180.png
new file mode 100644
index 0000000..5ebe941
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_180.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_20.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_20.png
new file mode 100644
index 0000000..2069aee
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_20.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_29.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_29.png
new file mode 100644
index 0000000..8fd5195
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_29.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-1.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-1.png
new file mode 100644
index 0000000..0e78849
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-1.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-2.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-2.png
new file mode 100644
index 0000000..0e78849
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40-2.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40.png
new file mode 100644
index 0000000..0e78849
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_40.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58-1.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58-1.png
new file mode 100644
index 0000000..ecacd21
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58-1.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58.png
new file mode 100644
index 0000000..ecacd21
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_58.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_60.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_60.png
new file mode 100644
index 0000000..b9805ed
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_60.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_76.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_76.png
new file mode 100644
index 0000000..fdf081d
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_76.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80-1.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80-1.png
new file mode 100644
index 0000000..be4c9d6
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80-1.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80.png
new file mode 100644
index 0000000..be4c9d6
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_80.png differ
diff --git a/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_87.png b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_87.png
new file mode 100644
index 0000000..9676592
Binary files /dev/null and b/Nitroxomat/Assets.xcassets/AppIcon.appiconset/Nitroxomat_87.png differ
diff --git a/Nitroxomat/Assets.xcassets/Contents.json b/Nitroxomat/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Nitroxomat/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Nitroxomat/ContentView.swift b/Nitroxomat/ContentView.swift
new file mode 100644
index 0000000..ea62e0a
--- /dev/null
+++ b/Nitroxomat/ContentView.swift
@@ -0,0 +1,172 @@
+//
+// ContentView.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import os
+import SwiftUI
+
+// MARK: - General Constant Values
+
+/// default value for minimal partial pressure of O2
+let PPO2Minimum = 1.2
+/// default value for maximal partial pressure of O2
+let PPO2Maximum = 1.6
+/// default value for partial pressure of O2
+let defaultPPO2Value = 1.4
+
+/// default value for minimal fraction of O2
+let FO2Minimum = 0.15
+/// default value for maximal fraction of O2
+let FO2Maximum = 1.0
+/// default value for fraction of O2
+let defaultFO2Value = 0.21
+
+/// default value for minimal MOD
+let MODMinimum = 0.0
+/// default value for maximal MOD
+let MODMaximum = 100.0
+
+/// default value for minimal EAD
+let EADMinimum = 0.0
+/// default value for maximal EAD
+let EADMaximum = 110.0
+
+// MARK: - Nitroxomat Constants
+
+/// the appname
+let AppName = "Nitroxomat"
+
+// MARK: - Nitroxomat Loggers
+
+import XCGLogger
+//In your AppDelegate (or other global file), declare a global constant to the default XCGLogger instance.
+
+let loggerMix = XCGLogger(identifier: "Nitroxomoat (Mixture)")
+let loggerGUI = XCGLogger(identifier: "Nitroxomoat (GUI)")
+
+// MARK: - Nitroxomat User Defaults
+
+/// key to store the PPO2 value
+let KeyPPO2 = "ppO2"
+/// key to store the FO2 value
+let KeyFO2 = "fO2"
+/// key to store if the legal notice should be displayed
+let KeyShowLegalNotice = "showLegalNotice"
+
+let defaults = UserDefaults.standard
+
+/// read partial pressure of O2 from settings or use defaultPPO2Value
+let defaultPPO2: Double = defaults.object(forKey: KeyPPO2) as? Double ?? defaultPPO2Value
+/// read fraction of O2 from settings or use defaultFO2Value
+let defaultFO2: Double = defaults.object(forKey: KeyFO2) as? Double ?? defaultFO2Value
+
+/// check if the user confirmed to be a certified Nitrox diver
+#if DEBUG
+private let defaultShowLegalNotice = true
+#else
+private let defaultShowLegalNotice = defaults.object(forKey: KeyShowLegalNotice) as? Bool ?? true
+#endif
+
+// MARK: - Nitroxomat UI Configurtion
+
+let SliderMinMaxValueColor = Color.black
+
+// MARK: - Nitroxomat Global Variables
+
+/// create a nitrox calculator with default PPO2 and PO2
+var Nitrox = GasMixture(withOxygen: defaultFO2)
+
+// MARK: - Views: ContentView
+
+struct ContentView: View {
+ /// the current PPO2
+ @State private var PPO2Value: Double = defaultPPO2
+ /// the current fO2
+ @State private var FO2Value: Double = defaultFO2
+ /// the current MOD
+ @State private var MODValue: Double = Nitrox.getMOD(withMaxPPO2: defaultPPO2)
+ /// the current EAD
+ @State private var EADValue: Double = Nitrox.getEAD(withMaxPPO2: defaultPPO2)
+
+ @State private var showLegalNotice: Bool = defaultShowLegalNotice
+
+ // the interface on the screen
+ var body: some View {
+ NavigationView {
+ VStack {
+ // the PPO2 Slider
+ PPO2View(PPO2Value: $PPO2Value, MODValue: $MODValue, EADValue: $EADValue)
+
+ // second Spacer
+ Spacer()
+
+ // the FO2 slider with a label
+ FO2View(PPO2Value: $PPO2Value, FO2Value: $FO2Value, MODValue: $MODValue, EADValue: $EADValue)
+
+ // third Spacer
+ Spacer()
+
+ // the MOD slider with a label
+ MODView(PPO2Value: $PPO2Value, FO2Value: $FO2Value, MODValue: $MODValue, EADValue: $EADValue)
+
+ // fourth spacer
+ Spacer()
+
+ // the EAD slider with a label
+ EADView(PPO2Value: $PPO2Value, FO2Value: $FO2Value, MODValue: $MODValue, EADValue: $EADValue)
+ } // VStack
+ .padding(30)
+ .navigationBarTitle(AppName)
+ .navigationBarItems(
+ leading:
+ Button(action: {
+ // reset to default PPO2 and gas-mixture AIR
+ self.PPO2Value = defaultPPO2Value
+ defaults.set(PPO2Value, forKey: KeyPPO2)
+
+ self.FO2Value = defaultFO2Value
+ defaults.set(FO2Value, forKey: KeyFO2)
+ Nitrox.FractionOxygen = FO2Value
+
+ // update UI - does not work
+ self.MODValue = Nitrox.getMOD(withMaxPPO2: self.PPO2Value)
+ self.EADValue = Nitrox.getEAD(withMaxPPO2: self.PPO2Value)
+ loggerGUI.debug("reset sliders")
+ loggerMix.debug("MOD (maxPPO2:\(self.PPO2Value), fO2:\(Nitrox.FractionOxygen)) = \(self.MODValue)")
+ loggerMix.debug("EAD (maxPPO2:\(self.PPO2Value), MOD:\(self.MODValue) = \(self.EADValue)")
+
+ }) {
+ Text("Reset")
+ },
+ trailing:
+ NavigationLink(destination: AboutView()) {
+ HStack {
+ Image(systemName: "info.circle")
+ .imageScale(.large)
+ }
+ }
+ )
+ } // NavigationView
+ .navigationViewStyle(StackNavigationViewStyle())
+ // let the user confirm that (s)he is a certified nitrox diver
+ .sheet(isPresented: self.$showLegalNotice,
+ onDismiss: {
+ self.showLegalNotice = false
+ defaults.set(self.showLegalNotice, forKey: KeyShowLegalNotice)
+ loggerGUI.debug("set defaults.\(KeyShowLegalNotice) notice to \(self.showLegalNotice)")
+ }) {
+ LegalNoticeView()
+ }
+ } // var body: some View
+} // struct ContentView: View
+
+// MARK: - ContentView_Previews
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ }
+}
diff --git a/Nitroxomat/Info.plist b/Nitroxomat/Info.plist
new file mode 100644
index 0000000..dbb1428
--- /dev/null
+++ b/Nitroxomat/Info.plist
@@ -0,0 +1,15 @@
+
+
+
+
+ CFBundleShortVersionString
+ 0.0.0
+ CFBundleVersion
+ Version/0.0.0
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+
+
+
diff --git a/Nitroxomat/Models/GasMixture.swift b/Nitroxomat/Models/GasMixture.swift
new file mode 100644
index 0000000..82c66a5
--- /dev/null
+++ b/Nitroxomat/Models/GasMixture.swift
@@ -0,0 +1,131 @@
+//
+// GasMixture.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import Foundation
+
+// MARK: Gaxmixture Constants
+
+/// the fraction of oxygen in air
+let AirFractionOxygen = 0.21
+
+// MARK: - GasMixture
+
+/// a gas mixture can be a combination of three gases 1) oxygen
+/// 2) nitrogen and 3) helium. the fraction of all three gases
+/// is between 0 and 1; the sum of all fractions equals 1; the
+/// fraction of nitrogen is a computed attribute
+class GasMixture {
+ // MARK: Lifecycle
+
+ /// default init for AIR
+ convenience init() {
+ self.init(withOxygen: AirFractionOxygen)
+ }
+
+ /// init for some Nitrox
+ /// - Parameter FractionOxygen: the fraction of O2 in this gas mixture
+ convenience init(withOxygen FractionOxygen: Double) {
+ self.init(withOxygen: FractionOxygen, withHelium: 0.0)
+ }
+
+ /// init for some Trimix
+ /// - Parameters:
+ /// - FractionOxygen: the fraction of O2 in this gas mixture
+ /// - FractionHelium: the fraction of Helium in this gas mixture
+ init(withOxygen FractionOxygen: Double, withHelium FractionHelium: Double) {
+ // setter should assert that FractionOxygen + FractionHelium <= 1.0 is true
+ self.FractionOxygen = FractionOxygen
+ self.FractionHelium = FractionHelium
+ }
+
+ // MARK: Properties with getter/setter Methods
+
+ /// fraction of oxygen in this gas mixture
+ var FractionOxygen: Double {
+ didSet {
+ if FractionOxygen < 0 {
+ FractionOxygen = AirFractionOxygen
+ } else if FractionOxygen > 1.0 {
+ FractionOxygen = 1.0 - FractionHelium
+ }
+ // re-size to fit current fraction of helium
+ if FractionOxygen + FractionHelium > 1.0 {
+ FractionOxygen = 1.0 - FractionHelium
+ }
+ FractionOxygen = round(FractionOxygen * 100.0) / 100.0
+ }
+ }
+
+ /// fraction of helium in this gas mixture
+ var FractionHelium: Double {
+ didSet {
+ if FractionHelium < 0 {
+ FractionHelium = 0.0
+ } else if FractionHelium > 1.0 {
+ FractionHelium = 1.0 - FractionOxygen
+ } else if FractionOxygen + FractionHelium > 1.0 {
+ FractionHelium = 1.0 - FractionOxygen
+ }
+ FractionHelium = round(FractionHelium * 100.0) / 100.0
+ }
+ }
+
+ /// fraction of nitrogen in this gas mixture
+ var FractionNitrogen: Double {
+ return (1.0 - FractionOxygen - FractionHelium)
+ }
+
+ // MARK: - Public Methods
+
+ /// get the Maximum Operation of Depth (MOD) for this gas mixture and a given maximum partial pressure of O2
+ /// - Parameter maxPPO2: the maximum partial pressure of O2
+ /// - Returns: the MOD for this gas mixture and a given maximum partial pressure of O2
+ func getMOD(withMaxPPO2 maxPPO2: Double) -> Double {
+ let MOD = ((maxPPO2 / FractionOxygen) - 1.0) * 10.0
+ return MOD
+ }
+
+ /// get the Equivalent Air Depth (EAD) for this gas mixture and a given maximum partial pressure of O2
+ /// - Parameter maxPPO2: the maximum partial pressure of O2
+ /// - Returns: the EAD for this gas mixture and the given maximal partial pressure of O2
+ func getEAD(withMaxPPO2 maxPPO2: Double) -> Double {
+ var EAD = (((100.0 - (FractionOxygen * 100.0)) * (getMOD(withMaxPPO2: maxPPO2) + 10.0)) / 79.0) - 10.0
+ if EAD < 0.0 {
+ EAD = 0.0
+ }
+ return EAD
+ }
+
+ /// END = ((fN2 * (Tiefe + 10)) / 0,79) - 10m
+
+ /// Equivalent Narcotic Depth
+ /// - Parameter Depth: the depth for which the END is computed
+ /// - Returns: the END for the given depth
+ func getEND(withDepth Depth: Double) -> Double {
+ var END = ((FractionNitrogen * (Depth + 10.0)) / 0.79) - 10.0
+ if END < 0.0 {
+ END = 0.0
+ }
+ return END
+ }
+
+ /// get the best oxygen fraction for this given gas mixture and a given maximum operation of depth
+ /// - Parameters:
+ /// - MaxDepth: the maximum operation of depth
+ /// - PPO2Max: the maximum partial pressure of O2
+ /// - Returns: the best fraction of oxygen for this gas mixture and the given maximum of depth
+ func getBestFractionO2(forMOD MaxDepth: Double, withPPO2 PPO2Max: Double) -> Double {
+ var BestFracO2 = PPO2Max / ((MaxDepth / 10.0) + 1.0)
+ // rounding
+ BestFracO2 = round(BestFracO2 * 100.0) / 100.0
+ // limit
+ if BestFracO2 > 1.0 {
+ BestFracO2 = 1.0
+ }
+ return BestFracO2
+ }
+} // class GasMixture
diff --git a/Nitroxomat/NitroxomatApp.swift b/Nitroxomat/NitroxomatApp.swift
new file mode 100644
index 0000000..b9dd4d6
--- /dev/null
+++ b/Nitroxomat/NitroxomatApp.swift
@@ -0,0 +1,17 @@
+//
+// NitroxomatApp.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+@main
+struct NitroxomatApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/Nitroxomat/Preview Content/Preview Assets.xcassets/Contents.json b/Nitroxomat/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Nitroxomat/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Nitroxomat/Views/AboutView.swift b/Nitroxomat/Views/AboutView.swift
new file mode 100644
index 0000000..8ce0866
--- /dev/null
+++ b/Nitroxomat/Views/AboutView.swift
@@ -0,0 +1,50 @@
+//
+// AboutView.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+private let BundleVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "no build"
+private let BundleShortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "no version"
+
+// MARK: - Views: AboutView
+
+struct AboutView: View {
+ var body: some View {
+ VStack {
+ Spacer()
+ Text(AppName)
+ .font(.title)
+ Spacer()
+ Text("Copyright ©2022 Boris Boesler")
+ .font(.title2)
+ Spacer()
+ Text("Version: \(BundleShortVersion)")
+ .font(.title2)
+#if DEBUG
+ Spacer()
+ VStack {
+ Text("DEBUG Build")
+ Text("Bundle version: \(BundleVersion)")
+ }
+ .padding(5)
+ .background(Color.red)
+ .cornerRadius(10)
+#endif
+ Spacer()
+ }
+ .padding(5)
+ .navigationBarTitle("About " + AppName, displayMode: .inline)
+ }
+}
+
+// MARK: - AboutView_Previews
+
+struct AboutView_Previews: PreviewProvider {
+ static var previews: some View {
+ AboutView()
+ }
+}
diff --git a/Nitroxomat/Views/EADView.swift b/Nitroxomat/Views/EADView.swift
new file mode 100644
index 0000000..712323c
--- /dev/null
+++ b/Nitroxomat/Views/EADView.swift
@@ -0,0 +1,58 @@
+//
+// EADView.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+// MARK: - Views: EADView
+
+struct EADView: View {
+ @Binding var PPO2Value: Double
+ @Binding var FO2Value: Double
+ @Binding var MODValue: Double
+ @Binding var EADValue: Double
+
+ var body: some View {
+ VStack {
+ // Text("EAD: \(Nitrox.getEAD(withMaxPPO2: PPO2Value), specifier: "%3.1f")m")
+ Text("EAD: \(EADValue + 0.05, specifier: "%3.1f")m") // round down
+ /* */
+ Slider(value: $EADValue, in: EADMinimum ... EADMaximum, step: 1.0,
+ minimumValueLabel: Text("\(Int(EADMinimum))m").foregroundColor(SliderMinMaxValueColor),
+ maximumValueLabel: Text("\(Int(EADMaximum))m").foregroundColor(SliderMinMaxValueColor))
+ { Text("") } // don't know what this text is for, it does not appear, but is needed
+ .accentColor(Color.blue)
+ .disabled(true)
+ /* */
+ }
+ // .background(Color.gray)
+ // or
+ // .border(Color.purple, width: 5/*, cornerRadius: 20*/)
+ // or
+ // .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
+ // or
+ // .padding(5).background(Color.gray).cornerRadius(10)
+ } // var body
+}
+
+#if TRUE_EQUALS_FALSE
+struct EADView_Previews: PreviewProvider {
+ /// the current PPO2
+ @State private var PPO2Value: Double = defaultPPO2
+ /// the current fO2
+ @State private var FO2Value: Double = defaultFO2
+ /// the current MOD
+ @State private var MODValue: Double = Nitrox.getMOD(withMaxPPO2: defaultPPO2)
+ /// the current EAD
+ @State private var EADValue: Double = Nitrox.getEAD(withMaxPPO2: defaultPPO2)
+
+ static var previews: some View {
+ // FIXME: How do we fix this?
+ EADView(PPO2Value: $PPO2Value, FO2Value: $FO2Value,
+ MODValue: $MODValue, EADValue: $EADValue)
+ }
+}
+#endif
diff --git a/Nitroxomat/Views/FO2View.swift b/Nitroxomat/Views/FO2View.swift
new file mode 100644
index 0000000..39ee08a
--- /dev/null
+++ b/Nitroxomat/Views/FO2View.swift
@@ -0,0 +1,58 @@
+//
+// FO2View.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+// MARK: - Views: FO2View
+
+struct FO2View: View {
+ @Binding var PPO2Value: Double
+ @Binding var FO2Value: Double
+ @Binding var MODValue: Double
+ @Binding var EADValue: Double
+
+ var body: some View {
+ VStack {
+ Text("fO2: \(Int(FO2Value * 100.0))%")
+ Slider(value: $FO2Value, in: FO2Minimum ... FO2Maximum, step: 0.010,
+ onEditingChanged: { _ in
+ // round FO2Value properly, this is a bugfix
+ FO2Value = Double(Int(FO2Value * 100.0)) / 100.0
+ // store value in user defaults
+ defaults.set(FO2Value, forKey: KeyFO2)
+ // set FO2 in gas mixture
+ Nitrox.FractionOxygen = FO2Value
+ // log using slider
+ loggerGUI.debug("slider fO2 moved to \(FO2Value)")
+
+ // update UI - does not work
+ self.MODValue = Nitrox.getMOD(withMaxPPO2: self.PPO2Value)
+ self.EADValue = Nitrox.getEAD(withMaxPPO2: self.PPO2Value)
+
+ loggerMix.debug("MOD (maxPPO2:\(self.PPO2Value), fO2:\(Nitrox.FractionOxygen)) = \(self.MODValue)")
+ loggerMix.debug("EAD (maxPPO2:\(self.PPO2Value), MOD:\(self.MODValue) = \(self.EADValue)")
+ },
+ minimumValueLabel: Text("\(Int(FO2Minimum * 100.0))%").foregroundColor(SliderMinMaxValueColor),
+ maximumValueLabel: Text("\(Int(FO2Maximum * 100.0))%").foregroundColor(SliderMinMaxValueColor)) { Text("") } // don't know what this text is for, it does not appear, but is needed
+ .accentColor(Color.green)
+ }
+ // .background(Color.gray)
+ // or
+ // .border(Color.purple, width: 5/*, cornerRadius: 20*/)
+ // or
+ // .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
+ } // var body
+}
+
+#if TRUE_EQUALS_FALSE
+struct FO2View_Previews: PreviewProvider {
+ static var previews: some View {
+ // FIXME: How do we fix this?
+ FO2View(PPO2Value: $PPO2Value, FO2Value: $FO2Value, MODValue: $MODValue, EADValue: $EADValue)
+ }
+}
+#endif
diff --git a/Nitroxomat/Views/LegalNoticeView.swift b/Nitroxomat/Views/LegalNoticeView.swift
new file mode 100644
index 0000000..ef6840e
--- /dev/null
+++ b/Nitroxomat/Views/LegalNoticeView.swift
@@ -0,0 +1,50 @@
+//
+// LegalNoticeView.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+// MARK: - LegalNoticeView
+
+struct LegalNoticeView: View {
+ @Environment(\.presentationMode) var presentationMode
+
+ var body: some View {
+ VStack {
+ Spacer()
+
+ Text("The user confirms to be a certified Nitrox diver.")
+ .multilineTextAlignment(.center)
+
+ Text("The software is for entertaining purposes.")
+ .multilineTextAlignment(.center)
+
+ Text("Use at your own risk.")
+ .multilineTextAlignment(.center)
+
+ Text("The authors of this software are not reliable for any damage or anything else.")
+ .multilineTextAlignment(.center)
+
+ Spacer()
+
+ Button(action: {
+ print("OK")
+ self.presentationMode.wrappedValue.dismiss()
+ }) {
+ Text("I'm a certified Nitrox diver")
+ }.padding(.bottom, 50)
+ }
+ .padding(50)
+ }
+}
+
+// MARK: - LegalNoticeView_Previews
+
+struct LegalNoticeView_Previews: PreviewProvider {
+ static var previews: some View {
+ LegalNoticeView()
+ }
+}
diff --git a/Nitroxomat/Views/MODView.swift b/Nitroxomat/Views/MODView.swift
new file mode 100644
index 0000000..5f6e951
--- /dev/null
+++ b/Nitroxomat/Views/MODView.swift
@@ -0,0 +1,67 @@
+//
+// MODView.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+// MARK: - Views: ModView
+
+struct MODView: View {
+ @Binding var PPO2Value: Double
+ @Binding var FO2Value: Double
+ @Binding var MODValue: Double
+ @Binding var EADValue: Double
+
+ var body: some View {
+ VStack {
+ Text("MOD: \(MODValue, specifier: "%3.1f")m") // round up
+ Slider(value: $MODValue, in: MODMinimum ... MODMaximum, step: 1.0,
+ onEditingChanged: { _ in
+ // TODO:
+ FO2Value = Nitrox.getBestFractionO2(forMOD: MODValue, withPPO2: PPO2Value)
+ defaults.set(FO2Value, forKey: KeyFO2)
+ // set FO2 in gas mixture
+ Nitrox.FractionOxygen = FO2Value
+ loggerGUI.debug("slider MOD moved to \(MODValue)")
+
+ // update UI - does not work
+ self.MODValue = Nitrox.getMOD(withMaxPPO2: self.PPO2Value)
+ self.EADValue = Nitrox.getEAD(withMaxPPO2: self.PPO2Value)
+
+ loggerMix.debug("MOD (maxPPO2:\(self.PPO2Value), fO2:\(Nitrox.FractionOxygen)) = \(self.MODValue)")
+ loggerMix.debug("EAD (maxPPO2:\(self.PPO2Value), MOD:\(self.MODValue) = \(self.EADValue)")
+ },
+ minimumValueLabel: Text("\(Int(MODMinimum))m").foregroundColor(SliderMinMaxValueColor),
+ maximumValueLabel: Text("\(Int(MODMaximum))m").foregroundColor(SliderMinMaxValueColor)) { Text("") } // don't know what tis text is for, it does not appear
+ .accentColor(Color.blue)
+ }
+ // .background(Color.gray)
+ // or
+ // .border(Color.purple, width: 5/*, cornerRadius: 20*/)
+ // or
+ // .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
+ // or
+ // .padding(5).background(Color.blue).cornerRadius(10)
+ } // var body
+}
+
+#if TRUE_EQUALS_FALSE
+struct MODView_Previews: PreviewProvider {
+ /// the current PPO2
+ @State private var PPO2Value: Double = defaultPPO2
+ /// the current fO2
+ @State private var FO2Value: Double = defaultFO2
+ /// the current MOD
+ @State private var MODValue: Double = Nitrox.getMOD(withMaxPPO2: defaultPPO2)
+ /// the current EAD
+ @State private var EADValue: Double = Nitrox.getEAD(withMaxPPO2: defaultPPO2)
+
+ static var previews: some View {
+ // FIXME: How do we fix this?
+ MODView(PPO2Value: $PPO2Value, FO2Value: $FO2Value, MODValue: $MODValue, EADValue: $EADValue)
+ }
+}
+#endif
diff --git a/Nitroxomat/Views/PPO2View.swift b/Nitroxomat/Views/PPO2View.swift
new file mode 100644
index 0000000..a207c82
--- /dev/null
+++ b/Nitroxomat/Views/PPO2View.swift
@@ -0,0 +1,63 @@
+//
+// PPO2View.swift
+// Nitroxomat
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import SwiftUI
+
+// MARK: - Views: PPO2View
+
+struct PPO2View: View {
+ @Binding var PPO2Value: Double
+ @Binding var MODValue: Double
+ @Binding var EADValue: Double
+
+ var body: some View {
+ VStack {
+ Text("ppO2 (bar)")
+ // TODO: need continuous updates: https://stackoverflow.com/questions/56725084/swiftui-how-to-get-continuous-updates-from-slider
+ Slider(value: $PPO2Value, in: PPO2Minimum ... PPO2Maximum, step: 0.1,
+ onEditingChanged: { _ in
+ // store value in user defaults
+ defaults.set(PPO2Value, forKey: KeyPPO2)
+ // log using slider
+ loggerGUI.debug("slider ppO2 moved to \(PPO2Value)")
+
+ // update UI
+ self.MODValue = Nitrox.getMOD(withMaxPPO2: self.PPO2Value)
+ self.EADValue = Nitrox.getEAD(withMaxPPO2: self.PPO2Value)
+
+ loggerMix.debug("MOD (maxPPO2:\(self.PPO2Value), fO2:\(Nitrox.FractionOxygen)) = \(self.MODValue)")
+ loggerMix.debug("EAD (maxPPO2:\(self.PPO2Value), MOD:\(self.MODValue) = \(self.EADValue)")
+ }
+ // minimumValueLabel and maximumValueLabel have no advantage here
+ ) { Text("") } // don't know what this text is for, it does not appear, but is needed
+ .accentColor(Color.green)
+
+ HStack {
+ // this is not nice, but better than fixed
+ Text("\(PPO2Minimum, specifier: "%1.1f")")
+ ForEach(Int(PPO2Minimum * 10.0 + 1.0) ... Int(PPO2Maximum * 10.0), id: \.self) { ppo2 in
+ HStack { Spacer(); Text("\(Float(ppo2) / 10.0, specifier: "%1.1f")") }
+ }
+ }
+ }
+ // .background(Color.green, cornerRadius: 20)
+ // or
+ // .border(Color.green, width: 5 /* , cornerRadius: 20 */ )
+ // or
+ // .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
+ // or
+ // .padding(5).background(Color.green).cornerRadius(10)
+ } // var body
+}
+
+#if TRUE_EQUALS_FALSE
+ struct PPO2View_Previews: PreviewProvider {
+ static var previews: some View {
+ PPO2View(PPO2Value: $PPO2Value, MODValue: $MODValue, EADValue: $EADValue)
+ }
+ }
+#endif
diff --git a/NitroxomatTests/NitroxomatTests.swift b/NitroxomatTests/NitroxomatTests.swift
new file mode 100644
index 0000000..45a0ad0
--- /dev/null
+++ b/NitroxomatTests/NitroxomatTests.swift
@@ -0,0 +1,36 @@
+//
+// NitroxomatTests.swift
+// NitroxomatTests
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import XCTest
+@testable import Nitroxomat
+
+class NitroxomatTests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/NitroxomatUITests/NitroxomatUITests.swift b/NitroxomatUITests/NitroxomatUITests.swift
new file mode 100644
index 0000000..46fe37c
--- /dev/null
+++ b/NitroxomatUITests/NitroxomatUITests.swift
@@ -0,0 +1,42 @@
+//
+// NitroxomatUITests.swift
+// NitroxomatUITests
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import XCTest
+
+class NitroxomatUITests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ // In UI tests it is usually best to stop immediately when a failure occurs.
+ continueAfterFailure = false
+
+ // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // UI tests must launch the application that they test.
+ let app = XCUIApplication()
+ app.launch()
+
+ // Use recording to get started writing UI tests.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testLaunchPerformance() throws {
+ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
+ // This measures how long it takes to launch your application.
+ measure(metrics: [XCTApplicationLaunchMetric()]) {
+ XCUIApplication().launch()
+ }
+ }
+ }
+}
diff --git a/NitroxomatUITests/NitroxomatUITestsLaunchTests.swift b/NitroxomatUITests/NitroxomatUITestsLaunchTests.swift
new file mode 100644
index 0000000..5e3dd37
--- /dev/null
+++ b/NitroxomatUITests/NitroxomatUITestsLaunchTests.swift
@@ -0,0 +1,32 @@
+//
+// NitroxomatUITestsLaunchTests.swift
+// NitroxomatUITests
+//
+// Created by Boris Boesler on 08.06.22.
+//
+
+import XCTest
+
+class NitroxomatUITestsLaunchTests: XCTestCase {
+
+ override class var runsForEachTargetApplicationUIConfiguration: Bool {
+ true
+ }
+
+ override func setUpWithError() throws {
+ continueAfterFailure = false
+ }
+
+ func testLaunch() throws {
+ let app = XCUIApplication()
+ app.launch()
+
+ // Insert steps here to perform after app launch but before taking a screenshot,
+ // such as logging into a test account or navigating somewhere in the app
+
+ let attachment = XCTAttachment(screenshot: app.screenshot())
+ attachment.name = "Launch Screen"
+ attachment.lifetime = .keepAlways
+ add(attachment)
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9d4ef2c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# Nitroxomat
+
+Simple app that shows relation between maximum ppO2, breathing gas mix
+and maximum depth.
+
+
+## License
+
+![License](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)
+This work is licensed under a [Creative Commons Attribution-Non Commercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/).