diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..27bf70574c2297bd84bf4f461e9c378f8489fea5
Binary files /dev/null and b/.DS_Store differ
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 8f04207db8961e660763b77544f84b7fe19ae0ce..0c30be4d96fde8050959618d673d7dfdd13667e7 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,35 @@
-# Nmo Teams App
+# [Traccar Client for iOS](https://www.traccar.org/client)
 
+[![Download on the App Store](http://www.tananaev.com/badges/app-store.svg)](https://itunes.apple.com/app/traccar-client/id843156974)
 
+## Overview
 
-## Getting started
+Traccar Client is an iOS GPS tracking application. It can work with Traccar open source server software.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+## Build
 
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+Project uses CocoaPods for dependencies management. To build the project you need to download dependencies:
 
 ```
-cd existing_repo
-git remote add origin https://git.beinmedia.com/aziz97/nmo-teams-app.git
-git branch -M main
-git push -uf origin main
+pod install
 ```
 
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://git.beinmedia.com/aziz97/nmo-teams-app/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
+## Team
 
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+- Anton Tananaev ([anton@traccar.org](mailto:anton@traccar.org))
 
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+## License
 
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+    Apache License, Version 2.0
 
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
 
-## License
-For open source projects, say how it is licensed.
+        http://www.apache.org/licenses/LICENSE-2.0
 
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
diff --git a/TraccarClient.xcodeproj/project.pbxproj b/TraccarClient.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000000000000000000000000000000000..495d1676dc11b7c9ca7097ef62cf3fb882d634cb
--- /dev/null
+++ b/TraccarClient.xcodeproj/project.pbxproj
@@ -0,0 +1,920 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 54;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		5E394EBE28A9CC7600396F33 /* BatteryStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */; };
+		5E716A271F63A0B100A2DBC3 /* DistanceCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */; };
+		5E716A291F63A45A00A2DBC3 /* RequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A281F63A45A00A2DBC3 /* RequestManager.swift */; };
+		5E716A2B1F63A60800A2DBC3 /* ProtocolFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */; };
+		5E716A2E1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */; };
+		5E716A301F63A9F600A2DBC3 /* RequestManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */; };
+		5EE3CA342998948F002C86E4 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA332998948F002C86E4 /* FirebaseAnalytics */; };
+		5EE3CA362998948F002C86E4 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */; };
+		5EE3CA39299894B9002C86E4 /* InAppSettingsKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5EE3CA38299894B9002C86E4 /* InAppSettingsKit */; };
+		CB4197931F674A3E008F301C /* DatabaseHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197921F674A3E008F301C /* DatabaseHelper.swift */; };
+		CB4197951F675518008F301C /* CoreDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197941F675518008F301C /* CoreDataTests.swift */; };
+		CB4197971F676B52008F301C /* DatabaseHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197961F676B52008F301C /* DatabaseHelperTests.swift */; };
+		CB4197991F67724F008F301C /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4197981F67724F008F301C /* NetworkManager.swift */; };
+		CB7ED0801F6602CD00A33FCF /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED07F1F6602CD00A33FCF /* Position.swift */; };
+		CB7ED0821F661B4F00A33FCF /* PositionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */; };
+		CB7ED0841F662BAF00A33FCF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */; };
+		CBAA0F7D1F68E14C008BBBBE /* StatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */; };
+		CBAA0F7F1F68E807008BBBBE /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */; };
+		CBAA0F811F68EB62008BBBBE /* TrackingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBAA0F801F68EB62008BBBBE /* TrackingController.swift */; };
+		CBCE82EF1B8D253600A7318B /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBCE82EE1B8D253600A7318B /* CoreData.framework */; };
+		CBCE82F21B8D265800A7318B /* TraccarClient.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */; };
+		CBDEEB8A1B8EB6A4006BC126 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */; };
+		CE5899C81B115C9100ED70D2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE5899C71B115C9100ED70D2 /* Images.xcassets */; };
+		CED4871C17DB1BF6007FCF57 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871B17DB1BF6007FCF57 /* UIKit.framework */; };
+		CED4871E17DB1BF6007FCF57 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871D17DB1BF6007FCF57 /* Foundation.framework */; };
+		CED4872017DB1BF6007FCF57 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */; };
+		CED4879917DB1DF4007FCF57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED4879817DB1DF4007FCF57 /* MessageUI.framework */; };
+		CED4879B17DB1E61007FCF57 /* InAppSettings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */; };
+		CEDB048717ED50C1000E7EDF /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEDB048617ED50C1000E7EDF /* CoreLocation.framework */; };
+		CEF643271B919FFA00195CEA /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF643241B919FFA00195CEA /* LaunchScreen.xib */; };
+		CEF6433C1B91AA9400195CEA /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */; };
+		CEF6433D1B91AA9400195CEA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CEF643371B91A94600195CEA /* Localizable.strings */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		CEE286D41B80839700AE7E83 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CED4871017DB1BF6007FCF57 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CED4871717DB1BF6007FCF57;
+			remoteInfo = TraccarClient;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CEFA94EF1EA2EE31009B5809 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStatus.swift; sourceTree = "<group>"; };
+		5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistanceCalculator.swift; sourceTree = "<group>"; };
+		5E716A281F63A45A00A2DBC3 /* RequestManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestManager.swift; sourceTree = "<group>"; };
+		5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolFormatter.swift; sourceTree = "<group>"; };
+		5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolFormatterTests.swift; sourceTree = "<group>"; };
+		5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestManagerTests.swift; sourceTree = "<group>"; };
+		CB4197921F674A3E008F301C /* DatabaseHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHelper.swift; sourceTree = "<group>"; };
+		CB4197941F675518008F301C /* CoreDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataTests.swift; sourceTree = "<group>"; };
+		CB4197961F676B52008F301C /* DatabaseHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHelperTests.swift; sourceTree = "<group>"; };
+		CB4197981F67724F008F301C /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
+		CB7ED07F1F6602CD00A33FCF /* Position.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Position.swift; sourceTree = "<group>"; };
+		CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PositionProvider.swift; sourceTree = "<group>"; };
+		CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusViewController.swift; sourceTree = "<group>"; };
+		CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
+		CBAA0F801F68EB62008BBBBE /* TrackingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingController.swift; sourceTree = "<group>"; };
+		CBCE82EE1B8D253600A7318B /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+		CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TraccarClient.xcdatamodel; sourceTree = "<group>"; };
+		CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+		CE2FA5181CB935C9007F86B4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B80FE1EC71CAE00D1C41D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/MainStoryboard.strings"; sourceTree = "<group>"; };
+		CE4B80FF1EC71CAE00D1C41D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
+		CE4B81001EC71CCF00D1C41D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B81011EC71CCF00D1C41D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE4B81021EC71CFF00D1C41D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B81031EC71CFF00D1C41D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE4B81041EC71D1700D1C41D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B81051EC71D1700D1C41D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE4B81061EC71D3600D1C41D /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B81071EC71D3600D1C41D /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE4B81081EC71D4300D1C41D /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B81091EC71D4300D1C41D /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE4B810A1EC71D4800D1C41D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CE4B810B1EC71D4800D1C41D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CE5899C71B115C9100ED70D2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		CED4871817DB1BF6007FCF57 /* TraccarClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TraccarClient.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		CED4871B17DB1BF6007FCF57 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		CED4871D17DB1BF6007FCF57 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+		CED4872317DB1BF6007FCF57 /* TraccarClient-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TraccarClient-Info.plist"; sourceTree = "<group>"; };
+		CED4872917DB1BF6007FCF57 /* TraccarClient-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TraccarClient-Prefix.pch"; sourceTree = "<group>"; };
+		CED4879817DB1DF4007FCF57 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
+		CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = InAppSettings.bundle; sourceTree = "<group>"; };
+		CEDB048617ED50C1000E7EDF /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
+		CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TraccarClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		CEE286D11B80839700AE7E83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CEECE4B91B91AE2B00EB6F4A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CEECE4BA1B91AE2C00EB6F4A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CEECE4BB1B91AE2D00EB6F4A /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CEF254DD1B905266001DEFD2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CEF254DF1B9052EB001DEFD2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainStoryboard.storyboard; sourceTree = "<group>"; };
+		CEF643241B919FFA00195CEA /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = "<group>"; };
+		CEF6432A1B91A48B00195CEA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CEF6432C1B91A49400195CEA /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CEF6432E1B91A49700195CEA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CEF643321B91A4BF00195CEA /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/MainStoryboard.strings; sourceTree = "<group>"; };
+		CEF643361B91A94600195CEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CEF643381B91A95700195CEA /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
+		CEF643391B91A98300195CEA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CED4871517DB1BF6007FCF57 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CBDEEB8A1B8EB6A4006BC126 /* SystemConfiguration.framework in Frameworks */,
+				CBCE82EF1B8D253600A7318B /* CoreData.framework in Frameworks */,
+				5EE3CA342998948F002C86E4 /* FirebaseAnalytics in Frameworks */,
+				CEDB048717ED50C1000E7EDF /* CoreLocation.framework in Frameworks */,
+				CED4879917DB1DF4007FCF57 /* MessageUI.framework in Frameworks */,
+				CED4871C17DB1BF6007FCF57 /* UIKit.framework in Frameworks */,
+				5EE3CA362998948F002C86E4 /* FirebaseCrashlytics in Frameworks */,
+				5EE3CA39299894B9002C86E4 /* InAppSettingsKit in Frameworks */,
+				CED4871E17DB1BF6007FCF57 /* Foundation.framework in Frameworks */,
+				CED4872017DB1BF6007FCF57 /* CoreGraphics.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CEE286CB1B80839600AE7E83 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CED4870F17DB1BF6007FCF57 = {
+			isa = PBXGroup;
+			children = (
+				CED4872117DB1BF6007FCF57 /* TraccarClient */,
+				CEE286CF1B80839700AE7E83 /* TraccarClientTests */,
+				CED4871A17DB1BF6007FCF57 /* Frameworks */,
+				CED4871917DB1BF6007FCF57 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CED4871917DB1BF6007FCF57 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CED4871817DB1BF6007FCF57 /* TraccarClient.app */,
+				CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CED4871A17DB1BF6007FCF57 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				CBDEEB891B8EB6A4006BC126 /* SystemConfiguration.framework */,
+				CBCE82EE1B8D253600A7318B /* CoreData.framework */,
+				CEDB048617ED50C1000E7EDF /* CoreLocation.framework */,
+				CED4879817DB1DF4007FCF57 /* MessageUI.framework */,
+				CED4871B17DB1BF6007FCF57 /* UIKit.framework */,
+				CED4871D17DB1BF6007FCF57 /* Foundation.framework */,
+				CED4871F17DB1BF6007FCF57 /* CoreGraphics.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		CED4872117DB1BF6007FCF57 /* TraccarClient */ = {
+			isa = PBXGroup;
+			children = (
+				CE5899C71B115C9100ED70D2 /* Images.xcassets */,
+				CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */,
+				CEF643241B919FFA00195CEA /* LaunchScreen.xib */,
+				CEF643371B91A94600195CEA /* Localizable.strings */,
+				CED4872217DB1BF6007FCF57 /* Supporting Files */,
+				CB7ED0831F662BAF00A33FCF /* AppDelegate.swift */,
+				CB4197921F674A3E008F301C /* DatabaseHelper.swift */,
+				5E716A261F63A0B100A2DBC3 /* DistanceCalculator.swift */,
+				CBAA0F7E1F68E807008BBBBE /* MainViewController.swift */,
+				CB4197981F67724F008F301C /* NetworkManager.swift */,
+				CB7ED07F1F6602CD00A33FCF /* Position.swift */,
+				5E394EBD28A9CC7600396F33 /* BatteryStatus.swift */,
+				CB7ED0811F661B4F00A33FCF /* PositionProvider.swift */,
+				5E716A2A1F63A60800A2DBC3 /* ProtocolFormatter.swift */,
+				5E716A281F63A45A00A2DBC3 /* RequestManager.swift */,
+				CBAA0F7C1F68E14C008BBBBE /* StatusViewController.swift */,
+				CBAA0F801F68EB62008BBBBE /* TrackingController.swift */,
+			);
+			path = TraccarClient;
+			sourceTree = "<group>";
+		};
+		CED4872217DB1BF6007FCF57 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				CED4872317DB1BF6007FCF57 /* TraccarClient-Info.plist */,
+				CED4872917DB1BF6007FCF57 /* TraccarClient-Prefix.pch */,
+				CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */,
+				CED4879A17DB1E61007FCF57 /* InAppSettings.bundle */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		CEE286CF1B80839700AE7E83 /* TraccarClientTests */ = {
+			isa = PBXGroup;
+			children = (
+				CEE286D01B80839700AE7E83 /* Supporting Files */,
+				CB4197941F675518008F301C /* CoreDataTests.swift */,
+				CB4197961F676B52008F301C /* DatabaseHelperTests.swift */,
+				5E716A2D1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift */,
+				5E716A2F1F63A9F600A2DBC3 /* RequestManagerTests.swift */,
+			);
+			path = TraccarClientTests;
+			sourceTree = "<group>";
+		};
+		CEE286D01B80839700AE7E83 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				CEE286D11B80839700AE7E83 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		CED4871717DB1BF6007FCF57 /* TraccarClient */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CED4873E17DB1BF6007FCF57 /* Build configuration list for PBXNativeTarget "TraccarClient" */;
+			buildPhases = (
+				CED4871417DB1BF6007FCF57 /* Sources */,
+				CED4871517DB1BF6007FCF57 /* Frameworks */,
+				CED4871617DB1BF6007FCF57 /* Resources */,
+				5E3175221F692FA600CDF0F5 /* ShellScript */,
+				CEFA94EF1EA2EE31009B5809 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = TraccarClient;
+			packageProductDependencies = (
+				5EE3CA332998948F002C86E4 /* FirebaseAnalytics */,
+				5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */,
+				5EE3CA38299894B9002C86E4 /* InAppSettingsKit */,
+			);
+			productName = TraccarClient;
+			productReference = CED4871817DB1BF6007FCF57 /* TraccarClient.app */;
+			productType = "com.apple.product-type.application";
+		};
+		CEE286CD1B80839600AE7E83 /* TraccarClientTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CEE286D61B80839700AE7E83 /* Build configuration list for PBXNativeTarget "TraccarClientTests" */;
+			buildPhases = (
+				CEE286CA1B80839600AE7E83 /* Sources */,
+				CEE286CB1B80839600AE7E83 /* Frameworks */,
+				CEE286CC1B80839600AE7E83 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				CEE286D51B80839700AE7E83 /* PBXTargetDependency */,
+			);
+			name = TraccarClientTests;
+			productName = TraccarClientTests;
+			productReference = CEE286CE1B80839700AE7E83 /* TraccarClientTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CED4871017DB1BF6007FCF57 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1200;
+				ORGANIZATIONNAME = Traccar;
+				TargetAttributes = {
+					CED4871717DB1BF6007FCF57 = {
+						LastSwiftMigration = 1100;
+					};
+					CEE286CD1B80839600AE7E83 = {
+						CreatedOnToolsVersion = 6.4;
+						LastSwiftMigration = 1100;
+						TestTargetID = CED4871717DB1BF6007FCF57;
+					};
+				};
+			};
+			buildConfigurationList = CED4871317DB1BF6007FCF57 /* Build configuration list for PBXProject "TraccarClient" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+				de,
+				es,
+				fr,
+				pt,
+				ru,
+				"pt-BR",
+				it,
+				pl,
+				nl,
+				zh,
+				ko,
+				ja,
+			);
+			mainGroup = CED4870F17DB1BF6007FCF57;
+			packageReferences = (
+				5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
+				5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */,
+			);
+			productRefGroup = CED4871917DB1BF6007FCF57 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CED4871717DB1BF6007FCF57 /* TraccarClient */,
+				CEE286CD1B80839600AE7E83 /* TraccarClientTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		CED4871617DB1BF6007FCF57 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CEF6433C1B91AA9400195CEA /* MainStoryboard.storyboard in Resources */,
+				CEF6433D1B91AA9400195CEA /* Localizable.strings in Resources */,
+				CE5899C81B115C9100ED70D2 /* Images.xcassets in Resources */,
+				CEF643271B919FFA00195CEA /* LaunchScreen.xib in Resources */,
+				CED4879B17DB1E61007FCF57 /* InAppSettings.bundle in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CEE286CC1B80839600AE7E83 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		5E3175221F692FA600CDF0F5 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
+				"$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "if [ \"${CONFIGURATION}\" = \"Google\" ]; then\ncp -r \"${PROJECT_DIR}/../environment/google-services/traccar-client-ios.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n\"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nfi\n";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CED4871417DB1BF6007FCF57 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CB7ED0801F6602CD00A33FCF /* Position.swift in Sources */,
+				CBAA0F7F1F68E807008BBBBE /* MainViewController.swift in Sources */,
+				CB7ED0821F661B4F00A33FCF /* PositionProvider.swift in Sources */,
+				CB4197991F67724F008F301C /* NetworkManager.swift in Sources */,
+				CB7ED0841F662BAF00A33FCF /* AppDelegate.swift in Sources */,
+				5E394EBE28A9CC7600396F33 /* BatteryStatus.swift in Sources */,
+				5E716A2B1F63A60800A2DBC3 /* ProtocolFormatter.swift in Sources */,
+				CBCE82F21B8D265800A7318B /* TraccarClient.xcdatamodeld in Sources */,
+				CBAA0F7D1F68E14C008BBBBE /* StatusViewController.swift in Sources */,
+				CBAA0F811F68EB62008BBBBE /* TrackingController.swift in Sources */,
+				5E716A271F63A0B100A2DBC3 /* DistanceCalculator.swift in Sources */,
+				5E716A291F63A45A00A2DBC3 /* RequestManager.swift in Sources */,
+				CB4197931F674A3E008F301C /* DatabaseHelper.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CEE286CA1B80839600AE7E83 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E716A301F63A9F600A2DBC3 /* RequestManagerTests.swift in Sources */,
+				CB4197971F676B52008F301C /* DatabaseHelperTests.swift in Sources */,
+				5E716A2E1F63A7A200A2DBC3 /* ProtocolFormatterTests.swift in Sources */,
+				CB4197951F675518008F301C /* CoreDataTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		CEE286D51B80839700AE7E83 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CED4871717DB1BF6007FCF57 /* TraccarClient */;
+			targetProxy = CEE286D41B80839700AE7E83 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		CED4873317DB1BF6007FCF57 /* MainStoryboard.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				CEF254DD1B905266001DEFD2 /* ru */,
+				CEF254DF1B9052EB001DEFD2 /* Base */,
+				CEF6432A1B91A48B00195CEA /* de */,
+				CEF6432C1B91A49400195CEA /* es */,
+				CEF6432E1B91A49700195CEA /* fr */,
+				CEF643321B91A4BF00195CEA /* pt */,
+				CE2FA5181CB935C9007F86B4 /* en */,
+				CE4B80FE1EC71CAE00D1C41D /* pt-BR */,
+				CE4B81001EC71CCF00D1C41D /* it */,
+				CE4B81021EC71CFF00D1C41D /* pl */,
+				CE4B81041EC71D1700D1C41D /* nl */,
+				CE4B81061EC71D3600D1C41D /* zh */,
+				CE4B81081EC71D4300D1C41D /* ko */,
+				CE4B810A1EC71D4800D1C41D /* ja */,
+			);
+			name = MainStoryboard.storyboard;
+			sourceTree = "<group>";
+		};
+		CEF643371B91A94600195CEA /* Localizable.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				CEF643361B91A94600195CEA /* Base */,
+				CEF643381B91A95700195CEA /* de */,
+				CEF643391B91A98300195CEA /* ru */,
+				CEECE4B91B91AE2B00EB6F4A /* es */,
+				CEECE4BA1B91AE2C00EB6F4A /* fr */,
+				CEECE4BB1B91AE2D00EB6F4A /* pt */,
+				CE4B80FF1EC71CAE00D1C41D /* pt-BR */,
+				CE4B81011EC71CCF00D1C41D /* it */,
+				CE4B81031EC71CFF00D1C41D /* pl */,
+				CE4B81051EC71D1700D1C41D /* nl */,
+				CE4B81071EC71D3600D1C41D /* zh */,
+				CE4B81091EC71D4300D1C41D /* ko */,
+				CE4B810B1EC71D4800D1C41D /* ja */,
+			);
+			name = Localizable.strings;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		5E3175231F69305200CDF0F5 /* Google */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				DEFINES_MODULE = YES;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Google;
+		};
+		5E3175241F69305200CDF0F5 /* Google */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 58;
+				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch";
+				INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 7.0;
+				OTHER_SWIFT_FLAGS = "-D FIREBASE";
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE = "";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				WRAPPER_EXTENSION = app;
+			};
+			name = Google;
+		};
+		5E3175251F69305200CDF0F5 /* Google */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				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;
+				INFOPLIST_FILE = TraccarClientTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient";
+			};
+			name = Google;
+		};
+		CED4873C17DB1BF6007FCF57 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEFINES_MODULE = YES;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		CED4873D17DB1BF6007FCF57 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				DEFINES_MODULE = YES;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		CED4873F17DB1BF6007FCF57 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				CURRENT_PROJECT_VERSION = 58;
+				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch";
+				INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 7.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE = "";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				WRAPPER_EXTENSION = app;
+			};
+			name = Debug;
+		};
+		CED4874017DB1BF6007FCF57 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 58;
+				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "TraccarClient/TraccarClient-Prefix.pch";
+				INFOPLIST_FILE = "TraccarClient/TraccarClient-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 7.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.traccar.client.TraccarClien;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE = "";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				WRAPPER_EXTENSION = app;
+			};
+			name = Release;
+		};
+		CEE286D71B80839700AE7E83 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_NO_COMMON_BLOCKS = YES;
+				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;
+				INFOPLIST_FILE = TraccarClientTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				MTL_ENABLE_DEBUG_INFO = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient";
+			};
+			name = Debug;
+		};
+		CEE286D81B80839700AE7E83 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = HK8L8KQMEZ;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				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;
+				INFOPLIST_FILE = TraccarClientTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.traccar.client.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_SWIFT3_OBJC_INFERENCE = On;
+				SWIFT_VERSION = 5.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TraccarClient.app/TraccarClient";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CED4871317DB1BF6007FCF57 /* Build configuration list for PBXProject "TraccarClient" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CED4873C17DB1BF6007FCF57 /* Debug */,
+				CED4873D17DB1BF6007FCF57 /* Release */,
+				5E3175231F69305200CDF0F5 /* Google */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CED4873E17DB1BF6007FCF57 /* Build configuration list for PBXNativeTarget "TraccarClient" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CED4873F17DB1BF6007FCF57 /* Debug */,
+				CED4874017DB1BF6007FCF57 /* Release */,
+				5E3175241F69305200CDF0F5 /* Google */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CEE286D61B80839700AE7E83 /* Build configuration list for PBXNativeTarget "TraccarClientTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CEE286D71B80839700AE7E83 /* Debug */,
+				CEE286D81B80839700AE7E83 /* Release */,
+				5E3175251F69305200CDF0F5 /* Google */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+		5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 9.0.0;
+			};
+		};
+		5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/futuretap/InAppSettingsKit.git";
+			requirement = {
+				branch = master;
+				kind = branch;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+		5EE3CA332998948F002C86E4 /* FirebaseAnalytics */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+			productName = FirebaseAnalytics;
+		};
+		5EE3CA352998948F002C86E4 /* FirebaseCrashlytics */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 5EE3CA322998948F002C86E4 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+			productName = FirebaseCrashlytics;
+		};
+		5EE3CA38299894B9002C86E4 /* InAppSettingsKit */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = 5EE3CA37299894B9002C86E4 /* XCRemoteSwiftPackageReference "InAppSettingsKit" */;
+			productName = InAppSettingsKit;
+		};
+/* End XCSwiftPackageProductDependency section */
+
+/* Begin XCVersionGroup section */
+		CBCE82F01B8D265800A7318B /* TraccarClient.xcdatamodeld */ = {
+			isa = XCVersionGroup;
+			children = (
+				CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */,
+			);
+			currentVersion = CBCE82F11B8D265800A7318B /* TraccarClient.xcdatamodel */;
+			path = TraccarClient.xcdatamodeld;
+			sourceTree = "<group>";
+			versionGroupType = wrapper.xcdatamodel;
+		};
+/* End XCVersionGroup section */
+	};
+	rootObject = CED4871017DB1BF6007FCF57 /* Project object */;
+}
diff --git a/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000000000000000000000000000000000..919434a6254f0e9651f402737811be6634a03e9c
--- /dev/null
+++ b/TraccarClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>
diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000000000000000000000000000000000..18d981003d68d0546c4804ac2ff47dd97c6e7921
--- /dev/null
+++ b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000000000000000000000000000000000000..a0a0de81c028f65b3a72df38a63d6de08c28d2c3
--- /dev/null
+++ b/TraccarClient.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,122 @@
+{
+  "pins" : [
+    {
+      "identity" : "abseil-cpp-swiftpm",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git",
+      "state" : {
+        "revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1",
+        "version" : "0.20220203.2"
+      }
+    },
+    {
+      "identity" : "boringssl-swiftpm",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/boringssl-SwiftPM.git",
+      "state" : {
+        "revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab",
+        "version" : "0.9.1"
+      }
+    },
+    {
+      "identity" : "firebase-ios-sdk",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/firebase-ios-sdk",
+      "state" : {
+        "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050",
+        "version" : "9.6.0"
+      }
+    },
+    {
+      "identity" : "googleappmeasurement",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleAppMeasurement.git",
+      "state" : {
+        "revision" : "c1cfde8067668027b23a42c29d11c246152fe046",
+        "version" : "9.6.0"
+      }
+    },
+    {
+      "identity" : "googledatatransport",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleDataTransport.git",
+      "state" : {
+        "revision" : "aae45a320fd0d11811820335b1eabc8753902a40",
+        "version" : "9.2.5"
+      }
+    },
+    {
+      "identity" : "googleutilities",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/GoogleUtilities.git",
+      "state" : {
+        "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b",
+        "version" : "7.11.5"
+      }
+    },
+    {
+      "identity" : "grpc-ios",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/grpc/grpc-ios.git",
+      "state" : {
+        "revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6",
+        "version" : "1.44.3-grpc"
+      }
+    },
+    {
+      "identity" : "gtm-session-fetcher",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/gtm-session-fetcher.git",
+      "state" : {
+        "revision" : "5ccda3981422a84186387dbb763ba739178b529c",
+        "version" : "2.3.0"
+      }
+    },
+    {
+      "identity" : "inappsettingskit",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/futuretap/InAppSettingsKit.git",
+      "state" : {
+        "branch" : "master",
+        "revision" : "08ab93cd15759eed534b821c2ea789d97a0fdca0"
+      }
+    },
+    {
+      "identity" : "leveldb",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/leveldb.git",
+      "state" : {
+        "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
+        "version" : "1.22.2"
+      }
+    },
+    {
+      "identity" : "nanopb",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/firebase/nanopb.git",
+      "state" : {
+        "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
+        "version" : "2.30909.0"
+      }
+    },
+    {
+      "identity" : "promises",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/promises.git",
+      "state" : {
+        "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e",
+        "version" : "2.3.1"
+      }
+    },
+    {
+      "identity" : "swift-protobuf",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-protobuf.git",
+      "state" : {
+        "revision" : "3c54ab05249f59f2c6641dd2920b8358ea9ed127",
+        "version" : "1.24.0"
+      }
+    }
+  ],
+  "version" : 2
+}
diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist
new file mode 100644
index 0000000000000000000000000000000000000000..5dd5da85fdbd81ad600c193382e3305209b9e392
--- /dev/null
+++ b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/IDEFindNavigatorScopes.plist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array/>
+</plist>
diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000000000000000000000000000000000000..b2d758c245a287f08b5ba9e4e4628cff98a6159a
Binary files /dev/null and b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist b/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000000000000000000000000000000000000..84e35758aec5a66ef5b837c12f92e045efbfda22
--- /dev/null
+++ b/TraccarClient.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>Promises (Playground) 1.xcscheme</key>
+		<dict>
+			<key>isShown</key>
+			<false/>
+			<key>orderHint</key>
+			<integer>2</integer>
+		</dict>
+		<key>Promises (Playground) 2.xcscheme</key>
+		<dict>
+			<key>isShown</key>
+			<false/>
+			<key>orderHint</key>
+			<integer>3</integer>
+		</dict>
+		<key>Promises (Playground).xcscheme</key>
+		<dict>
+			<key>isShown</key>
+			<false/>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+		<key>TraccarClient.xcscheme_^#shared#^_</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+</dict>
+</plist>
diff --git a/TraccarClient/AppDelegate.swift b/TraccarClient/AppDelegate.swift
new file mode 100644
index 0000000000000000000000000000000000000000..732da69387cb905f29992dd42d94f687961a90db
--- /dev/null
+++ b/TraccarClient/AppDelegate.swift
@@ -0,0 +1,176 @@
+//
+// Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import UIKit
+import CoreData
+import Firebase
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate, PositionProviderDelegate {
+    
+    var window: UIWindow?
+    
+    var managedObjectContext: NSManagedObjectContext?
+    var managedObjectModel: NSManagedObjectModel?
+    var persistentStoreCoordinator: NSPersistentStoreCoordinator?
+    
+    var trackingController: TrackingController?
+    var positionProvider: PositionProvider?
+    
+    static var instance: AppDelegate {
+        return UIApplication.shared.delegate as! AppDelegate
+    }
+    
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
+        #if FIREBASE
+        FirebaseApp.configure()
+        #endif
+
+        UIDevice.current.isBatteryMonitoringEnabled = true
+
+        let userDefaults = UserDefaults.standard
+        if userDefaults.string(forKey: "device_id_preference") == nil {
+            let identifier = "\(Int.random(in: 100000..<1000000))"
+            userDefaults.setValue(identifier, forKey: "device_id_preference")
+        }
+
+        registerDefaultsFromSettingsBundle()
+        
+        migrateLegacyDefaults()
+        
+        let modelUrl = Bundle.main.url(forResource: "TraccarClient", withExtension: "momd")
+        managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl!)
+        
+        persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!)
+        let storeUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("TraccarClient.sqlite")
+        let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
+        try! persistentStoreCoordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeUrl, options: options)
+        
+        managedObjectContext = NSManagedObjectContext.init(concurrencyType: .mainQueueConcurrencyType)
+        managedObjectContext?.persistentStoreCoordinator = persistentStoreCoordinator
+
+        if userDefaults.bool(forKey: "service_status_preference") {
+            StatusViewController.addMessage(NSLocalizedString("Service created", comment: ""))
+            trackingController = TrackingController()
+            trackingController?.start()
+        }
+
+        return true
+    }
+    
+    func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
+        
+        let userDefaults = UserDefaults.standard
+        
+        switch shortcutItem.type {
+        case "org.traccar.client.start":
+            if !userDefaults.bool(forKey: "service_status_preference") {
+                userDefaults.setValue(true, forKey: "service_status_preference")
+                StatusViewController.addMessage(NSLocalizedString("Service created", comment: ""))
+                trackingController = TrackingController()
+                trackingController?.start()
+                showToast(message: NSLocalizedString("Service created", comment: ""))
+            }
+        case "org.traccar.client.stop":
+            if userDefaults.bool(forKey: "service_status_preference") {
+                userDefaults.setValue(false, forKey: "service_status_preference")
+                StatusViewController.addMessage(NSLocalizedString("Service destroyed", comment: ""))
+                trackingController?.stop()
+                trackingController = nil
+                showToast(message: NSLocalizedString("Service destroyed", comment: ""))
+            }
+        case "org.traccar.client.sos":
+            positionProvider = PositionProvider()
+            positionProvider?.delegate = self
+            positionProvider?.startUpdates()
+        default:
+            break
+        }
+        
+        completionHandler(true)
+    }
+    
+    func didUpdate(position: Position) {
+
+        positionProvider?.stopUpdates()
+        positionProvider = nil
+
+        let userDefaults = UserDefaults.standard
+        
+        if let request = ProtocolFormatter.formatPostion(position, url: userDefaults.string(forKey: "server_url_preference")!, alarm: "sos") {
+            RequestManager.sendRequest(request, completionHandler: {(_ success: Bool) -> Void in
+                if success {
+                    self.showToast(message: NSLocalizedString("Send successfully", comment: ""))
+                } else {
+                    self.showToast(message: NSLocalizedString("Send failed", comment: ""))
+                }
+            })
+        }
+    }
+    
+    func showToast(message : String) {
+        let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
+        window?.rootViewController?.present(alert, animated: true)
+        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
+            alert.dismiss(animated: true)
+        }
+    }
+    
+    func applicationWillTerminate(_ application: UIApplication) {
+        if let context = managedObjectContext {
+            if context.hasChanges {
+                try! context.save()
+            }
+        }
+    }
+    
+    func registerDefaultsFromSettingsBundle() {
+        let settingsBundle = Bundle.main.path(forResource: "InAppSettings", ofType: "bundle")!
+        let finalPath = URL(fileURLWithPath: settingsBundle).appendingPathComponent("Root.plist")
+        let settingsDictionary = NSDictionary(contentsOf: finalPath)
+        let preferenceSpecifiers = settingsDictionary?.object(forKey: "PreferenceSpecifiers") as! [NSDictionary]
+        
+        var defaults: [String:Any] = [:]
+        
+        for item in preferenceSpecifiers {
+            if let key = item.object(forKey: "Key") as? String {
+                defaults[key] = item.object(forKey: "DefaultValue")
+            }
+        }
+        
+        UserDefaults.standard.register(defaults: defaults)
+    }
+    
+    func migrateLegacyDefaults() {
+        let userDefaults = UserDefaults.standard
+        if userDefaults.object(forKey: "server_address_preference") != nil {
+            var urlComponents = URLComponents()
+            urlComponents.scheme = userDefaults.bool(forKey: "secure_preference") ? "https" : "http"
+            urlComponents.host = userDefaults.string(forKey: "server_address_preference")
+            urlComponents.port = userDefaults.integer(forKey: "server_port_preference")
+            if urlComponents.port == 0 {
+                urlComponents.port = 5055
+            }
+            
+            userDefaults.set(urlComponents.string, forKey: "server_url_preference")
+            
+            userDefaults.removeObject(forKey: "server_port_preference")
+            userDefaults.removeObject(forKey: "server_address_preference")
+            userDefaults.removeObject(forKey: "secure_preference")
+        }
+    }
+
+}
diff --git a/TraccarClient/Base.lproj/Localizable.strings b/TraccarClient/Base.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..92a31cc82dd116ea886fa9319439cdd55314ae51
--- /dev/null
+++ b/TraccarClient/Base.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar Client";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Send failed";
+"Location update" = "Location update";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/Base.lproj/MainStoryboard.storyboard b/TraccarClient/Base.lproj/MainStoryboard.storyboard
new file mode 100644
index 0000000000000000000000000000000000000000..f5ea5bcf7da3a57c47628446d9b50eeb04e6a12e
--- /dev/null
+++ b/TraccarClient/Base.lproj/MainStoryboard.storyboard
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="4Uy-y2-7bi">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
+        <capability name="Named colors" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Root View Controller-->
+        <scene sceneID="Ejt-ia-q1N">
+            <objects>
+                <tableViewController id="ZiV-Qa-hnE" customClass="MainViewController" customModule="TraccarClient" customModuleProvider="target" sceneMemberID="viewController">
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="KcQ-JP-xCp">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
+                    </tableView>
+                    <navigationItem key="navigationItem" title="Root View Controller" id="mBp-pp-ezn">
+                        <barButtonItem key="rightBarButtonItem" title="Status" id="eKa-7m-jq1">
+                            <connections>
+                                <segue destination="LmD-5n-dPz" kind="push" id="iTl-2p-q5b"/>
+                            </connections>
+                        </barButtonItem>
+                    </navigationItem>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="68Y-02-qka" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="863.768115942029" y="82.366071428571431"/>
+        </scene>
+        <!--Status View Controller-->
+        <scene sceneID="El5-px-WCe">
+            <objects>
+                <tableViewController id="LmD-5n-dPz" customClass="StatusViewController" customModule="TraccarClient" customModuleProvider="target" sceneMemberID="viewController">
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="fdn-aT-LfP">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
+                        <prototypes>
+                            <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="StatusCell" id="bOU-VM-Ej4">
+                                <rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bOU-VM-Ej4" id="jfy-rh-RFY">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                </tableViewCellContentView>
+                            </tableViewCell>
+                        </prototypes>
+                        <connections>
+                            <outlet property="dataSource" destination="LmD-5n-dPz" id="6o2-Hb-hz7"/>
+                            <outlet property="delegate" destination="LmD-5n-dPz" id="hkG-pc-QSu"/>
+                        </connections>
+                    </tableView>
+                    <navigationItem key="navigationItem" id="mzb-XD-7DL">
+                        <barButtonItem key="rightBarButtonItem" title="Clear" id="He4-eJ-a31">
+                            <connections>
+                                <action selector="clear:" destination="LmD-5n-dPz" id="pZK-JC-IGX"/>
+                            </connections>
+                        </barButtonItem>
+                    </navigationItem>
+                </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="522-xf-AQn" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="1633" y="82"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="kvI-h8-0j0">
+            <objects>
+                <navigationController definesPresentationContext="YES" id="4Uy-y2-7bi" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="aeM-3M-Z4b">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                        <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <color key="barTintColor" name="Brand"/>
+                        <navigationBarAppearance key="standardAppearance">
+                            <color key="backgroundColor" name="Brand"/>
+                            <textAttributes key="titleTextAttributes">
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            </textAttributes>
+                        </navigationBarAppearance>
+                        <navigationBarAppearance key="scrollEdgeAppearance">
+                            <color key="backgroundColor" name="Brand"/>
+                            <textAttributes key="titleTextAttributes">
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            </textAttributes>
+                        </navigationBarAppearance>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="ZiV-Qa-hnE" kind="relationship" relationship="rootViewController" id="nGO-vC-KYZ"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Kiw-gD-Vdj" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="89.855072463768124" y="82.366071428571431"/>
+        </scene>
+    </scenes>
+    <resources>
+        <namedColor name="Brand">
+            <color red="0.1803921568627451" green="0.49019607843137253" blue="0.19607843137254902" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </namedColor>
+        <systemColor name="groupTableViewBackgroundColor">
+            <color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
+</document>
diff --git a/TraccarClient/BatteryStatus.swift b/TraccarClient/BatteryStatus.swift
new file mode 100644
index 0000000000000000000000000000000000000000..70b8cf821d11d62e2f5bb512a71d111cfc16e38b
--- /dev/null
+++ b/TraccarClient/BatteryStatus.swift
@@ -0,0 +1,22 @@
+//
+// Copyright 2022 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+struct BatteryStatus {
+    public let level: Float
+    public let charging: Bool
+}
diff --git a/TraccarClient/DatabaseHelper.swift b/TraccarClient/DatabaseHelper.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8a06abcd65a23530f1ca0901c3b5e3d348493f34
--- /dev/null
+++ b/TraccarClient/DatabaseHelper.swift
@@ -0,0 +1,48 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+import CoreData
+import UIKit
+
+public class DatabaseHelper: NSObject {
+    
+    let managedObjectContext: NSManagedObjectContext
+    
+    convenience override init() {
+        self.init(managedObjectContext: AppDelegate.instance.managedObjectContext!)
+    }
+    
+    public init(managedObjectContext: NSManagedObjectContext) {
+        self.managedObjectContext = managedObjectContext
+        super.init()
+    }
+    
+    public func selectPosition() -> Position? {
+        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Position")
+        if let fetchedObjects = try? managedObjectContext.fetch(fetchRequest) {
+            if fetchedObjects.count > 0 {
+                return fetchedObjects[0] as? Position
+            }
+        }
+        return nil
+    }
+    
+    public func delete(position: Position) {
+        managedObjectContext.delete(position)
+    }
+
+}
diff --git a/TraccarClient/DistanceCalculator.swift b/TraccarClient/DistanceCalculator.swift
new file mode 100644
index 0000000000000000000000000000000000000000..747fa9bd6aa3a98810627f7ef75b2b219114dc78
--- /dev/null
+++ b/TraccarClient/DistanceCalculator.swift
@@ -0,0 +1,33 @@
+//
+// Copyright 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+class DistanceCalculator: NSObject {
+    
+    static let EQUATORIAL_EARTH_RADIUS = 6378.1370
+    static let DEG_TO_RAD = Double.pi / 180
+    
+    static func distance(fromLat lat1: Double, fromLon lon1: Double, toLat lat2: Double, toLon lon2: Double) -> Double {
+        let dlong = (lon2 - lon1) * DEG_TO_RAD
+        let dlat = (lat2 - lat1) * DEG_TO_RAD
+        let a = pow(sin(dlat / 2), 2) + cos(lat1 * DEG_TO_RAD) * cos(lat2 * DEG_TO_RAD) * pow(sin(dlong / 2), 2)
+        let c = 2 * atan2(sqrt(a), sqrt(1 - a))
+        let d = EQUATORIAL_EARTH_RADIUS * c
+        return d * 1000
+    }
+
+}
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2fd176693fa7fb61ea3a98bedff3d287d8b26da
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/100.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b31dd5db33453fa95d6fcf21e3a53c9d8ce61e7
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/1024.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png
new file mode 100644
index 0000000000000000000000000000000000000000..b590b7608aadbbd0287265e820ab68ecfb2eef70
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/114.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff47ff513bd7dd3b14e765f59d6b05a844acbce0
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/120.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7dc8fd745e0649bf030ba506a678422ecbc12bc
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/144.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png
new file mode 100644
index 0000000000000000000000000000000000000000..a352b16e19f007dd26d6e53cd6b49ceccc33826f
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/152.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png
new file mode 100644
index 0000000000000000000000000000000000000000..775d0f13a7a0dd1df91e27ce450b4a00aba9a65f
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/167.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png
new file mode 100644
index 0000000000000000000000000000000000000000..2da8eb5371d8049cae1cdc3d634e3d63fb999b42
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/180.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png
new file mode 100644
index 0000000000000000000000000000000000000000..a02a3585dda02fea50c666c7780b8fc7d1adb4bd
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/20.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png
new file mode 100644
index 0000000000000000000000000000000000000000..9332bbfbebe3a53fd8331ba4594b5513619d9ca2
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/29.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png
new file mode 100644
index 0000000000000000000000000000000000000000..0ad24730ab6915664a8aa18a40d4856a13c59e94
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/40.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1d873799721bafa570d571928cb82f24243e3c5
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/50.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png
new file mode 100644
index 0000000000000000000000000000000000000000..27d7e8599f82bec5a73d750fa766450e5e123a2e
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/57.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png
new file mode 100644
index 0000000000000000000000000000000000000000..71395ffb8e585642c602bcab0d2ff455ce62f0e1
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/58.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e12676f4f3bcb1190676377db77be2f213f30c9
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/60.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a3720e633247927765e8dbdcaf1d49366ff0264
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/72.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png
new file mode 100644
index 0000000000000000000000000000000000000000..93a8c0dca2cecd5cf724fcd56a88347cb87e9141
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/76.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png
new file mode 100644
index 0000000000000000000000000000000000000000..627736770128d5e760cce2e28ac18357e1f74277
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/80.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png b/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png
new file mode 100644
index 0000000000000000000000000000000000000000..c25052d78fdde19b8ec4f62abb6b4f75a30d0bf2
Binary files /dev/null and b/TraccarClient/Images.xcassets/AppIcon.appiconset/87.png differ
diff --git a/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json b/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..65b74d7ef11fa59fafa829e681ac90906f3ac8b2
--- /dev/null
+++ b/TraccarClient/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1 @@
+{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]}
\ No newline at end of file
diff --git a/TraccarClient/Images.xcassets/Brand.colorset/Contents.json b/TraccarClient/Images.xcassets/Brand.colorset/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..1b244fc84744e4d0ffe272bcc36cb37a4c669048
--- /dev/null
+++ b/TraccarClient/Images.xcassets/Brand.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x32",
+          "green" : "0x7D",
+          "red" : "0x2E"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x05",
+          "green" : "0x50",
+          "red" : "0x00"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/TraccarClient/Images.xcassets/Contents.json b/TraccarClient/Images.xcassets/Contents.json
new file mode 100644
index 0000000000000000000000000000000000000000..da4a164c918651cdd1e11dca5cc62c333f097601
--- /dev/null
+++ b/TraccarClient/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/TraccarClient/InAppSettings.bundle/Root.plist b/TraccarClient/InAppSettings.bundle/Root.plist
new file mode 100644
index 0000000000000000000000000000000000000000..97c1a60469121ccff268cec5bda4f91d5fae6ec2
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/Root.plist
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>PreferenceSpecifiers</key>
+	<array>
+		<dict>
+			<key>Type</key>
+			<string>PSToggleSwitchSpecifier</string>
+			<key>Title</key>
+			<string>Service status</string>
+			<key>Key</key>
+			<string>service_status_preference</string>
+			<key>DefaultValue</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>AutocapitalizationType</key>
+			<string>None</string>
+			<key>AutocorrectionType</key>
+			<string>No</string>
+			<key>IsSecure</key>
+			<false/>
+			<key>Key</key>
+			<string>device_id_preference</string>
+			<key>KeyboardType</key>
+			<string>Alphabet</string>
+			<key>Title</key>
+			<string>Device identifier</string>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+		</dict>
+		<dict>
+			<key>DefaultValue</key>
+			<string>http://demo.traccar.org:5055</string>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>Server URL</string>
+			<key>Key</key>
+			<string>server_url_preference</string>
+		</dict>
+		<dict>
+			<key>Type</key>
+			<string>PSMultiValueSpecifier</string>
+			<key>Title</key>
+			<string>Location accuracy</string>
+			<key>Key</key>
+			<string>accuracy_preference</string>
+			<key>DefaultValue</key>
+			<string>medium</string>
+			<key>Titles</key>
+			<array>
+				<string>High</string>
+				<string>Medium</string>
+				<string>Low</string>
+			</array>
+			<key>Values</key>
+			<array>
+				<string>high</string>
+				<string>medium</string>
+				<string>low</string>
+			</array>
+		</dict>
+		<dict>
+			<key>KeyboardType</key>
+			<string>NumberPad</string>
+			<key>DefaultValue</key>
+			<string>300</string>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>Frequency</string>
+			<key>Key</key>
+			<string>frequency_preference</string>
+		</dict>
+		<dict>
+			<key>KeyboardType</key>
+			<string>NumberPad</string>
+			<key>DefaultValue</key>
+			<string></string>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>Distance</string>
+			<key>Key</key>
+			<string>distance_preference</string>
+		</dict>
+		<dict>
+			<key>KeyboardType</key>
+			<string>NumberPad</string>
+			<key>DefaultValue</key>
+			<string></string>
+			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>Angle</string>
+			<key>Key</key>
+			<string>angle_preference</string>
+		</dict>
+        <dict>
+            <key>Type</key>
+            <string>PSToggleSwitchSpecifier</string>
+            <key>Title</key>
+            <string>Offline buffering</string>
+            <key>Key</key>
+            <string>buffer_preference</string>
+            <key>DefaultValue</key>
+            <true/>
+        </dict>
+	</array>
+</dict>
+</plist>
diff --git a/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..694cf5f9a611e20a85c25cb1d6adbb07ea5eef3e
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/de.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Dienststatus";
+"Device identifier" = "Gerätekennung";
+"Server URL" = "Server URL";
+"Location accuracy" = "Positionsgenauigkeit";
+"High" = "Hoch";
+"Medium" = "Mittel";
+"Low" = "Niedrig";
+"Frequency" = "Frequenz";
+"Distance" = "Entfernung";
+"Angle" = "Winkel";
diff --git a/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..5e8f08c75e9188b4c28a9f5a180d36bfebc596db
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/en.lproj/Root.strings
@@ -0,0 +1,11 @@
+"Service status" = "Service status";
+"Device identifier" = "Device identifier";
+"Server URL" = "Server URL";
+"Location accuracy" = "Location accuracy";
+"High" = "High";
+"Medium" = "Medium";
+"Low" = "Low";
+"Frequency" = "Frequency";
+"Distance" = "Distance";
+"Angle" = "Angle";
+"Offline buffering" = "Offline buffering";
diff --git a/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..41223f6da362df3a98ca1c7bf9b38bd1324c7539
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/es.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Estado del servicio";
+"Device identifier" = "Identificador de dispositivo";
+"Server URL" = "URL del servidor";
+"Location accuracy" = "Precisión";
+"High" = "Alto";
+"Medium" = "Medio";
+"Low" = "Bajo";
+"Frequency" = "Frecuencia de rastreo";
+"Distance" = "Distancia";
+"Angle" = "Ángulo";
diff --git a/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..4e420ca0826c95adcf4f760cdbefba36b25ddfd7
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/fr.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Statut";
+"Device identifier" = "Identifiant de l\'appareil";
+"Server URL" = "URL du serveur";
+"Location accuracy" = "Precision de la localisation";
+"High" = "Haut";
+"Medium" = "Moyen";
+"Low" = "Bas";
+"Frequency" = "Fréquence";
+"Distance" = "Distance";
+"Angle" = "Angle";
diff --git a/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..64e72479824e1fe5dbbd9aba84801e90059b0836
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/it.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Stato del servizio";
+"Device identifier" = "Identificativo dispositivo";
+"Server URL" = "Server URL";
+"Location accuracy" = "Accuratezza posizione";
+"High" = "Alta";
+"Medium" = "Media";
+"Low" = "Bassa";
+"Frequency" = "Frequenza";
+"Distance" = "Distanza";
+"Angle" = "Angolo";
diff --git a/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..21cdbdb5b25b53aad046499e223bf4c2091957b5
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/ja.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "サービスステータス";
+"Device identifier" = "デバイス ID";
+"Server URL" = "サーバー URL";
+"Location accuracy" = "位置情報の精度";
+"High" = "高";
+"Medium" = "中";
+"Low" = "低";
+"Frequency" = "周期";
+"Distance" = "距離";
+"Angle" = "角度";
diff --git a/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..e37f2a6c8dcf54a15da868dd7c3fe837e75255e6
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/ko.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "서비스상태";
+"Device identifier" = "장치 식별자";
+"Server URL" = "Server URL";
+"Location accuracy" = "Location accuracy";
+"High" = "High";
+"Medium" = "Medium";
+"Low" = "Low";
+"Frequency" = "주기";
+"Distance" = "Distance";
+"Angle" = "Angle";
diff --git a/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..e97f2c3bd3c96e3d8ef51ad5c4401f9fe7502a57
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/nl.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Server status";
+"Device identifier" = "Toestelidentificatie";
+"Server URL" = "Server URL";
+"Location accuracy" = "Locatienauwkeurigheid";
+"High" = "Hoog";
+"Medium" = "Gemiddeld";
+"Low" = "Laag";
+"Frequency" = "Frequentie";
+"Distance" = "Afstand";
+"Angle" = "Hoek";
diff --git a/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..28edf5dd826f96113832fa125e371c8b7692e6a8
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/pl.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Status usługi";
+"Device identifier" = "Identyfikator urzÄ…dzenia";
+"Server URL" = "URL Serwera";
+"Location accuracy" = "Dokładność lokalizacji";
+"High" = "Wysoka";
+"Medium" = "Åšrednia";
+"Low" = "Niska";
+"Frequency" = "Częstotliwość";
+"Distance" = "Dystans";
+"Angle" = "KÄ…t";
diff --git a/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..f87b852717d00bf18e7d406fc280157c9a11f281
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/pt-BR.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Status do Serviço";
+"Device identifier" = "Identificador do dispositivo";
+"Server URL" = "URL do Servidor";
+"Location accuracy" = "Precisão de localização";
+"High" = "Alto";
+"Medium" = "Médio";
+"Low" = "Baixo";
+"Frequency" = "Invervalo";
+"Distance" = "Distância";
+"Angle" = "Ângulo";
diff --git a/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..036a8cb58d237b5c10fed984c9988cd1c231e93a
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/pt.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Status do Serviço";
+"Device identifier" = "Identificador único";
+"Server URL" = "URL do Servidor";
+"Location accuracy" = "Precisão de localização";
+"High" = "Alto";
+"Medium" = "Médio";
+"Low" = "Baixo";
+"Frequency" = "Invervalo";
+"Distance" = "Distância";
+"Angle" = "Ângulo";
diff --git a/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..6a0beb1c3596dc3fbd107d1d1f741e7f04a8c557
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/ru.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "Состояние сервиса";
+"Device identifier" = "Идентификатор устройства";
+"Server URL" = "URL сервера";
+"Location accuracy" = "Точность местоположения";
+"High" = "Высокая";
+"Medium" = "Средняя";
+"Low" = "Низкая";
+"Frequency" = "Частота";
+"Distance" = "Расстояние";
+"Angle" = "Угол";
diff --git a/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings b/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings
new file mode 100644
index 0000000000000000000000000000000000000000..7a8ff6181cecf81b7ddeb4647a1f527689b0ac27
--- /dev/null
+++ b/TraccarClient/InAppSettings.bundle/zh.lproj/Root.strings
@@ -0,0 +1,10 @@
+"Service status" = "定位";
+"Device identifier" = "设备编码";
+"Server URL" = "服务器地址";
+"Location accuracy" = "位置精度";
+"High" = "高";
+"Medium" = "中";
+"Low" = "低";
+"Frequency" = "定位频率(秒)";
+"Distance" = "距离";
+"Angle" = "角度";
diff --git a/TraccarClient/LaunchScreen.xib b/TraccarClient/LaunchScreen.xib
new file mode 100644
index 0000000000000000000000000000000000000000..9922a47edd9230eabbdedfede3fbf1537dfcfc5c
--- /dev/null
+++ b/TraccarClient/LaunchScreen.xib
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
+        <capability name="Named colors" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jyO-lQ-xCZ">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="64"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="64" id="TfC-6h-A1k"/>
+                    </constraints>
+                    <color key="barTintColor" name="Brand"/>
+                </navigationBar>
+            </subviews>
+            <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="jyO-lQ-xCZ" secondAttribute="trailing" id="BVV-Jp-FLs"/>
+                <constraint firstItem="jyO-lQ-xCZ" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Ra4-bu-wwa"/>
+                <constraint firstItem="jyO-lQ-xCZ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="frB-PI-Vg3"/>
+            </constraints>
+            <point key="canvasLocation" x="499" y="433"/>
+        </view>
+    </objects>
+    <resources>
+        <namedColor name="Brand">
+            <color red="0.2627450980392157" green="0.62745098039215685" blue="0.27843137254901962" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </namedColor>
+    </resources>
+</document>
diff --git a/TraccarClient/MainViewController.swift b/TraccarClient/MainViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..19b59b463c7349a4d86f25af84d2562c83715447
--- /dev/null
+++ b/TraccarClient/MainViewController.swift
@@ -0,0 +1,80 @@
+//
+// Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import UIKit
+import InAppSettingsKit
+
+class MainViewController: IASKAppSettingsViewController {
+    
+    var settingsObserver: NSObjectProtocol?
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        title = NSLocalizedString("Traccar Client", comment: "")
+        showCreditsFooter = false
+        neverShowPrivacySettings = true
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        self.settingsObserver = NotificationCenter.default.addObserver(forName: nil, object: nil, queue: nil) { (notification) in
+            self.didChangeSetting(notification)
+        }
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        if let observer = self.settingsObserver {
+            NotificationCenter.default.removeObserver(observer)
+        }
+    }
+    
+    func showError(_ message: String) {
+        let alert = UIAlertController(title: nil, message: NSLocalizedString(message, comment: ""), preferredStyle: .alert)
+        let defaultAction = UIAlertAction(title: "OK", style: .default, handler: {(_ action: UIAlertAction) -> Void in
+            UserDefaults.standard.setValue(nil, forKey: "service_status_preference")
+        })
+        alert.addAction(defaultAction)
+        present(alert, animated: true)
+    }
+
+    func didChangeSetting(_ notification: Notification) {
+        if let status = notification.userInfo?["service_status_preference"] as? Bool {
+            if status && AppDelegate.instance.trackingController == nil {
+                let userDefaults = UserDefaults.standard
+                let url = userDefaults.string(forKey: "server_url_preference")
+                let frequency = userDefaults.integer(forKey: "frequency_preference")
+                
+                let candidateUrl = NSURL(string: url!)
+                
+                if candidateUrl == nil || candidateUrl?.host == nil || (candidateUrl?.scheme != "http" && candidateUrl?.scheme != "https") {
+                    self.showError("Invalid server URL")
+                } else if frequency <= 0 {
+                    self.showError("Invalid frequency value")
+                } else {
+                    StatusViewController.addMessage(NSLocalizedString("Service created", comment: ""))
+                    AppDelegate.instance.trackingController = TrackingController()
+                    AppDelegate.instance.trackingController?.start()
+                }
+            } else if !status && AppDelegate.instance.trackingController != nil {
+                StatusViewController.addMessage(NSLocalizedString("Service destroyed", comment: ""))
+                AppDelegate.instance.trackingController?.stop()
+                AppDelegate.instance.trackingController = nil
+            }
+        }
+    }
+    
+}
diff --git a/TraccarClient/NetworkManager.swift b/TraccarClient/NetworkManager.swift
new file mode 100644
index 0000000000000000000000000000000000000000..92e93b10f82326b2e63565c04e4c9628d176d6be
--- /dev/null
+++ b/TraccarClient/NetworkManager.swift
@@ -0,0 +1,82 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+import SystemConfiguration
+
+protocol NetworkManagerDelegate: AnyObject {
+    func didUpdateNetwork(online: Bool)
+}
+
+class NetworkManager : NSObject {
+    
+    weak var delegate: NetworkManagerDelegate?
+    
+    //var reachability: SCNetworkReachability
+    
+    override init() {
+        /*var zeroAddress = sockaddr_in()
+        zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
+        zeroAddress.sin_family = sa_family_t(AF_INET)
+
+        reachability = withUnsafePointer(to: &zeroAddress, {
+            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
+                SCNetworkReachabilityCreateWithAddress(nil, $0)
+            }
+        })!*/
+
+        super.init()
+    }
+
+    func online() -> Bool {
+        /*var flags: SCNetworkReachabilityFlags = []
+        SCNetworkReachabilityGetFlags(reachability, &flags)
+        return NetworkManager.online(for: flags)*/
+        return true
+    }
+    
+    func start() {
+        /*var context = SCNetworkReachabilityContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil)
+        SCNetworkReachabilitySetCallback(reachability, { (reachability, flags, pointer) in
+            let networkManager = Unmanaged<NetworkManager>.fromOpaque(pointer!).takeUnretainedValue()
+            networkManager.delegate?.didUpdateNetwork(online: NetworkManager.online(for: flags))
+        }, &context)
+        SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)*/
+    }
+    
+    func stop() {
+        /*SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)*/
+    }
+
+    static func online(for flags: SCNetworkReachabilityFlags) -> Bool {
+        if !flags.contains(.reachable) {
+            return false
+        }
+        if !flags.contains(.connectionRequired) {
+            return true
+        }
+        if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) {
+            if !flags.contains(.interventionRequired) {
+                return true
+            }
+        }
+        if flags.contains(.isWWAN) {
+            return true
+        }
+        return false
+    }
+
+}
diff --git a/TraccarClient/Position.swift b/TraccarClient/Position.swift
new file mode 100644
index 0000000000000000000000000000000000000000..694a40c37b459f8fa7a92625bb0d3c859d319f01
--- /dev/null
+++ b/TraccarClient/Position.swift
@@ -0,0 +1,53 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+import CoreData
+import CoreLocation
+
+public class Position: NSManagedObject {
+    
+    @NSManaged public var deviceId: String?
+    @NSManaged public var time: NSDate?
+    @NSManaged public var latitude: NSNumber?
+    @NSManaged public var longitude: NSNumber?
+    @NSManaged public var altitude: NSNumber?
+    @NSManaged public var speed: NSNumber?
+    @NSManaged public var course: NSNumber?
+    @NSManaged public var battery: NSNumber?
+    @NSManaged public var charging: Bool
+    @NSManaged public var accuracy: NSNumber?
+    
+    public convenience init(managedObjectContext context: NSManagedObjectContext) {
+        let entityDescription = NSEntityDescription.entity(forEntityName: "Position", in: context)
+        self.init(entity: entityDescription!, insertInto: context)
+    }
+    
+    public func setLocation(_ location: CLLocation) {
+        time = location.timestamp as NSDate
+        latitude = location.coordinate.latitude as NSNumber
+        longitude = location.coordinate.longitude as NSNumber
+        altitude = location.altitude as NSNumber
+        if location.speed >= 0 {
+            speed = location.speed * 1.94384 as NSNumber // knots from m/s
+        }
+        if location.course >= 0 {
+            course = location.course as NSNumber
+        }
+        accuracy = location.horizontalAccuracy as NSNumber
+    }
+
+}
diff --git a/TraccarClient/PositionProvider.swift b/TraccarClient/PositionProvider.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a16dc65acc5392d22255c19293fa6ea57f6830de
--- /dev/null
+++ b/TraccarClient/PositionProvider.swift
@@ -0,0 +1,127 @@
+//
+// Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import UIKit
+import CoreLocation
+
+protocol PositionProviderDelegate: AnyObject {
+    func didUpdate(position: Position)
+}
+
+class PositionProvider: NSObject, CLLocationManagerDelegate {
+
+    weak var delegate: PositionProviderDelegate?
+    
+    var locationManager: CLLocationManager
+    var lastLocation: CLLocation?
+
+    var deviceId: String
+    var interval: Double
+    var distance: Double
+    var angle: Double
+    
+    var pendingStart = false
+    
+    override init() {
+        let userDefaults = UserDefaults.standard
+        deviceId = userDefaults.string(forKey: "device_id_preference")!
+        interval = userDefaults.double(forKey: "frequency_preference")
+        distance = userDefaults.double(forKey: "distance_preference")
+        angle = userDefaults.double(forKey: "angle_preference")
+
+        locationManager = CLLocationManager()
+        
+        super.init()
+
+        locationManager.delegate = self
+
+        locationManager.pausesLocationUpdatesAutomatically = false
+        
+        switch userDefaults.string(forKey: "accuracy_preference") ?? "medium" {
+        case "high":
+            locationManager.desiredAccuracy = kCLLocationAccuracyBest
+        case "low":
+            locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
+        default:
+            locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
+        }
+
+        if #available(iOS 9.0, *) {
+            locationManager.allowsBackgroundLocationUpdates = true
+        }
+    }
+    
+    func startUpdates() {
+        switch CLLocationManager.authorizationStatus() {
+        case .authorizedAlways:
+            locationManager.startUpdatingLocation()
+        default:
+            pendingStart = true
+            locationManager.requestAlwaysAuthorization()
+        }
+    }
+    
+    func stopUpdates() {
+        locationManager.stopUpdatingLocation()
+    }
+    
+    func getBatteryStatus() -> BatteryStatus {
+        let device = UIDevice.current
+        if device.batteryState != .unknown {
+            return BatteryStatus(
+                level: device.batteryLevel * 100,
+                charging: device.batteryState == .charging || device.batteryState == .full
+            )
+        } else {
+            return BatteryStatus(level: 0, charging: true)
+        }
+    }
+    
+    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
+        switch status {
+        case .authorizedAlways, .authorizedWhenInUse:
+            if pendingStart {
+                pendingStart = false
+                locationManager.startUpdatingLocation()
+            }
+        default:
+            break
+        }
+    }
+    
+    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+        if let location = locations.last {
+            if lastLocation == nil
+                || location.timestamp.timeIntervalSince(lastLocation!.timestamp) >= interval
+                || (distance > 0 && DistanceCalculator.distance(fromLat: location.coordinate.latitude, fromLon: location.coordinate.longitude, toLat: lastLocation!.coordinate.latitude, toLon: lastLocation!.coordinate.longitude) >= distance)
+                || (angle > 0 && fabs(location.course - lastLocation!.course) >= angle) {
+                
+                let position = Position(managedObjectContext: DatabaseHelper().managedObjectContext)
+                position.deviceId = deviceId
+                position.setLocation(location)
+                let batteryStatus = getBatteryStatus()
+                position.battery = batteryStatus.level as NSNumber
+                position.charging = batteryStatus.charging
+                delegate?.didUpdate(position: position)
+                lastLocation = location
+            }
+        }
+    }
+    
+    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
+    }
+
+}
diff --git a/TraccarClient/ProtocolFormatter.swift b/TraccarClient/ProtocolFormatter.swift
new file mode 100644
index 0000000000000000000000000000000000000000..8730dfa2929235cf9c237f9c4483e08aac999fdf
--- /dev/null
+++ b/TraccarClient/ProtocolFormatter.swift
@@ -0,0 +1,65 @@
+//
+// Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+public class ProtocolFormatter: NSObject {
+    
+    public static func formatPostion(_ position: Position, url: String, alarm: String? = nil) -> URL? {
+        var components = URLComponents(string: url)
+
+        var query = String()
+        
+        if let deviceId = position.deviceId {
+            query += "id=\(deviceId)&"
+        }
+        if let time = position.time {
+            query += "timestamp=\(Int(time.timeIntervalSince1970))&"
+        }
+        if let latitude = position.latitude {
+            query += String(format: "lat=%.06f&", latitude.doubleValue)
+        }
+        if let longitude = position.longitude {
+            query += String(format: "lon=%.06f&", longitude.doubleValue)
+        }
+        if let speed = position.speed {
+            query += "speed=\(speed)&"
+        }
+        if let course = position.course {
+            query += "bearing=\(course)&"
+        }
+        if let altitude = position.altitude {
+            query += "altitude=\(altitude)&"
+        }
+        if let accuracy = position.accuracy {
+            query += "accuracy=\(accuracy)&"
+        }
+        if let battery = position.battery {
+            query += "batt=\(battery)&"
+        }
+        if position.charging {
+            query += "charge=true&"
+        }
+        if let alarm = alarm {
+            query += "alarm=\(alarm)&"
+        }
+        components?.query = String(query.dropLast())
+
+        // use queryItems for iOS 8
+        return components?.url
+    }
+
+}
diff --git a/TraccarClient/RequestManager.swift b/TraccarClient/RequestManager.swift
new file mode 100644
index 0000000000000000000000000000000000000000..5085462fe5614a6c19a6446c9d29b9f7c7721630
--- /dev/null
+++ b/TraccarClient/RequestManager.swift
@@ -0,0 +1,29 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+
+public class RequestManager: NSObject {
+    
+    public static func sendRequest(_ url: URL, completionHandler handler: @escaping (Bool) -> Void) {
+        var request = URLRequest(url: url)
+        request.httpMethod = "POST"
+        NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: {(response, data, connectionError) -> Void in
+            handler(data != nil)
+        })
+    }
+
+}
diff --git a/TraccarClient/StatusViewController.swift b/TraccarClient/StatusViewController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..95cdd8972728d092d74f6a7648eb53df39ffd109
--- /dev/null
+++ b/TraccarClient/StatusViewController.swift
@@ -0,0 +1,81 @@
+//
+// Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import UIKit
+
+class StatusViewController: UITableViewController {
+    
+    static let LIMIT = 20
+    static var statusViewController: StatusViewController?
+    
+    static var messages = [String]()
+    
+    class func addMessage(_ message: String) {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "HH:mm - "
+
+        messages.append(formatter.string(from: Date()) + (message))
+
+        if messages.count > LIMIT {
+            messages.remove(at: 0)
+        }
+
+        statusViewController?.tableView.reloadData()
+    }
+    
+    class func clearMessages() {
+        messages.removeAll()
+
+        statusViewController?.tableView.reloadData()
+    }
+
+    @IBAction func clear(_ sender: UIBarButtonItem) {
+        StatusViewController.clearMessages()
+    }
+    
+    override func viewDidLoad() {
+        tableView.tableFooterView = UIView()
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        StatusViewController.statusViewController = self
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        StatusViewController.statusViewController = nil
+    }
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+    
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return StatusViewController.messages.count
+    }
+    
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cellIdentifier: String = "status"
+        var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
+        if cell == nil {
+            cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
+        }
+        cell?.textLabel?.text = StatusViewController.messages[indexPath.row]
+        return cell!
+    }
+
+}
diff --git a/TraccarClient/TraccarClient-Info.plist b/TraccarClient/TraccarClient-Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..296861d9f5a0ac4d04154ed563f4077d9d035431
--- /dev/null
+++ b/TraccarClient/TraccarClient-Info.plist
@@ -0,0 +1,91 @@
+<?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>CFBundleDisplayName</key>
+	<string>Traccar Client</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>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(MARKETING_VERSION)</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>NSAppTransportSecurity</key>
+	<dict>
+		<key>NSAllowsArbitraryLoads</key>
+		<true/>
+	</dict>
+	<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
+	<string>This is a tracking application and therefore requires access to location services</string>
+	<key>NSLocationAlwaysUsageDescription</key>
+	<string>This is a tracking application and therefore requires access to location services</string>
+	<key>NSLocationWhenInUseUsageDescription</key>
+	<string>This is a tracking application and therefore requires access to location services</string>
+	<key>UIApplicationShortcutItems</key>
+	<array>
+		<dict>
+			<key>UIApplicationShortcutItemTitle</key>
+			<string>Start service</string>
+			<key>UIApplicationShortcutItemType</key>
+			<string>org.traccar.client.start</string>
+		</dict>
+		<dict>
+			<key>UIApplicationShortcutItemTitle</key>
+			<string>Stop service</string>
+			<key>UIApplicationShortcutItemType</key>
+			<string>org.traccar.client.stop</string>
+		</dict>
+		<dict>
+			<key>UIApplicationShortcutItemTitle</key>
+			<string>Send SOS</string>
+			<key>UIApplicationShortcutItemType</key>
+			<string>org.traccar.client.sos</string>
+		</dict>
+	</array>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>location</string>
+	</array>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>MainStoryboard</string>
+	<key>UIMainStoryboardFile~ipad</key>
+	<string>MainStoryboard</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UIStatusBarStyle</key>
+	<string>UIStatusBarStyleLightContent</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<false/>
+</dict>
+</plist>
diff --git a/TraccarClient/TraccarClient-Prefix.pch b/TraccarClient/TraccarClient-Prefix.pch
new file mode 100644
index 0000000000000000000000000000000000000000..936fe15b8302fdee7eb11e21fd4ab53afa29c447
--- /dev/null
+++ b/TraccarClient/TraccarClient-Prefix.pch
@@ -0,0 +1,15 @@
+//
+// Prefix header for all source files of the 'TraccarClient' target in the 'TraccarClient' project
+//
+
+#import <Availability.h>
+
+#ifndef __IPHONE_5_0
+#warning "This project uses features only available in iOS SDK 5.0 and later."
+#endif
+
+#ifdef __OBJC__
+    #import <UIKit/UIKit.h>
+    #import <Foundation/Foundation.h>
+    #import <CoreData/CoreData.h>
+#endif
diff --git a/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents b/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents
new file mode 100644
index 0000000000000000000000000000000000000000..4960488e64c2d93e2f914f411a0b686c5628f749
--- /dev/null
+++ b/TraccarClient/TraccarClient.xcdatamodeld/TraccarClient.xcdatamodel/contents
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+    <entity name="Position" representedClassName=".Position" syncable="YES">
+        <attribute name="accuracy" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <attribute name="altitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="battery" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="charging" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="course" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="deviceId" optional="YES" attributeType="String"/>
+        <attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="speed" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
+        <attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <elements>
+        <element name="Position" positionX="-45" positionY="-9" width="128" height="179"/>
+    </elements>
+</model>
\ No newline at end of file
diff --git a/TraccarClient/TrackingController.swift b/TraccarClient/TrackingController.swift
new file mode 100644
index 0000000000000000000000000000000000000000..56a832cb96778287855b1338415bcc0348e3cb96
--- /dev/null
+++ b/TraccarClient/TrackingController.swift
@@ -0,0 +1,150 @@
+//
+// Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import Foundation
+import CoreLocation
+
+class TrackingController: NSObject, PositionProviderDelegate, NetworkManagerDelegate, CLLocationManagerDelegate {
+    
+    static let RETRY_DELAY: UInt64 = 30 * 1000;
+    
+    var online = false
+    var waiting = false
+    var stopped = false
+
+    let positionProvider = PositionProvider()
+    let locationManager = CLLocationManager()
+    let databaseHelper = DatabaseHelper()
+    let networkManager = NetworkManager()
+    let userDefaults = UserDefaults.standard
+
+    let url: String
+    let buffer: Bool
+    
+    override init() {
+        online = networkManager.online()
+        url = userDefaults.string(forKey: "server_url_preference")!
+        buffer = userDefaults.bool(forKey: "buffer_preference")
+
+        super.init()
+
+        positionProvider.delegate = self
+        locationManager.delegate = self
+        networkManager.delegate = self
+    }
+    
+    func start() {
+        self.stopped = false
+        if self.online {
+            read()
+        }
+        positionProvider.startUpdates()
+        locationManager.startMonitoringSignificantLocationChanges()
+        networkManager.start()
+    }
+    
+    func stop() {
+        networkManager.stop()
+        locationManager.stopMonitoringSignificantLocationChanges()
+        positionProvider.stopUpdates()
+        self.stopped = true
+    }
+
+    func didUpdate(position: Position) {
+        StatusViewController.addMessage(NSLocalizedString("Location update", comment: ""))
+        if buffer {
+            write(position)
+        } else {
+            send(position)
+        }
+    }
+    
+    func didUpdateNetwork(online: Bool) {
+        StatusViewController.addMessage(NSLocalizedString("Connectivity change", comment: ""))
+        if !self.online && online {
+            read()
+        }
+        self.online = online
+    }
+    
+    //
+    // State transition examples:
+    //
+    // write -> read -> send -> delete -> read
+    //
+    // read -> send -> retry -> read -> send
+    //
+    
+    func write(_ position: Position) {
+        let context = DatabaseHelper().managedObjectContext
+        if context.hasChanges {
+            try? context.save()
+        }
+        if self.online && self.waiting {
+            read()
+            self.waiting = false
+        }
+    }
+    
+    func read() {
+        if let position = databaseHelper.selectPosition() {
+            if (position.deviceId == userDefaults.string(forKey: "device_id_preference")) {
+                send(position)
+            } else {
+                delete(position)
+            }
+        } else {
+            self.waiting = true
+        }
+    }
+    
+    func delete(_ position: Position) {
+        databaseHelper.delete(position: position)
+        read()
+    }
+    
+    func send(_ position: Position) {
+        if let request = ProtocolFormatter.formatPostion(position, url: url) {
+            RequestManager.sendRequest(request, completionHandler: {(_ success: Bool) -> Void in
+                if success {
+                    if self.buffer {
+                        self.delete(position)
+                    }
+                } else {
+                    StatusViewController.addMessage(NSLocalizedString("Send failed", comment: ""))
+                    if self.buffer {
+                        self.retry()
+                    }
+                }
+            })
+        } else {
+            StatusViewController.addMessage(NSLocalizedString("Send failed", comment: ""))
+            if buffer {
+                self.retry()
+            }
+        }
+    }
+    
+    func retry() {
+        let deadline = DispatchTime.now() + Double(TrackingController.RETRY_DELAY * NSEC_PER_MSEC) / Double(NSEC_PER_SEC)
+        DispatchQueue.main.asyncAfter(deadline: deadline, execute: {() -> Void in
+            if !self.stopped && self.online {
+                self.read()
+            }
+        })
+    }
+
+}
diff --git a/TraccarClient/de.lproj/Localizable.strings b/TraccarClient/de.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..7838aa5f4bdcadec3a1d1d8f8fe6b2abdeafb300
--- /dev/null
+++ b/TraccarClient/de.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar Client";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Übertragung fehlerhaft";
+"Location update" = "Positionsupdate";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/de.lproj/MainStoryboard.strings b/TraccarClient/de.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..e72108181de932e581214e3accf0509bb7858484
--- /dev/null
+++ b/TraccarClient/de.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar Client";
+"eKa-7m-jq1.title" = "Status";
+"gEG-ov-HEk.title" = "Über";
+"He4-eJ-a31.title" = "Leeren";
+"drL-Ag-IYB.text" = "Echtzeit GPS Ortung für Android Geräte. Kompatibel mit Traccar Server und anderen Ortungsservern.";
+"2Hh-sN-c9g.text" = "Dieses Programm ist frei und Open Source. Es ist unter Apache Lizenz Version 2.0 lizenziert und ist auf GitHub verfügbar.";
+"jF9-Ub-rRP.text" = "Für weitere Informationen besuchen Sie www.traccar.org/client";
diff --git a/TraccarClient/en.lproj/MainStoryboard.strings b/TraccarClient/en.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..0ec39b8859ff6fc622dee3aad35cb0c8434ef83d
--- /dev/null
+++ b/TraccarClient/en.lproj/MainStoryboard.strings
@@ -0,0 +1,3 @@
+"TDj-2j-wEh.text" = "Traccar Client";
+"eKa-7m-jq1.title" = "Status";
+"He4-eJ-a31.title" = "Clear";
diff --git a/TraccarClient/es.lproj/Localizable.strings b/TraccarClient/es.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..ca7946de6ed8c0575ddffeb92eeae883328c564a
--- /dev/null
+++ b/TraccarClient/es.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Cliente Traccar";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Falló el envío";
+"Location update" = "Actualización de localización";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/es.lproj/MainStoryboard.strings b/TraccarClient/es.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..8648329822254ffada43558d6d8930e1331934c5
--- /dev/null
+++ b/TraccarClient/es.lproj/MainStoryboard.strings
@@ -0,0 +1,8 @@
+"TDj-2j-wEh.text" = "Cliente Traccar";
+"eKa-7m-jq1.title" = "Estado";
+"gEG-ov-HEk.title" = "Acerca de…";
+"He4-eJ-a31.title" = "Limpiar";
+"drL-Ag-IYB.text" = "Rastreador GPS en tiempo real para dispositivos Android. Compatible con Servidor Traccar y otros sistemas de rastreo.";
+"2Hh-sN-c9g.text" = "Esta aplición es gratuíta y de código abierto. El código fuente está bajo la Licencia Apache Versión 2 y disponible en Github.";
+"jF9-Ub-rRP.text" = "Para más información visite
+www.traccar.org/client";
diff --git a/TraccarClient/fr.lproj/Localizable.strings b/TraccarClient/fr.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..7785509d0524680ec6186c1b03fb27cfe64519b1
--- /dev/null
+++ b/TraccarClient/fr.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Client Traccar";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "L’envoi a échoué";
+"Location update" = "Localisation mise à jour";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/fr.lproj/MainStoryboard.strings b/TraccarClient/fr.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..5f4114c74a48d4def25e024863f526a9c427bb1e
--- /dev/null
+++ b/TraccarClient/fr.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Client Traccar";
+"eKa-7m-jq1.title" = "Etat";
+"gEG-ov-HEk.title" = "A propos";
+"He4-eJ-a31.title" = "Effacer";
+"drL-Ag-IYB.text" = "Suivi GPS en temps réels pour appareils Android. Compatible avec le serveur Traccar ainsi que d\'autres plateformes de suivi.";
+"2Hh-sN-c9g.text" = "Cette application open source est gratuite. Le code source est disponible sur GitHub est sous licence Apache Version 2.0.";
+"jF9-Ub-rRP.text" = "Pour plus d’information, visitez le site: \nwww.traccar.org/client";
diff --git a/TraccarClient/it.lproj/Localizable.strings b/TraccarClient/it.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..f564adbc86f698e7ff7098da6870cea18f129357
--- /dev/null
+++ b/TraccarClient/it.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar Client";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Invio fallito";
+"Location update" = "Aggiornamento posizione";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/it.lproj/MainStoryboard.strings b/TraccarClient/it.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..cf670ea77b5ffa738f90a3203f43b2c2945b45ff
--- /dev/null
+++ b/TraccarClient/it.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar Client";
+"eKa-7m-jq1.title" = "Stato";
+"gEG-ov-HEk.title" = "Informazioni";
+"He4-eJ-a31.title" = "Pulisci";
+"drL-Ag-IYB.text" = "Real time GPS tracker per dispositivi Android. Compatibile con Traccar Server e altri sistemi di tracking.";
+"2Hh-sN-c9g.text" = "Questa applicazione è libera e open source, il codice sorgente è sotto licenza Apache License Version 2.0 e disponibile su GitHub.";
+"jF9-Ub-rRP.text" = "Per maggiori informazioni visitate \nwww.traccar.org/client";
diff --git a/TraccarClient/ja.lproj/Localizable.strings b/TraccarClient/ja.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..085c411d114ecb6cd1cabcac7078468b3507424b
--- /dev/null
+++ b/TraccarClient/ja.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar クライアント";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "送信に失敗しました";
+"Location update" = "位置情報更新";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/ja.lproj/MainStoryboard.strings b/TraccarClient/ja.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..511a88e6c3e897ba1cc0b051b9263a7071e751a0
--- /dev/null
+++ b/TraccarClient/ja.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar クライアント";
+"eKa-7m-jq1.title" = "ステータス";
+"gEG-ov-HEk.title" = "アプリについて";
+"He4-eJ-a31.title" = "クリア";
+"drL-Ag-IYB.text" = "Android デバイス用のリアルタイム GPS トラッカーです。 Traccar サーバーや他のトラッキングシステムと互換性があります。";
+"2Hh-sN-c9g.text" = "このアプリケーションは無料のオープンソースで、ソースコードは Apache ライセンス バージョン 2.0 でライセンスされます。 GitHub で利用可能です。";
+"jF9-Ub-rRP.text" = "詳細は visit\nwww.traccar.org/client";
diff --git a/TraccarClient/ko.lproj/Localizable.strings b/TraccarClient/ko.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..d323218e704884e709b05e3662e4f325b901a93e
--- /dev/null
+++ b/TraccarClient/ko.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar 고객";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "보내기 실패";
+"Location update" = "위치 수정";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/ko.lproj/MainStoryboard.strings b/TraccarClient/ko.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..ca3b01ed9b9d53e17cb837de9023bc7a4a1f0599
--- /dev/null
+++ b/TraccarClient/ko.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar 고객";
+"eKa-7m-jq1.title" = "상태";
+"gEG-ov-HEk.title" = "관하여";
+"He4-eJ-a31.title" = "초기화";
+"drL-Ag-IYB.text" = "안드로이드 장치를위한 실시간 GPS 추적기. Traccar 서버 및 기타 추적 시스템과 호환됩니다.";
+"2Hh-sN-c9g.text" = "이 응용 프로그램은 무료이며 오픈 소스이며, 소스 코드는 Apache License Version 2.0에서 사용이 허가되며 GitHub에서 사용할 수 있습니다.";
+"jF9-Ub-rRP.text" = "더 자세한 정보는  \www.traccar.org/client 를 방문하십시오.";
diff --git a/TraccarClient/nl.lproj/Localizable.strings b/TraccarClient/nl.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..81fa125a9b3c144b1bf507963e20cad7ba17bf85
--- /dev/null
+++ b/TraccarClient/nl.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar client";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Verzenden mislukt";
+"Location update" = "Locatie update";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/nl.lproj/MainStoryboard.strings b/TraccarClient/nl.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..ede2035229c4f0f5259aa37c6721049d7949a83d
--- /dev/null
+++ b/TraccarClient/nl.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar client";
+"eKa-7m-jq1.title" = "Status";
+"gEG-ov-HEk.title" = "Over ons";
+"He4-eJ-a31.title" = "Wis";
+"drL-Ag-IYB.text" = "Real time GPS tracker voor Android apparaten. Compatibel met de Traccar server en andere tracking systemen.";
+"2Hh-sN-c9g.text" = "Deze toepassing is gratis en open source, de broncode is beschikbaar onder de Apache Licentie versie 2.0 op GitHub.";
+"jF9-Ub-rRP.text" = "Voor meer informatie bezoek\nwww.traccar.org/client";
diff --git a/TraccarClient/pl.lproj/Localizable.strings b/TraccarClient/pl.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..fee3d1ec4c87413c0ce6073574b283a6f69b2a5c
--- /dev/null
+++ b/TraccarClient/pl.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar Klient";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Wysłanie nieudane";
+"Location update" = "Aktualizacja lokalizacji";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/pl.lproj/MainStoryboard.strings b/TraccarClient/pl.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..5f2182d6da436bd8ef6a1b98c942cd87dc910088
--- /dev/null
+++ b/TraccarClient/pl.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar Klient";
+"eKa-7m-jq1.title" = "Status";
+"gEG-ov-HEk.title" = "O Traccar";
+"He4-eJ-a31.title" = "Wyczyść";
+"drL-Ag-IYB.text" = "Monitor GPS dla urządzeń Android. Kompatybilny z serwerem Traccar oraz innymi platformami śledzącymi.";
+"2Hh-sN-c9g.text" = "Ta aplikacja jest darmowa i otwartoźródłowa. Kod źródłowy jest licencjonowany Apache License Version 2.0 i jest dostępny na GitHub.";
+"jF9-Ub-rRP.text" = "Więcej informacji - odwiedź\nwww.traccar.org/client";
diff --git a/TraccarClient/pt-BR.lproj/Localizable.strings b/TraccarClient/pt-BR.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..10c227edbb37595503836229d8c7dd798c54a241
--- /dev/null
+++ b/TraccarClient/pt-BR.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Cliente Traccar";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Falha no envio";
+"Location update" = "Localização atualizada";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/pt-BR.lproj/MainStoryboard.strings b/TraccarClient/pt-BR.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..061dffa7c9515c7bb6a0925812faf2fd0bc5b74f
--- /dev/null
+++ b/TraccarClient/pt-BR.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Cliente Traccar";
+"eKa-7m-jq1.title" = "Status";
+"gEG-ov-HEk.title" = "Sobre";
+"He4-eJ-a31.title" = "Limpar";
+"drL-Ag-IYB.text" = "Rastreador GPS em tempo real para dispositivos Android. Compatível com o Traccar Server e outros sistemas de rastreamento.";
+"2Hh-sN-c9g.text" = "Esta aplicação é livre e de código aberto, o código fonte é licenciado sob a licença Apache Versão 2.0 e disponível no GitHub.";
+"jF9-Ub-rRP.text" = "Para mais informações visite\nwww.traccar.org/client";
diff --git a/TraccarClient/pt.lproj/Localizable.strings b/TraccarClient/pt.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..10c227edbb37595503836229d8c7dd798c54a241
--- /dev/null
+++ b/TraccarClient/pt.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Cliente Traccar";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Falha no envio";
+"Location update" = "Localização atualizada";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/pt.lproj/MainStoryboard.strings b/TraccarClient/pt.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..5fb390e5fe16e58cd84dcfb4d1da158e4a05d39c
--- /dev/null
+++ b/TraccarClient/pt.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Cliente Traccar";
+"eKa-7m-jq1.title" = "Estado";
+"gEG-ov-HEk.title" = "Sobre";
+"He4-eJ-a31.title" = "Limpar";
+"drL-Ag-IYB.text" = "Localizador GPS em tempo real para dispositivos Android. Compatível com servidor Traccar e outros sistemas de localização.";
+"2Hh-sN-c9g.text" = "Esta aplicação é livre e de código aberto, o código fonte é licenciado sob a licença Apache Versão 2.0 e disponível no GitHub.";
+"jF9-Ub-rRP.text" = "Para mais informações visite \nwww.traccar.org/client";
diff --git a/TraccarClient/ru.lproj/Localizable.strings b/TraccarClient/ru.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..79fa773db4da9c0aad1f94f80289e6e59ca4e260
--- /dev/null
+++ b/TraccarClient/ru.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar Клиент";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "Ошибка отправки";
+"Location update" = "Обновление местоположения";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/ru.lproj/MainStoryboard.strings b/TraccarClient/ru.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..39ee6fbe54e8447fced48c3b29e41a4f11d54958
--- /dev/null
+++ b/TraccarClient/ru.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar Клиент";
+"eKa-7m-jq1.title" = "Состояние";
+"gEG-ov-HEk.title" = "О программе";
+"He4-eJ-a31.title" = "Очистить";
+"drL-Ag-IYB.text" = "Трекер для Android устройств. Совместим с Traccar сервером и другими мониторинговыми системами.";
+"2Hh-sN-c9g.text" = "Это приложение распростаняется бесплатно, исходный код распростаняется по лицензии Apache License Version 2.0 и доступен на GitHub.";
+"jF9-Ub-rRP.text" = "Для получения дополнительной информации посетите\nwww.traccar.org/client";
diff --git a/TraccarClient/zh.lproj/Localizable.strings b/TraccarClient/zh.lproj/Localizable.strings
new file mode 100644
index 0000000000000000000000000000000000000000..a424ae16431f7137e880ff35366f207544ff9633
--- /dev/null
+++ b/TraccarClient/zh.lproj/Localizable.strings
@@ -0,0 +1,9 @@
+"Traccar Client" = "Traccar定位端";
+"Service created" = "Service created";
+"Service destroyed" = "Service destroyed";
+"Send failed" = "发送失败";
+"Location update" = "位置更新";
+"Connectivity change" = "Connectivity change";
+"Invalid server address" = "Invalid server address";
+"Invalid server port" = "Invalid server port";
+"Invalid frequency value" = "Invalid frequency value";
diff --git a/TraccarClient/zh.lproj/MainStoryboard.strings b/TraccarClient/zh.lproj/MainStoryboard.strings
new file mode 100644
index 0000000000000000000000000000000000000000..fc16c54c8649def5c385131d984ad8bc7e286a57
--- /dev/null
+++ b/TraccarClient/zh.lproj/MainStoryboard.strings
@@ -0,0 +1,7 @@
+"TDj-2j-wEh.text" = "Traccar定位端";
+"eKa-7m-jq1.title" = "定位状态";
+"gEG-ov-HEk.title" = "关于";
+"He4-eJ-a31.title" = "清除";
+"drL-Ag-IYB.text" = "Android设备的实时GPS跟踪器。兼容Traccar服务器和其他跟踪系统。";
+"2Hh-sN-c9g.text" = "此应用免费且开源。源码许可为Apache V2.0,在GitHub上发布。";
+"jF9-Ub-rRP.text" = "更多信息请访问www.traccar.org/client";
diff --git a/TraccarClientTests/CoreDataTests.swift b/TraccarClientTests/CoreDataTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..be123bae6964ffb976eb94567c1049da8c29b72e
--- /dev/null
+++ b/TraccarClientTests/CoreDataTests.swift
@@ -0,0 +1,36 @@
+//
+// Copyright 2015 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import XCTest
+import CoreData
+
+class CoreDataTests: XCTestCase {
+
+    var managedObjectContext: NSManagedObjectContext?
+    var managedObjectModel: NSManagedObjectModel?
+    var persistentStoreCoordinator: NSPersistentStoreCoordinator?
+    
+    override func setUp() {
+        let modelURL = Bundle.main.url(forResource: "TraccarClient", withExtension: "momd")
+        managedObjectModel = NSManagedObjectModel(contentsOf: modelURL!)
+
+        persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!)
+        
+        managedObjectContext = NSManagedObjectContext()
+        managedObjectContext?.persistentStoreCoordinator = persistentStoreCoordinator
+    }
+
+}
diff --git a/TraccarClientTests/DatabaseHelperTests.swift b/TraccarClientTests/DatabaseHelperTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..359868db476e26712bbdb22d36cf6716b9e4db12
--- /dev/null
+++ b/TraccarClientTests/DatabaseHelperTests.swift
@@ -0,0 +1,39 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import XCTest
+import TraccarClient
+
+class DatabaseHelperTests: CoreDataTests {
+    
+    func testFormatPosition() {
+        let databaseHelper = DatabaseHelper(managedObjectContext: managedObjectContext!)
+
+        XCTAssertNil(databaseHelper.selectPosition())
+
+        let position = Position(managedObjectContext: managedObjectContext!)
+        position.deviceId = "123456789012345"
+        position.time = Date(timeIntervalSince1970: 0) as NSDate
+
+        XCTAssertNotNil(databaseHelper.selectPosition())
+
+        databaseHelper.delete(position: position)
+
+        XCTAssertNil(databaseHelper.selectPosition())
+    }
+
+    
+}
diff --git a/TraccarClientTests/Info.plist b/TraccarClientTests/Info.plist
new file mode 100644
index 0000000000000000000000000000000000000000..ba72822e8728ef2951005e49b6c27a2f1da6572d
--- /dev/null
+++ b/TraccarClientTests/Info.plist
@@ -0,0 +1,24 @@
+<?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>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/TraccarClientTests/ProtocolFormatterTests.swift b/TraccarClientTests/ProtocolFormatterTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..1f7bb3f73676810b46832337c997ff6937d7e61d
--- /dev/null
+++ b/TraccarClientTests/ProtocolFormatterTests.swift
@@ -0,0 +1,32 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import XCTest
+import TraccarClient
+
+class ProtocolFormatterTests: CoreDataTests {
+
+    func testFormatPosition() {
+        let position = Position(managedObjectContext: managedObjectContext!)
+        position.deviceId = "123456789012345"
+        position.time = Date(timeIntervalSince1970: 0) as NSDate
+
+        let url = ProtocolFormatter.formatPostion(position, url: "http://localhost:5055")
+
+        XCTAssertEqual("http://localhost:5055?id=123456789012345&timestamp=0&lat=0.000000&lon=0.000000&speed=0&bearing=0&altitude=0&accuracy=0&batt=0", url?.absoluteString)
+    }
+
+}
diff --git a/TraccarClientTests/RequestManagerTests.swift b/TraccarClientTests/RequestManagerTests.swift
new file mode 100644
index 0000000000000000000000000000000000000000..432a756962aaabc6c9a8a99886afc1bd5cedb941
--- /dev/null
+++ b/TraccarClientTests/RequestManagerTests.swift
@@ -0,0 +1,33 @@
+//
+// Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import XCTest
+import TraccarClient
+
+class RequestManagerTests: XCTestCase {
+
+    func testSendRequest() {
+        let expectation: XCTestExpectation? = self.expectation(description: "sendRequest")
+
+        RequestManager.sendRequest(URL(string: "http://www.google.com")!, completionHandler: {(success) -> Void in
+            XCTAssertTrue(success)
+            expectation?.fulfill()
+        })
+
+        waitForExpectations(timeout: 5, handler: nil)
+    }
+
+}
diff --git a/translate.py b/translate.py
new file mode 100755
index 0000000000000000000000000000000000000000..d8a2d1004c62ca3329c648d53e3dbcb1245c9694
--- /dev/null
+++ b/translate.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+
+import os
+import optparse
+import urllib2
+import json
+import base64
+import re
+import codecs
+import xml.etree.ElementTree
+
+parser = optparse.OptionParser()
+parser.add_option('-u', '--user', dest='username', help='transifex user login')
+parser.add_option('-p', '--password', dest='password', help='transifex user password')
+
+(options, args) = parser.parse_args()
+
+if not options.username or not options.password:
+    parser.error('User name and password are required')
+
+os.chdir(os.path.dirname(os.path.abspath(__file__)) + '/TraccarClient')
+
+def request(url):
+    req = urllib2.Request(url)
+    auth = base64.encodestring("%s:%s" % (options.username, options.password)).replace("\n", "")
+    req.add_header("Authorization", "Basic %s" % auth)
+    return urllib2.urlopen(req)
+
+mapping_en = {}
+
+def load_en():
+    data = request('https://www.transifex.com/api/2/project/traccar/resource/client/translation/en?file').read().decode('utf-8')
+    for entry in xml.etree.ElementTree.fromstring(data).findall('string'):
+        mapping_en[entry.attrib['name']] = entry.text.replace('Android', 'iOS')
+
+load_en()
+
+regex = re.compile(r'"([^"]+)" = "([^"]+)";')
+
+def write_storyboard(code, mapping):
+    in_file = codecs.open('en.lproj/MainStoryboard.strings', 'r', 'utf-8')
+    out_file = codecs.open(code + '.lproj/MainStoryboard.strings', 'w', 'utf-8')
+    for line in in_file:
+        match = regex.match(line)
+        if match:
+            out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n')
+    out_file.close()
+    in_file.close()
+
+def write_strings(code, mapping):
+    in_file = codecs.open('Base.lproj/Localizable.strings', 'r', 'utf-8')
+    out_file = codecs.open(code + '.lproj/Localizable.strings', 'w', 'utf-8')
+    for line in in_file:
+        match = regex.match(line)
+        if match:
+            out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n')
+    out_file.close()
+    in_file.close()
+
+def write_settings(code, mapping):
+    filename = 'InAppSettings.bundle/' + code + '.lproj/Root.strings'
+    if not os.path.exists(os.path.dirname(filename)):
+        os.makedirs(os.path.dirname(filename))
+    in_file = codecs.open('InAppSettings.bundle/en.lproj/Root.strings', 'r', 'utf-8')
+    out_file = codecs.open(filename, 'w', 'utf-8')
+    for line in in_file:
+        match = regex.match(line)
+        if match:
+            out_file.write('"' + match.group(1) + '" = "' + mapping.get(match.group(2), match.group(2)) + '";\n')
+    out_file.close()
+    in_file.close()
+
+for directory in os.walk('.'):
+    match = re.match(r"([a-z]{2}(?:-[A-Z]{2})?)\.lproj", directory[0][2:])
+    if match:
+        code = match.group(1)
+        if code != 'en':
+            mapping = {}
+            data = request('https://www.transifex.com/api/2/project/traccar/resource/client/translation/' + code.replace('-', '_') + '?file').read()
+            for entry in xml.etree.ElementTree.fromstring(data).findall('string'):
+                mapping[mapping_en[entry.attrib['name']]] = entry.text
+            write_storyboard(code, mapping)
+            write_strings(code, mapping)
+            write_settings(code, mapping)
+            print code