Browse Source

settings, notification setup, and basic export/import

dignifiedquire 6 years ago
parent
commit
af13fd9acd
99 changed files with 7227 additions and 892 deletions
  1. 4 1
      Podfile
  2. 13 1
      Podfile.lock
  3. 78 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD-Defines.h
  4. 306 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD.h
  5. 1046 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD.m
  6. 43 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDAnimation.h
  7. 48 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDAnimation.m
  8. 21 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDErrorIndicatorView.h
  9. 52 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDErrorIndicatorView.m
  10. 30 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeAnimation.h
  11. 56 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeAnimation.m
  12. 37 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeZoomAnimation.h
  13. 77 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeZoomAnimation.m
  14. 25 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDImageIndicatorView.h
  15. 21 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDImageIndicatorView.m
  16. 28 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndeterminateIndicatorView.h
  17. 44 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndeterminateIndicatorView.m
  18. 57 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndicatorView.h
  19. 103 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndicatorView.m
  20. 38 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDPieIndicatorView.h
  21. 171 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDPieIndicatorView.m
  22. 53 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDRingIndicatorView.h
  23. 194 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDRingIndicatorView.m
  24. 37 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDShadow.h
  25. 30 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDShadow.m
  26. 21 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDSuccessIndicatorView.h
  27. 52 0
      Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDSuccessIndicatorView.m
  28. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error.png
  29. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error@2x.png
  30. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error@3x.png
  31. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success.png
  32. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success@2x.png
  33. BIN
      Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success@3x.png
  34. 20 0
      Pods/JGProgressHUD/LICENSE.txt
  35. 174 0
      Pods/JGProgressHUD/README.md
  36. 13 1
      Pods/Manifest.lock
  37. 738 434
      Pods/Pods.xcodeproj/project.pbxproj
  38. 21 0
      Pods/QuickTableViewController/LICENSE
  39. 275 0
      Pods/QuickTableViewController/README.md
  40. 78 0
      Pods/QuickTableViewController/Source/Model/Icon.swift
  41. 120 0
      Pods/QuickTableViewController/Source/Model/RadioSection.swift
  42. 52 0
      Pods/QuickTableViewController/Source/Model/Section.swift
  43. 93 0
      Pods/QuickTableViewController/Source/Model/Subtitle.swift
  44. 50 0
      Pods/QuickTableViewController/Source/Protocol/Configurable.swift
  45. 77 0
      Pods/QuickTableViewController/Source/Protocol/Reusable.swift
  46. 41 0
      Pods/QuickTableViewController/Source/Protocol/Row.swift
  47. 48 0
      Pods/QuickTableViewController/Source/Protocol/RowCompatible.swift
  48. 53 0
      Pods/QuickTableViewController/Source/Protocol/RowStyle.swift
  49. 187 0
      Pods/QuickTableViewController/Source/QuickTableViewController.swift
  50. 102 0
      Pods/QuickTableViewController/Source/Rows/NavigationRow.swift
  51. 110 0
      Pods/QuickTableViewController/Source/Rows/OptionRow.swift
  52. 122 0
      Pods/QuickTableViewController/Source/Rows/SwitchRow.swift
  53. 89 0
      Pods/QuickTableViewController/Source/Rows/TapActionRow.swift
  54. 111 0
      Pods/QuickTableViewController/Source/Views/SwitchCell.swift
  55. 79 0
      Pods/QuickTableViewController/Source/Views/TapActionCell.swift
  56. 19 0
      Pods/ReachabilitySwift/LICENSE
  57. 202 0
      Pods/ReachabilitySwift/README.md
  58. 316 0
      Pods/ReachabilitySwift/Sources/Reachability.swift
  59. 26 0
      Pods/Target Support Files/JGProgressHUD/Info.plist
  60. 5 0
      Pods/Target Support Files/JGProgressHUD/JGProgressHUD-dummy.m
  61. 12 0
      Pods/Target Support Files/JGProgressHUD/JGProgressHUD-prefix.pch
  62. 29 0
      Pods/Target Support Files/JGProgressHUD/JGProgressHUD-umbrella.h
  63. 6 0
      Pods/Target Support Files/JGProgressHUD/JGProgressHUD.modulemap
  64. 9 0
      Pods/Target Support Files/JGProgressHUD/JGProgressHUD.xcconfig
  65. 24 0
      Pods/Target Support Files/JGProgressHUD/ResourceBundle-JGProgressHUD-Info.plist
  66. 72 0
      Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-acknowledgements.markdown
  67. 90 0
      Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-acknowledgements.plist
  68. 6 0
      Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-frameworks.sh
  69. 3 3
      Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.debug.xcconfig
  70. 3 3
      Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.release.xcconfig
  71. 26 0
      Pods/Target Support Files/QuickTableViewController/Info.plist
  72. 5 0
      Pods/Target Support Files/QuickTableViewController/QuickTableViewController-dummy.m
  73. 12 0
      Pods/Target Support Files/QuickTableViewController/QuickTableViewController-prefix.pch
  74. 16 0
      Pods/Target Support Files/QuickTableViewController/QuickTableViewController-umbrella.h
  75. 6 0
      Pods/Target Support Files/QuickTableViewController/QuickTableViewController.modulemap
  76. 9 0
      Pods/Target Support Files/QuickTableViewController/QuickTableViewController.xcconfig
  77. 26 0
      Pods/Target Support Files/ReachabilitySwift/Info.plist
  78. 5 0
      Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-dummy.m
  79. 12 0
      Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-prefix.pch
  80. 16 0
      Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-umbrella.h
  81. 6 0
      Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.modulemap
  82. 10 0
      Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.xcconfig
  83. 31 4
      deltachat-ios.xcodeproj/project.pbxproj
  84. 0 5
      deltachat-ios/AppCoordinator.swift
  85. 206 51
      deltachat-ios/AppDelegate.swift
  86. 9 2
      deltachat-ios/ChatListController.swift
  87. 1 5
      deltachat-ios/ChatViewController.swift
  88. 3 0
      deltachat-ios/Constants.swift
  89. 16 6
      deltachat-ios/ContactCell.swift
  90. 11 3
      deltachat-ios/ContactListController.swift
  91. 0 72
      deltachat-ios/ContactViewController.swift
  92. 31 117
      deltachat-ios/CredentialsController.swift
  93. 10 2
      deltachat-ios/Info.plist
  94. 45 8
      deltachat-ios/NavigationController.swift
  95. 96 129
      deltachat-ios/SettingsController.swift
  96. 94 0
      deltachat-ios/TextFieldCell.swift
  97. 10 0
      deltachat-ios/UIImage+Extension.swift
  98. 343 45
      deltachat-ios/Wrapper.swift
  99. 12 0
      deltachat-ios/deltachat-ios.entitlements

+ 4 - 1
Podfile

@@ -1,12 +1,15 @@
 target 'deltachat-ios' do
   use_frameworks!
   pod 'openssl-ios-bitcode', '1.0.210'
+  pod 'ReachabilitySwift'
+  pod 'QuickTableViewController'
+  pod 'JGProgressHUD'
   pod 'MessageKit', '2.0.0'
   post_install do |installer|
       installer.pods_project.targets.each do |target|
           if target.name == 'MessageKit'
               target.build_configurations.each do |config|
-                  config.build_settings['SWIFT_VERSION'] = '4.0'
+                  config.build_settings['SWIFT_VERSION'] = '4.2'
               end
           end
       end

+ 13 - 1
Podfile.lock

@@ -1,24 +1,36 @@
 PODS:
+  - JGProgressHUD (2.0.3)
   - MessageInputBar/Core (0.4.1)
   - MessageKit (2.0.0):
     - MessageInputBar/Core
   - openssl-ios-bitcode (1.0.210)
+  - QuickTableViewController (1.0.0)
+  - ReachabilitySwift (4.3.0)
 
 DEPENDENCIES:
+  - JGProgressHUD
   - MessageKit (= 2.0.0)
   - openssl-ios-bitcode (= 1.0.210)
+  - QuickTableViewController
+  - ReachabilitySwift
 
 SPEC REPOS:
   https://github.com/cocoapods/specs.git:
+    - JGProgressHUD
     - MessageInputBar
     - MessageKit
     - openssl-ios-bitcode
+    - QuickTableViewController
+    - ReachabilitySwift
 
 SPEC CHECKSUMS:
+  JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
   MessageInputBar: e81c7535347f1f7b923de7080409a535a004b6e4
   MessageKit: 29c1c87e5a396d2ca7a3f712e5171dc9aba42a1e
   openssl-ios-bitcode: c833701a4488bd43de0051db41cfa75f6fef8109
+  QuickTableViewController: a49fb6dc5623b9dbe301a03ac5c563099e234fbc
+  ReachabilitySwift: 408477d1b6ed9779dba301953171e017c31241f3
 
-PODFILE CHECKSUM: 738743efb43b4f6b89816b656cd3f132c9e93608
+PODFILE CHECKSUM: 4cc25fb37ef9036decc6e11a25a0ea70c96e2149
 
 COCOAPODS: 1.5.3

+ 78 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD-Defines.h

@@ -0,0 +1,78 @@
+//
+//  JGProgressHUD-Defines.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 28.04.15.
+//  Copyright (c) 2015 Jonas Gessner. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/**
+ Positions of the HUD.
+ */
+typedef NS_ENUM(NSUInteger, JGProgressHUDPosition) {
+    /** Center position. */
+    JGProgressHUDPositionCenter = 0,
+    /** Top left position. */
+    JGProgressHUDPositionTopLeft,
+    /** Top center position. */
+    JGProgressHUDPositionTopCenter,
+    /** Top right position. */
+    JGProgressHUDPositionTopRight,
+    /** Center left position. */
+    JGProgressHUDPositionCenterLeft,
+    /** Center right position. */
+    JGProgressHUDPositionCenterRight,
+    /** Bottom left position. */
+    JGProgressHUDPositionBottomLeft,
+    /** Bottom center position. */
+    JGProgressHUDPositionBottomCenter,
+    /** Bottom right position. */
+    JGProgressHUDPositionBottomRight
+};
+
+/**
+ Appearance styles of the HUD.
+ */
+typedef NS_ENUM(NSUInteger, JGProgressHUDStyle) {
+    /** Extra light HUD with dark elements. */
+    JGProgressHUDStyleExtraLight = 0,
+    /** Light HUD with dark elemets. */
+    JGProgressHUDStyleLight,
+    /** Dark HUD with light elements. */
+    JGProgressHUDStyleDark
+};
+
+#if TARGET_OS_IOS
+/**
+ Interaction types.
+ */
+typedef NS_ENUM(NSUInteger, JGProgressHUDInteractionType) {
+    /** Block all touches. No interaction behin the HUD is possible. */
+    JGProgressHUDInteractionTypeBlockAllTouches = 0,
+    /** Block touches on the HUD view. */
+    JGProgressHUDInteractionTypeBlockTouchesOnHUDView,
+    /** Block no touches. */
+    JGProgressHUDInteractionTypeBlockNoTouches
+};
+#endif
+
+/**
+ Parallax Modes.
+ */
+typedef NS_ENUM(NSUInteger, JGProgressHUDParallaxMode) {
+    /** Follows the device setting for parallax. If "Reduce Motion" is enabled, no parallax effect is added to the HUD, if "Reduce Motion" is disabled the HUD will have a parallax effect. This behaviour is only supported on iOS 8 and higher. */
+    JGProgressHUDParallaxModeDevice = 0,
+    /** Always adds a parallax effect to the HUD. Parallax is only supported on iOS 7 and higher. */
+    JGProgressHUDParallaxModeAlwaysOn,
+    /** Never adds a parallax effect to the HUD. */
+    JGProgressHUDParallaxModeAlwaysOff
+};
+
+#ifndef fequal
+/**
+ Macro for safe floating point comparison (for internal use in JGProgressHUD).
+ */
+#define fequal(a,b) (fabs((a) - (b)) < FLT_EPSILON)
+#endif

+ 306 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD.h

@@ -0,0 +1,306 @@
+//
+//  JGProgressHUD.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUD-Defines.h"
+#import "JGProgressHUDShadow.h"
+#import "JGProgressHUDAnimation.h"
+#import "JGProgressHUDFadeAnimation.h"
+#import "JGProgressHUDFadeZoomAnimation.h"
+#import "JGProgressHUDIndicatorView.h"
+#import "JGProgressHUDErrorIndicatorView.h"
+#import "JGProgressHUDSuccessIndicatorView.h"
+#import "JGProgressHUDRingIndicatorView.h"
+#import "JGProgressHUDPieIndicatorView.h"
+#import "JGProgressHUDIndeterminateIndicatorView.h"
+
+@protocol JGProgressHUDDelegate;
+
+/**
+ A HUD to indicate progress, success, error, warnings or other notifications to the user.
+@discussion @c JGProgressHUD respects its @c layoutMargins when positioning the HUD view. Additionally, on iOS 11 if @c insetsLayoutMarginsFromSafeArea is set to @c YES (default) the @c layoutMargins additionally contain the @c safeAreaInsets.
+ @note Remember to call every method from the main thread! UIKit => main thread!
+ @attention You may not add JGProgressHUD to a view which has an alpha value < 1.0 or to a view which is a subview of a view with an alpha value < 1.0.
+ */
+@interface JGProgressHUD : UIView
+
+/**
+ Designated initializer.
+ @param style The appearance style of the HUD.
+ */
+- (instancetype __nonnull)initWithStyle:(JGProgressHUDStyle)style;
+
+/**
+ Convenience initializer.
+ @param style The appearance style of the HUD.
+ */
++ (instancetype __nonnull)progressHUDWithStyle:(JGProgressHUDStyle)style;
+
+/**
+ The appearance style of the HUD.
+ @b Default: JGProgressHUDStyleExtraLight.
+ */
+@property (nonatomic, assign, readonly) JGProgressHUDStyle style;
+
+/** The view in which the HUD is presented. */
+@property (nonatomic, weak, readonly, nullable) UIView *targetView;
+
+/**
+ The delegate of the HUD.
+ @sa JGProgressHUDDelegate.
+ */
+@property (nonatomic, weak, nullable) id <JGProgressHUDDelegate> delegate;
+
+/** The actual HUD view visible on screen. You may add animations to this view. */
+@property (nonatomic, strong, readonly, nonnull) UIView *HUDView;
+
+/**
+ The content view inside the @c HUDView. If you want to add additional views to the HUD you should add them as subview to the @c contentView.
+ */
+@property (nonatomic, strong, readonly, nonnull) UIView *contentView;
+
+/**
+ The label used to present text on the HUD. Set the @c text or @c attributedText property of this label to change the displayed text. You may not change the label's @c frame or @c bounds.
+ */
+@property (nonatomic, strong, readonly, nonnull) UILabel *textLabel;
+
+/**
+ The label used to present detail text on the HUD. Set the @c text or @c attributedText property of this label to change the displayed text. You may not change the label's @c frame or @c bounds.
+ */
+@property (nonatomic, strong, readonly, nonnull) UILabel *detailTextLabel;
+
+/**
+ The indicator view. You can assign a custom subclass of @c JGProgressHUDIndicatorView to this property or one of the default indicator views (if you do so, you should assign it before showing the HUD). This value is optional.
+ @b Default: JGProgressHUDIndeterminateIndicatorView.
+ */
+@property (nonatomic, strong, nullable) JGProgressHUDIndicatorView *indicatorView;
+
+/**
+ The shadow cast by the @c HUDView. This value is optional. Setting this to @c nil means no shadow is cast by the HUD.
+ @b Default: nil.
+ */
+@property (nonatomic, strong, nullable) JGProgressHUDShadow *shadow;
+
+/**
+ The position of the HUD inside the hosting view's frame, or inside the specified frame.
+ @b Default: JGProgressHUDPositionCenter
+ */
+@property (nonatomic, assign) JGProgressHUDPosition position;
+
+/**
+ The animation used for showing and dismissing the HUD.
+ @b Default: JGProgressHUDFadeAnimation.
+ */
+@property (nonatomic, strong, nonnull) JGProgressHUDAnimation *animation;
+
+#if TARGET_OS_IOS
+/**
+ Interaction type of the HUD. Determines whether touches should be let through to the views behind the HUD.
+ @sa JGProgressHUDInteractionType.
+ @b Default: JGProgressHUDInteractionTypeBlockAllTouches.
+ */
+@property (nonatomic, assign) JGProgressHUDInteractionType interactionType;
+#endif
+
+/**
+ Parallax mode for the HUD. This setting determines whether the HUD should have a parallax (@c UIDeviceMotion) effect. This effect is controlled by device motion on iOS and remote touchpad panning gestures on tvOS.
+ @sa JGProgressHUDParallaxMode.
+ @b Default: JGProgressHUDParallaxModeDevice.
+ */
+@property (nonatomic, assign) JGProgressHUDParallaxMode parallaxMode;
+
+#if TARGET_OS_TV
+/**
+ When this property is set to @c YES the HUD will try to become focused, which prevents interactions with the @c targetView. If set to @c NO the HUD will not become focused and interactions with @c targetView remain possible. Default: @c YES.
+ */
+@property (nonatomic, assign) BOOL wantsFocus;
+#endif
+
+/**
+ If the HUD should always have the same width and height.
+ @b Default: NO.
+ */
+@property (nonatomic, assign) BOOL square;
+
+/**
+ Internally @c JGProgressHUD uses an @c UIVisualEffectView with a @c UIBlurEffect. A second @c UIVisualEffectView can be added on top of that with a @c UIVibrancyEffect which amplifies and adjusts the color of content layered behind the view, allowing content placed inside the contentView to become more vivid. This flag sets whether the @c UIVibrancyEffect should be used. Using the vibrancy effect can sometimes, depending on the contents of the display, result in a weird look (especially on iOS < 9.3).
+ @b Default: NO.
+ */
+@property (nonatomic, assign) BOOL vibrancyEnabled;
+
+/**
+ The radius used for rounding the four corners of the HUD view.
+ @b Default: 10.0.
+ */
+@property (nonatomic, assign) CGFloat cornerRadius;
+
+/**
+ Insets the contents of the HUD.
+ @b Default: (20, 20, 20, 20).
+ */
+@property (nonatomic, assign) UIEdgeInsets contentInsets;
+
+/**
+ Insets the HUD from the frame of the hosting view or from the specified frame to present the HUD from.
+ @b Default: (20, 20, 20, 20).
+ */
+@property (nonatomic, assign) UIEdgeInsets marginInsets __attribute((deprecated(("Use layoutMargins instead."))));
+
+/**
+ @attention This property is deprecated and does nothing.
+ */
+@property (nonatomic, assign) NSTimeInterval layoutChangeAnimationDuration __attribute((deprecated(("Use UIView animation to animate layout changes. This allows setting a custom animation duration, animaiton curve and other options."))));
+
+/**
+ @return Whether the HUD is visible on screen.
+ */
+@property (nonatomic, assign, readonly, getter = isVisible) BOOL visible;
+
+/**
+ The progress to display using the @c progressIndicatorView. A change of this property is not animated. Use the @c setProgress:animated: method for an animated progress change.
+ @b Default: 0.0.
+ */
+@property (nonatomic, assign) float progress;
+
+/**
+ Adjusts the current progress shown by the receiver, optionally animating the change.
+ 
+ The current progress is represented by a floating-point value between 0.0 and 1.0, inclusive, where 1.0 indicates the completion of the task. The default value is 0.0. Values less than 0.0 and greater than 1.0 are pinned to those limits.
+ @param progress The new progress value.
+ @param animated YES if the change should be animated, NO if the change should happen immediately.
+ */
+- (void)setProgress:(float)progress animated:(BOOL)animated;
+
+/**
+ Specifies a minimum time that the HUD will be on-screen. Useful to prevent the HUD from flashing quickly on the screen when indeterminate tasks complete more quickly than expected.
+ @b Default: 0.0.
+ */
+@property (nonatomic, assign) NSTimeInterval minimumDisplayTime;
+
+/**
+ Determines whether Voice Over announcements should be made upon displaying the HUD (if Voice Over is active).
+ @b Default: YES
+ */
+@property (nonatomic, assign) BOOL voiceOverEnabled;
+
+#if TARGET_OS_IOS
+/**
+ A block to be invoked when the HUD view is tapped.
+ @note The interaction type of the HUD must be @c JGProgressHUDInteractionTypeBlockTouchesOnHUDView or @c JGProgressHUDInteractionTypeBlockAllTouches, otherwise this block won't be fired.
+ */
+@property (nonatomic, copy, nullable) void (^tapOnHUDViewBlock)(JGProgressHUD *__nonnull HUD);
+
+/**
+ A block to be invoked when the area outside of the HUD view is tapped.
+ @note The interaction type of the HUD must be @c JGProgressHUDInteractionTypeBlockAllTouches, otherwise this block won't be fired.
+ */
+@property (nonatomic, copy, nullable) void (^tapOutsideBlock)(JGProgressHUD *__nonnull HUD);
+#endif
+
+/**
+ Shows the HUD animated. You should preferably show the HUD in a UIViewController's view. The HUD will be repositioned in response to rotation and keyboard show/hide notifications.
+ @param view The view to show the HUD in. The frame of the @c view will be used to calculate the position of the HUD.
+ */
+- (void)showInView:(UIView *__nonnull)view;
+
+/**
+ Shows the HUD. You should preferably show the HUD in a UIViewController's view.  The HUD will be repositioned in response to rotation and keyboard show/hide notifications.
+ @param view The view to show the HUD in. The frame of the @c view will be used to calculate the position of the HUD.
+ @param animated If the HUD should show with an animation.
+ */
+- (void)showInView:(UIView *__nonnull)view animated:(BOOL)animated;
+
+/** Dismisses the HUD animated. */
+- (void)dismiss;
+
+/**
+ Dismisses the HUD.
+ @param animated If the HUD should dismiss with an animation.
+ */
+- (void)dismissAnimated:(BOOL)animated;
+
+/**
+ Dismisses the HUD animated after a delay.
+ @param delay The delay until the HUD will be dismissed.
+ */
+- (void)dismissAfterDelay:(NSTimeInterval)delay;
+
+/**
+ Dismisses the HUD after a delay.
+ @param delay The delay until the HUD will be dismissed.
+ @param animated If the HUD should dismiss with an animation.
+ */
+- (void)dismissAfterDelay:(NSTimeInterval)delay animated:(BOOL)animated;
+
+@end
+
+@interface JGProgressHUD (HUDManagement)
+
+/**
+ @param view The view to return all visible progress HUDs for.
+ @return All visible progress HUDs in the view.
+ */
++ (NSArray<JGProgressHUD *> *__nonnull)allProgressHUDsInView:(UIView *__nonnull)view;
+
+/**
+ @param view The view to return all visible progress HUDs for.
+ @return All visible progress HUDs in the view and its subviews.
+ */
++ (NSArray<JGProgressHUD *> *__nonnull)allProgressHUDsInViewHierarchy:(UIView *__nonnull)view;
+
+@end
+
+@interface JGProgressHUD (Deprecated)
+
+#define JG_PROGRESS_HUD_SHOW_IN_RECT_DEPRECATED __attribute((deprecated(("Showing a HUD in a specific frame is no longer supported. Use a blank UIView with the desired frame and present the HUD in that view to achieve this behaviour."))))
+
+/**
+ Shows the HUD animated. You should preferably show the HUD in a UIViewController's view.
+ @param view The view to show the HUD in.
+ @param rect The rect allocated in @c view for displaying the HUD.
+ */
+- (void)showInRect:(CGRect)rect inView:(UIView *__nonnull)view JG_PROGRESS_HUD_SHOW_IN_RECT_DEPRECATED;
+
+/**
+ Shows the HUD. You should preferably show the HUD in a UIViewController's view.
+ @param view The view to show the HUD in.
+ @param rect The rect allocated in @c view for displaying the HUD.
+ @param animated If the HUD should show with an animation.
+ */
+- (void)showInRect:(CGRect)rect inView:(UIView *__nonnull)view animated:(BOOL)animated JG_PROGRESS_HUD_SHOW_IN_RECT_DEPRECATED;
+
+@end
+
+@protocol JGProgressHUDDelegate <NSObject>
+
+@optional
+
+/**
+ Called before the HUD will appear.
+ @param view The view in which the HUD is presented.
+ */
+- (void)progressHUD:(JGProgressHUD *__nonnull)progressHUD willPresentInView:(UIView *__nonnull)view;
+
+/**
+ Called after the HUD appeared.
+ @param view The view in which the HUD is presented.
+ */
+- (void)progressHUD:(JGProgressHUD *__nonnull)progressHUD didPresentInView:(UIView *__nonnull)view;
+
+/**
+ Called before the HUD will disappear.
+ @param view The view in which the HUD is presented and will be dismissed from.
+ */
+- (void)progressHUD:(JGProgressHUD *__nonnull)progressHUD willDismissFromView:(UIView *__nonnull)view;
+
+/**
+ Called after the HUD has disappeared.
+ @param view The view in which the HUD was presented and was be dismissed from.
+ */
+- (void)progressHUD:(JGProgressHUD *__nonnull)progressHUD didDismissFromView:(UIView *__nonnull)view;
+
+@end

+ 1046 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUD.m

@@ -0,0 +1,1046 @@
+//
+//  JGProgressHUD.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUD.h"
+#import <QuartzCore/QuartzCore.h>
+#import "JGProgressHUDFadeAnimation.h"
+#import "JGProgressHUDIndeterminateIndicatorView.h"
+
+#if !__has_feature(objc_arc)
+#error "JGProgressHUD requires ARC!"
+#endif
+
+static CGRect JGProgressHUD_CGRectIntegral(CGRect rect) {
+    CGFloat scale = [[UIScreen mainScreen] scale];
+    
+    return (CGRect){{((CGFloat)floor(rect.origin.x*scale))/scale, ((CGFloat)floor(rect.origin.y*scale))/scale}, {((CGFloat)ceil(rect.size.width*scale))/scale, ((CGFloat)ceil(rect.size.height*scale))/scale}};
+}
+
+@interface JGProgressHUD () {
+    BOOL _transitioning;
+    BOOL _updateAfterAppear;
+    
+    BOOL _dismissAfterTransitionFinished;
+    BOOL _dismissAfterTransitionFinishedWithAnimation;
+    
+    CFAbsoluteTime _displayTimestamp;
+    
+    JGProgressHUDIndicatorView *__nullable _indicatorViewAfterTransitioning;
+    
+    UIView *__nonnull _blurViewContainer;
+    UIView *__nonnull _shadowView;
+    CAShapeLayer *__nonnull _shadowMaskLayer;
+}
+
+@property (nonatomic, strong, readonly, nonnull) UIVisualEffectView *blurView;
+@property (nonatomic, strong, readonly, nonnull) UIVisualEffectView *vibrancyView;
+
+@end
+
+@interface JGProgressHUDAnimation (Private)
+
+@property (nonatomic, weak, nullable) JGProgressHUD *progressHUD;
+
+@end
+
+@implementation JGProgressHUD
+
+@synthesize HUDView = _HUDView;
+@synthesize blurView = _blurView;
+@synthesize vibrancyView = _vibrancyView;
+@synthesize textLabel = _textLabel;
+@synthesize detailTextLabel = _detailTextLabel;
+@synthesize indicatorView = _indicatorView;
+@synthesize animation = _animation;
+@synthesize contentView = _contentView;
+
+@dynamic visible;
+
+#pragma mark - Keyboard
+
+static CGRect keyboardFrame = (CGRect){{0.0, 0.0}, {0.0, 0.0}};
+
+#if TARGET_OS_IOS
++ (void)keyboardFrameWillChange:(NSNotification *)notification {
+    keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+    if (CGRectIsEmpty(keyboardFrame)) {
+        keyboardFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
+    }
+}
+
++ (void)keyboardFrameDidChange:(NSNotification *)notification {
+    keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+}
+
++ (void)keyboardDidHide {
+    keyboardFrame = CGRectZero;
+}
+
++ (void)load {
+    [super load];
+    
+    @autoreleasepool {
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
+        
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameDidChange:) name:UIKeyboardDidChangeFrameNotification object:nil];
+        
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide) name:UIKeyboardDidHideNotification object:nil];
+    }
+}
+#endif
+
++ (CGRect)currentKeyboardFrame {
+    return keyboardFrame;
+}
+
+#pragma mark - Initializers
+
+- (instancetype)init {
+    return [self initWithStyle:JGProgressHUDStyleExtraLight];
+}
+
+- (instancetype)initWithFrame:(CGRect __unused)frame {
+    return [self initWithStyle:JGProgressHUDStyleExtraLight];
+}
+
+/*
+ Basic architecture:
+ 
+ * self covers the entire target view.
+ * self.HUDView is a subview of self and has the size and position of the visible HUD. The layer has rounded corners set with self.cornerRadius. The layer does not clip to bounds.
+ * _shadowView is a subview of self.HUDView and always covers the entire HUDView. The corners are also rounded in the same way as self.HUDView. It draws its shadow only on the outside by using a masking layer, so that the shadow does not interfere with the blur view.
+ * _blurViewContainer is a subview of self.HUDView and always covers the entire self.HUDView. The corners are also rounded in the same way as self.HUDView but it additionally clips to bounds.
+ * self.blurView is a subview of _blurViewContainer and provides the blur effect. The corners are not rounded and the view does not clip to bounds.
+ * self.vibrancyView is a subview of self.blurView.contentView and provides the vibrancy effect. The corners are not rounded and the view does not clip to bounds.
+ * self.contentView is a subview of self.vibrancyView.contentView. It does not always have the same frame as it's superview (during transitions).
+ * self.contentView contains the labels and the indicator view.
+ 
+ */
+- (instancetype)initWithStyle:(JGProgressHUDStyle)style {
+    self = [super initWithFrame:CGRectZero];
+    
+    if (self) {
+        _style = style;
+        _voiceOverEnabled = YES;
+        
+        _HUDView = [[UIView alloc] init];
+        self.HUDView.backgroundColor = [UIColor clearColor];
+        [self addSubview:self.HUDView];
+        
+        _blurViewContainer = [[UIView alloc] init];
+        _blurViewContainer.backgroundColor = [UIColor clearColor];
+        _blurViewContainer.clipsToBounds = YES;
+        [self.HUDView addSubview:_blurViewContainer];
+        
+        _shadowView = [[UIView alloc] init];
+        _shadowView.backgroundColor = [UIColor blackColor];
+        _shadowView.userInteractionEnabled = NO;
+        _shadowView.layer.shadowOpacity = 1.0;
+        _shadowView.alpha = 0.0;
+        
+        _shadowMaskLayer = [CAShapeLayer layer];
+        _shadowMaskLayer.fillRule = kCAFillRuleEvenOdd;
+        _shadowMaskLayer.fillColor = [UIColor blackColor].CGColor;
+        _shadowMaskLayer.opacity = 1.0;
+        
+        _shadowView.layer.mask = _shadowMaskLayer;
+        
+        [self.HUDView addSubview:_shadowView];
+        
+        _indicatorView = [[JGProgressHUDIndeterminateIndicatorView alloc] init];
+        [self.indicatorView setUpForHUDStyle:self.style vibrancyEnabled:self.vibrancyEnabled];
+        
+        self.hidden = YES;
+        self.backgroundColor = [UIColor clearColor];
+        
+        self.contentInsets = UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
+        self.layoutMargins = UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0);
+        
+        self.cornerRadius = 10.0;
+        
+#if TARGET_OS_IOS
+        [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]];
+#elif TARGET_OS_TV
+        _wantsFocus = YES;
+#endif
+    }
+    
+    return self;
+}
+
++ (instancetype)progressHUDWithStyle:(JGProgressHUDStyle)style {
+    return [(JGProgressHUD *)[self alloc] initWithStyle:style];
+}
+
+#pragma mark - Layout
+
+- (void)setHUDViewFrameCenterWithSize:(CGSize)size insetViewFrame:(CGRect)viewFrame {
+    CGRect frame = (CGRect){CGPointZero, size};
+    
+    switch (self.position) {
+        case JGProgressHUDPositionTopLeft:
+            frame.origin.x = CGRectGetMinX(viewFrame);
+            frame.origin.y = CGRectGetMinY(viewFrame);
+            break;
+            
+        case JGProgressHUDPositionTopCenter:
+            frame.origin.x = CGRectGetMidX(viewFrame) - size.width/2.0;
+            frame.origin.y = CGRectGetMinY(viewFrame);
+            break;
+            
+        case JGProgressHUDPositionTopRight:
+            frame.origin.x = CGRectGetMaxX(viewFrame) - size.width;
+            frame.origin.y = CGRectGetMinY(viewFrame);
+            break;
+            
+        case JGProgressHUDPositionCenterLeft:
+            frame.origin.x = CGRectGetMinX(viewFrame);
+            frame.origin.y = CGRectGetMidY(viewFrame) - size.height/2.0;
+            break;
+            
+        case JGProgressHUDPositionCenter:
+            frame.origin.x = CGRectGetMidX(viewFrame) - size.width/2.0;
+            frame.origin.y = CGRectGetMidY(viewFrame) - size.height/2.0;
+            break;
+            
+        case JGProgressHUDPositionCenterRight:
+            frame.origin.x = CGRectGetMaxX(viewFrame) - frame.size.width;
+            frame.origin.y = CGRectGetMidY(viewFrame) - size.height/2.0;
+            break;
+            
+        case JGProgressHUDPositionBottomLeft:
+            frame.origin.x = CGRectGetMinX(viewFrame);
+            frame.origin.y = CGRectGetMaxY(viewFrame) - size.height;
+            break;
+            
+        case JGProgressHUDPositionBottomCenter:
+            frame.origin.x = CGRectGetMidX(viewFrame) - size.width/2.0;
+            frame.origin.y = CGRectGetMaxY(viewFrame) - frame.size.height;
+            break;
+            
+        case JGProgressHUDPositionBottomRight:
+            frame.origin.x = CGRectGetMaxX(viewFrame) - size.width;
+            frame.origin.y = CGRectGetMaxY(viewFrame) - size.height;
+            break;
+    }
+    
+    CGRect oldHUDFrame = self.HUDView.frame;
+    CGRect updatedHUDFrame = JGProgressHUD_CGRectIntegral(frame);
+    
+    self.HUDView.frame = updatedHUDFrame;
+    _shadowView.frame = self.HUDView.bounds;
+    [self updateShadowViewMask];
+    
+    _blurViewContainer.frame = self.HUDView.bounds;
+    self.blurView.frame = self.HUDView.bounds;
+    self.vibrancyView.frame = self.HUDView.bounds;
+    
+    [UIView performWithoutAnimation:^{
+        self.contentView.frame = (CGRect){{(oldHUDFrame.size.width - updatedHUDFrame.size.width)/2.0, (oldHUDFrame.size.height - updatedHUDFrame.size.height)/2.0}, updatedHUDFrame.size};
+    }];
+    
+    self.contentView.frame = self.HUDView.bounds;
+}
+
+- (void)updateShadowViewMask {
+    if (CGRectIsEmpty(_shadowView.layer.bounds)) {
+        return;
+    }
+    
+    CGRect layerBounds = CGRectMake(0.0, 0.0, _shadowView.layer.bounds.size.width + self.shadow.radius*4.0, _shadowView.layer.bounds.size.height + self.shadow.radius*4.0);
+    
+    UIBezierPath *path = [UIBezierPath bezierPathWithRect:layerBounds];
+    
+    CGRect maskRect = CGRectInset(layerBounds, self.shadow.radius*2.0, self.shadow.radius*2.0);
+    
+    UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:maskRect cornerRadius:self.cornerRadius];
+    
+    [path appendPath:roundedPath];
+    
+    _shadowMaskLayer.frame = CGRectInset(_shadowView.layer.bounds, -self.shadow.radius*2.0, -self.shadow.radius*2.0);
+    
+    CAAnimation *currentAnimation = [self.HUDView.layer animationForKey:@"position"];
+    if (currentAnimation != nil) {
+        [CATransaction begin];
+        
+        [CATransaction setAnimationDuration:currentAnimation.duration];
+        [CATransaction setAnimationTimingFunction:currentAnimation.timingFunction];
+        
+        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
+        [_shadowMaskLayer addAnimation:pathAnimation forKey:@"path"];
+        
+        _shadowMaskLayer.path = path.CGPath;
+        
+        [CATransaction commit];
+    }
+    else {
+        _shadowMaskLayer.path = path.CGPath;
+        // Remove implicit CALayer animations:
+        [_shadowMaskLayer removeAllAnimations];
+    }
+}
+
+- (void)layoutHUD {
+    if (_transitioning) {
+        _updateAfterAppear = YES;
+        return;
+    }
+    
+    if (_targetView == nil) {
+        return;
+    }
+    
+    CGRect indicatorFrame = self.indicatorView.frame;
+    indicatorFrame.origin.y = self.contentInsets.top;
+    
+    CGRect insetFrame = [self insetFrameForView:self];
+    
+    CGFloat maxContentWidth = insetFrame.size.width - self.contentInsets.left - self.contentInsets.right;
+    CGFloat maxContentHeight = insetFrame.size.height - self.contentInsets.top - self.contentInsets.bottom;
+    
+    CGRect labelFrame = CGRectZero;
+    CGRect detailFrame = CGRectZero;
+    
+    CGFloat currentY = CGRectGetMaxY(indicatorFrame);
+    if (!CGRectIsEmpty(indicatorFrame)) {
+        currentY += 10.0;
+    }
+    
+    if (_textLabel.text.length > 0) {
+        _textLabel.preferredMaxLayoutWidth = maxContentWidth;
+        
+        CGSize neededSize = _textLabel.intrinsicContentSize;
+        neededSize.height = MIN(neededSize.height, maxContentHeight);
+        
+        labelFrame.size = neededSize;
+        labelFrame.origin.y = currentY;
+        currentY = CGRectGetMaxY(labelFrame) + 6.0;
+    }
+    
+    if (_detailTextLabel.text.length > 0) {
+        _detailTextLabel.preferredMaxLayoutWidth = maxContentWidth;
+        
+        CGSize neededSize = _detailTextLabel.intrinsicContentSize;
+        neededSize.height = MIN(neededSize.height, maxContentHeight);
+        
+        detailFrame.size = neededSize;
+        detailFrame.origin.y = currentY;
+    }
+    
+    CGSize size = CGSizeZero;
+    
+    CGFloat width = MIN(self.contentInsets.left + MAX(indicatorFrame.size.width, MAX(labelFrame.size.width, detailFrame.size.width)) + self.contentInsets.right, insetFrame.size.width);
+    
+    CGFloat height = MAX(CGRectGetMaxY(labelFrame), MAX(CGRectGetMaxY(detailFrame), CGRectGetMaxY(indicatorFrame))) + self.contentInsets.bottom;
+    
+    if (self.square) {
+        CGFloat uniSize = MAX(width, height);
+        
+        size.width = uniSize;
+        size.height = uniSize;
+        
+        CGFloat heightDelta = (uniSize-height)/2.0;
+        
+        labelFrame.origin.y += heightDelta;
+        detailFrame.origin.y += heightDelta;
+        indicatorFrame.origin.y += heightDelta;
+    }
+    else {
+        size.width = width;
+        size.height = height;
+    }
+    
+    CGPoint center = CGPointMake(size.width/2.0, size.height/2.0);
+    
+    indicatorFrame.origin.x = center.x - indicatorFrame.size.width/2.0;
+    labelFrame.origin.x = center.x - labelFrame.size.width/2.0;
+    detailFrame.origin.x = center.x - detailFrame.size.width/2.0;
+    
+    [UIView performWithoutAnimation:^{
+        self.indicatorView.frame = indicatorFrame;
+        self->_textLabel.frame = JGProgressHUD_CGRectIntegral(labelFrame);
+        self->_detailTextLabel.frame = JGProgressHUD_CGRectIntegral(detailFrame);
+    }];
+    
+    [self setHUDViewFrameCenterWithSize:size insetViewFrame:insetFrame];
+}
+
+- (CGRect)insetFrameForView:(UIView *)view {
+    CGRect localKeyboardFrame = [view convertRect:[[self class] currentKeyboardFrame] fromView:nil];
+    CGRect frame = view.bounds;
+    
+    if (!CGRectIsEmpty(localKeyboardFrame) && CGRectIntersectsRect(frame, localKeyboardFrame)) {
+        CGFloat keyboardMinY = CGRectGetMinY(localKeyboardFrame);
+        
+        if (@available(iOS 11, tvOS 11, *)) {
+            if (self.insetsLayoutMarginsFromSafeArea) {
+                // This makes sure that the bottom safe area inset is only respected when that area is not covered by the keyboard. When the keyboard covers the bottom area outside of the safe area it is not necessary to keep the bottom safe area insets part of the insets for the HUD.
+                keyboardMinY += self.safeAreaInsets.bottom;
+            }
+        }
+        
+        frame.size.height = MAX(MIN(frame.size.height, keyboardMinY), 0.0);
+    }
+    
+    return UIEdgeInsetsInsetRect(frame, view.layoutMargins);
+}
+
+- (void)applyCornerRadius {
+    self.HUDView.layer.cornerRadius = self.cornerRadius;
+    _blurViewContainer.layer.cornerRadius = self.cornerRadius;
+    _shadowView.layer.cornerRadius = self.cornerRadius;
+    
+    [self updateShadowViewMask];
+}
+
+#pragma mark - Showing
+
+- (void)cleanUpAfterPresentation {
+#if TARGET_OS_TV
+    if (self.wantsFocus) {
+        [self.targetView setNeedsFocusUpdate];
+    }
+#endif
+
+    self.hidden = NO;
+    
+    _transitioning = NO;
+    // Correct timestamp to the current time for animated presentations:
+    _displayTimestamp = CFAbsoluteTimeGetCurrent();
+    
+    if (_indicatorViewAfterTransitioning) {
+        self.indicatorView = _indicatorViewAfterTransitioning;
+        _indicatorViewAfterTransitioning = nil;
+        _updateAfterAppear = NO;
+    }
+    else if (_updateAfterAppear) {
+        [self layoutHUD];
+        _updateAfterAppear = NO;
+    }
+    
+    if ([self.delegate respondsToSelector:@selector(progressHUD:didPresentInView:)]){
+        [self.delegate progressHUD:self didPresentInView:self.targetView];
+    }
+    
+    if (_dismissAfterTransitionFinished) {
+        [self dismissAnimated:_dismissAfterTransitionFinishedWithAnimation];
+        _dismissAfterTransitionFinished = NO;
+        _dismissAfterTransitionFinishedWithAnimation = NO;
+    }
+    else if (self.voiceOverEnabled && UIAccessibilityIsVoiceOverRunning()) {
+        UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self);
+    }
+}
+
+- (void)showInView:(UIView *)view {
+    [self showInView:view animated:YES];
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    [self layoutHUD];
+}
+
+- (void)showInView:(UIView *)view animated:(BOOL)animated {
+    if (_transitioning) {
+        return;
+    }
+    else if (self.targetView != nil) {
+#if DEBUG
+        NSLog(@"[Warning] The HUD is already visible! Ignoring.");
+#endif
+        return;
+    }
+    
+    if ([self.delegate respondsToSelector:@selector(progressHUD:willPresentInView:)]) {
+        [self.delegate progressHUD:self willPresentInView:view];
+    }
+    
+    _targetView = view;
+    
+    self.frame = _targetView.bounds;
+    
+    [_targetView addSubview:self];
+    
+    self.translatesAutoresizingMaskIntoConstraints = NO;
+    
+    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_targetView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0].active = YES;
+    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_targetView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0].active = YES;
+    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_targetView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0].active = YES;
+    [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_targetView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0].active = YES;
+
+    [self setNeedsLayout];
+    [self layoutIfNeeded];
+    
+    _transitioning = YES;
+    
+    _displayTimestamp = CFAbsoluteTimeGetCurrent();
+    
+    if (animated && self.animation != nil) {
+        [self.animation show];
+    }
+    else {
+        [self cleanUpAfterPresentation];
+    }
+    
+#if TARGET_OS_IOS
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChanged:) name:UIKeyboardWillChangeFrameNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChanged:) name:UIKeyboardDidChangeFrameNotification object:nil];
+#endif
+}
+
+#pragma mark - Dismissing
+
+- (void)cleanUpAfterDismissal {
+    self.hidden = YES;
+    [self removeFromSuperview];
+    
+    [self removeObservers];
+    
+    _transitioning = NO;
+    _dismissAfterTransitionFinished = NO;
+    _dismissAfterTransitionFinishedWithAnimation = NO;
+    
+    __typeof(self.targetView) targetView = self.targetView;
+    _targetView = nil;
+    
+    if ([self.delegate respondsToSelector:@selector(progressHUD:didDismissFromView:)]) {
+        [self.delegate progressHUD:self didDismissFromView:targetView];
+    }
+}
+
+- (void)dismiss {
+    [self dismissAnimated:YES];
+}
+
+- (void)dismissAnimated:(BOOL)animated {
+    if (_transitioning) {
+        _dismissAfterTransitionFinished = YES;
+        _dismissAfterTransitionFinishedWithAnimation = animated;
+        return;
+    }
+    
+    if (self.targetView == nil) {
+        return;
+    }
+    
+    if (self.minimumDisplayTime > 0.0 && _displayTimestamp > 0.0) {
+        CFAbsoluteTime displayedTime = CFAbsoluteTimeGetCurrent()-_displayTimestamp;
+        
+        if (displayedTime < self.minimumDisplayTime) {
+            NSTimeInterval delta = self.minimumDisplayTime-displayedTime;
+            
+            [self dismissAfterDelay:delta animated:animated];
+            
+            return;
+        }
+    }
+    
+    if ([self.delegate respondsToSelector:@selector(progressHUD:willDismissFromView:)]) {
+        [self.delegate progressHUD:self willDismissFromView:self.targetView];
+    }
+    
+    _transitioning = YES;
+    
+    if (animated && self.animation) {
+        [self.animation hide];
+    }
+    else {
+        [self cleanUpAfterDismissal];
+    }
+}
+
+- (void)dismissAfterDelay:(NSTimeInterval)delay {
+    [self dismissAfterDelay:delay animated:YES];
+}
+
+- (void)dismissAfterDelay:(NSTimeInterval)delay animated:(BOOL)animated {
+    __weak __typeof(self) weakSelf = self;
+    
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        if (weakSelf) {
+            __strong __typeof(weakSelf) strongSelf = weakSelf;
+            if (strongSelf.visible) {
+                [strongSelf dismissAnimated:animated];
+            }
+        }
+    });
+}
+
+#pragma mark - Callbacks
+
+#if TARGET_OS_IOS
+- (void)tapped:(UITapGestureRecognizer *)t {
+    if (CGRectContainsPoint(self.contentView.bounds, [t locationInView:self.contentView])) {
+        if (self.tapOnHUDViewBlock != nil) {
+            self.tapOnHUDViewBlock(self);
+        }
+    }
+    else if (self.tapOutsideBlock != nil) {
+        self.tapOutsideBlock(self);
+    }
+}
+
+static UIViewAnimationOptions UIViewAnimationOptionsFromUIViewAnimationCurve(UIViewAnimationCurve curve) {
+    UIViewAnimationOptions testOptions = UIViewAnimationCurveLinear << 16;
+    
+    if (testOptions != UIViewAnimationOptionCurveLinear) {
+        NSLog(@"Unexpected implementation of UIViewAnimationOptionCurveLinear");
+    }
+    
+    return (UIViewAnimationOptions)(curve << 16);
+}
+
+- (void)keyboardFrameChanged:(NSNotification *)notification {
+    NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
+    
+    UIViewAnimationCurve curve = (UIViewAnimationCurve)[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
+    
+    [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionsFromUIViewAnimationCurve(curve) animations:^{
+        [self layoutHUD];
+    } completion:nil];
+}
+#endif
+
+- (void)updateMotionOnHUDView {
+    BOOL reduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled();
+    
+    BOOL wantsParallax = ((self.parallaxMode == JGProgressHUDParallaxModeDevice && !reduceMotionEnabled) || self.parallaxMode == JGProgressHUDParallaxModeAlwaysOn);
+    BOOL hasParallax = (self.HUDView.motionEffects.count > 0);
+    
+    if (wantsParallax == hasParallax) {
+        return;
+    }
+    
+    if (!wantsParallax) {
+        self.HUDView.motionEffects = @[];
+    }
+    else {
+        UIInterpolatingMotionEffect *x = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
+        
+        CGFloat maxMovement = 20.0;
+        
+        x.minimumRelativeValue = @(-maxMovement);
+        x.maximumRelativeValue = @(maxMovement);
+        
+        UIInterpolatingMotionEffect *y = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
+        
+        y.minimumRelativeValue = @(-maxMovement);
+        y.maximumRelativeValue = @(maxMovement);
+        
+        self.HUDView.motionEffects = @[x, y];
+    }
+}
+
+- (void)animationDidFinish:(BOOL)presenting {
+    if (presenting) {
+        [self cleanUpAfterPresentation];
+    }
+    else {
+        [self cleanUpAfterDismissal];
+    }
+}
+
+#pragma mark - Getters
+
+- (BOOL)isVisible {
+    return (self.superview != nil);
+}
+
+- (UIVisualEffectView *)blurView {
+    if (!_blurView) {
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateMotionOnHUDView) name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil];
+        
+        UIBlurEffectStyle effect;
+        
+        if (self.style == JGProgressHUDStyleDark) {
+            effect = UIBlurEffectStyleDark;
+        }
+        else if (self.style == JGProgressHUDStyleLight) {
+            effect = UIBlurEffectStyleLight;
+        }
+        else {
+            effect = UIBlurEffectStyleExtraLight;
+        }
+        
+        UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:effect];
+        
+        _blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
+        
+        [self updateMotionOnHUDView];
+        
+        [_blurViewContainer addSubview:_blurView];
+        
+#if TARGET_OS_IOS
+        [self.contentView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]];
+#endif
+    }
+    
+    return _blurView;
+}
+
+- (UIVisualEffectView *)vibrancyView {
+    if (!_vibrancyView) {
+        UIVibrancyEffect *vibrancyEffect = (self.vibrancyEnabled ? [UIVibrancyEffect effectForBlurEffect:(UIBlurEffect *)self.blurView.effect] : nil);
+        
+        _vibrancyView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
+
+        [self.blurView.contentView addSubview:_vibrancyView];
+    }
+    
+    return _vibrancyView;
+}
+
+- (UIView *)contentView {
+    if (_contentView == nil) {
+        _contentView = [[UIView alloc] init];
+        [self.vibrancyView.contentView addSubview:_contentView];
+        
+        if (self.indicatorView != nil) {
+            [self.contentView addSubview:self.indicatorView];
+        }
+    }
+    
+    return _contentView;
+}
+
+- (UILabel *)textLabel {
+    if (!_textLabel) {
+        _textLabel = [[UILabel alloc] init];
+        _textLabel.backgroundColor = [UIColor clearColor];
+        _textLabel.textColor = (self.style == JGProgressHUDStyleDark ? [UIColor whiteColor] : [UIColor blackColor]);
+        _textLabel.textAlignment = NSTextAlignmentCenter;
+#if TARGET_OS_TV
+        CGFloat fontSize = 20.0;
+#else
+        CGFloat fontSize = 17.0;
+#endif
+        _textLabel.font = [UIFont boldSystemFontOfSize:fontSize];
+        _textLabel.numberOfLines = 0;
+        [_textLabel addObserver:self forKeyPath:@"attributedText" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        [_textLabel addObserver:self forKeyPath:@"text" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        [_textLabel addObserver:self forKeyPath:@"font" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        _textLabel.isAccessibilityElement = YES;
+        
+        [self.contentView addSubview:_textLabel];
+    }
+    
+    return _textLabel;
+}
+
+- (UILabel *)detailTextLabel {
+    if (!_detailTextLabel) {
+        _detailTextLabel = [[UILabel alloc] init];
+        _detailTextLabel.backgroundColor = [UIColor clearColor];
+        _detailTextLabel.textColor = (self.style == JGProgressHUDStyleDark ? [UIColor whiteColor] : [UIColor blackColor]);
+        _detailTextLabel.textAlignment = NSTextAlignmentCenter;
+#if TARGET_OS_TV
+        CGFloat fontSize = 17.0;
+#else
+        CGFloat fontSize = 15.0;
+#endif
+        _detailTextLabel.font = [UIFont systemFontOfSize:fontSize];
+        _detailTextLabel.numberOfLines = 0;
+        [_detailTextLabel addObserver:self forKeyPath:@"attributedText" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        [_detailTextLabel addObserver:self forKeyPath:@"text" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        [_detailTextLabel addObserver:self forKeyPath:@"font" options:(NSKeyValueObservingOptions)kNilOptions context:NULL];
+        _detailTextLabel.isAccessibilityElement = YES;
+        
+        [self.contentView addSubview:_detailTextLabel];
+    }
+    
+    return _detailTextLabel;
+}
+
+- (JGProgressHUDAnimation *)animation {
+    if (!_animation) {
+        self.animation = [JGProgressHUDFadeAnimation animation];
+    }
+    
+    return _animation;
+}
+
+#pragma mark - Setters
+
+#if TARGET_OS_TV
+- (void)setWantsFocus:(BOOL)wantsFocus {
+    if (self.wantsFocus == wantsFocus) {
+        return;
+    }
+
+    _wantsFocus = wantsFocus;
+
+    self.userInteractionEnabled = self.wantsFocus;
+
+    [self.targetView setNeedsFocusUpdate];
+}
+#endif
+
+- (void)setCornerRadius:(CGFloat)cornerRadius {
+    if (fequal(self.cornerRadius, cornerRadius)) {
+        return;
+    }
+    
+    _cornerRadius = cornerRadius;
+    
+    [self applyCornerRadius];
+}
+
+- (void)setShadow:(JGProgressHUDShadow *)shadow {
+    if (shadow == self.shadow) {
+        return;
+    }
+    
+    _shadow = shadow;
+    
+    [self updateShadowViewMask];
+    
+    if (_shadow != nil) {
+        _shadowView.layer.shadowColor = _shadow.color.CGColor;
+        _shadowView.layer.shadowOffset = _shadow.offset;
+        _shadowView.layer.shadowRadius = _shadow.radius;
+        
+        _shadowView.alpha = _shadow.opacity;
+    }
+    else {
+        _shadowView.layer.shadowOffset = CGSizeZero;
+        _shadowView.layer.shadowRadius = 0.0;
+        
+        _shadowView.alpha = 0.0;
+    }
+}
+
+- (void)setAnimation:(JGProgressHUDAnimation *)animation {
+    if (_animation == animation) {
+        return;
+    }
+    
+    _animation.progressHUD = nil;
+    
+    _animation = animation;
+    
+    _animation.progressHUD = self;
+}
+
+- (void)setParallaxMode:(JGProgressHUDParallaxMode)parallaxMode {
+    if (self.parallaxMode == parallaxMode) {
+        return;
+    }
+    
+    _parallaxMode = parallaxMode;
+    
+    [self updateMotionOnHUDView];
+}
+
+- (void)setPosition:(JGProgressHUDPosition)position {
+    if (self.position == position) {
+        return;
+    }
+    
+    _position = position;
+    [self layoutHUD];
+}
+
+- (void)setSquare:(BOOL)square {
+    if (self.square == square) {
+        return;
+    }
+    
+    _square = square;
+    
+    [self layoutHUD];
+}
+
+- (void)setVibrancyEnabled:(BOOL)vibrancyEnabled {
+    if (vibrancyEnabled == self.vibrancyEnabled) {
+        return;
+    }
+    
+    _vibrancyEnabled = vibrancyEnabled;
+    
+    UIVibrancyEffect *vibrancyEffect = (self.vibrancyEnabled ? [UIVibrancyEffect effectForBlurEffect:(UIBlurEffect *)self.blurView.effect] : nil);
+    
+    self.vibrancyView.effect = vibrancyEffect;
+    
+    [self.indicatorView setUpForHUDStyle:self.style vibrancyEnabled:self.vibrancyEnabled];
+}
+
+- (void)setIndicatorView:(JGProgressHUDIndicatorView *)indicatorView {
+    if (self.indicatorView == indicatorView) {
+        return;
+    }
+    
+    if (_transitioning) {
+        _indicatorViewAfterTransitioning = indicatorView;
+        return;
+    }
+    
+    [UIView performWithoutAnimation:^{
+        [self->_indicatorView removeFromSuperview];
+        self->_indicatorView = indicatorView;
+        
+        if (self.indicatorView != nil) {
+            [self.indicatorView setUpForHUDStyle:self.style vibrancyEnabled:self.vibrancyEnabled];
+            [self.contentView addSubview:self.indicatorView];
+        }
+    }];
+    
+    [self layoutHUD];
+}
+
+- (void)layoutMarginsDidChange {
+    [super layoutMarginsDidChange];
+    
+    [self layoutHUD];
+}
+
+- (void)setMarginInsets:(UIEdgeInsets)marginInsets {
+    [self setLayoutMargins:marginInsets];
+}
+
+- (UIEdgeInsets)marginInsets {
+    return [self layoutMargins];
+}
+
+- (void)setContentInsets:(UIEdgeInsets)contentInsets {
+    if (UIEdgeInsetsEqualToEdgeInsets(self.contentInsets, contentInsets)) {
+        return;
+    }
+    
+    _contentInsets = contentInsets;
+    
+    [self layoutHUD];
+}
+
+- (void)setProgress:(float)progress {
+    [self setProgress:progress animated:NO];
+}
+
+- (void)setProgress:(float)progress animated:(BOOL)animated {
+    if (fequal(self.progress, progress)) {
+        return;
+    }
+    
+    _progress = progress;
+    
+    [self.indicatorView setProgress:progress animated:animated];
+}
+
+#pragma mark - Overrides
+
+#if TARGET_OS_IOS
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
+    if (self.interactionType == JGProgressHUDInteractionTypeBlockNoTouches) {
+        return nil;
+    }
+    else {
+        UIView *view = [super hitTest:point withEvent:event];
+
+        if (self.interactionType == JGProgressHUDInteractionTypeBlockAllTouches) {
+            return view;
+        }
+        else if (self.interactionType == JGProgressHUDInteractionTypeBlockTouchesOnHUDView && view != self) {
+            return view;
+        }
+        
+        return nil;
+    }
+}
+#elif TARGET_OS_TV
+- (NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments {
+    return @[self];
+}
+
+- (UIView *)preferredFocusedView {
+    return nil;
+}
+
+- (BOOL)canBecomeFocused {
+    return self.wantsFocus;
+}
+#endif
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    if (object == _textLabel || object == _detailTextLabel) {
+        [self layoutHUD];
+    }
+    else {
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+    }
+}
+
+- (void)dealloc {
+    [self removeObservers];
+    
+    [_textLabel removeObserver:self forKeyPath:@"attributedText"];
+    [_textLabel removeObserver:self forKeyPath:@"text"];
+    [_textLabel removeObserver:self forKeyPath:@"font"];
+    
+    [_detailTextLabel removeObserver:self forKeyPath:@"attributedText"];
+    [_detailTextLabel removeObserver:self forKeyPath:@"text"];
+    [_detailTextLabel removeObserver:self forKeyPath:@"font"];
+}
+
+- (void)removeObservers {
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil];
+    
+#if TARGET_OS_IOS
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidChangeFrameNotification object:nil];
+#endif
+}
+
+@end
+
+@implementation JGProgressHUD (HUDManagement)
+
++ (NSArray *)allProgressHUDsInView:(UIView *)view {
+    NSMutableArray *HUDs = [NSMutableArray array];
+    
+    for (UIView *v in view.subviews) {
+        if ([v isKindOfClass:[JGProgressHUD class]]) {
+            [HUDs addObject:v];
+        }
+    }
+    
+    return HUDs.copy;
+}
+
++ (NSMutableArray *)_allProgressHUDsInViewHierarchy:(UIView *)view {
+    NSMutableArray *HUDs = [NSMutableArray array];
+    
+    for (UIView *v in view.subviews) {
+        if ([v isKindOfClass:[JGProgressHUD class]]) {
+            [HUDs addObject:v];
+        }
+        else {
+            [HUDs addObjectsFromArray:[self _allProgressHUDsInViewHierarchy:v]];
+        }
+    }
+    
+    return HUDs;
+}
+
++ (NSArray *)allProgressHUDsInViewHierarchy:(UIView *)view {
+    return [self _allProgressHUDsInViewHierarchy:view].copy;
+}
+
+@end
+
+@implementation JGProgressHUD (Deprecated)
+
+- (void)showInRect:(CGRect __unused)rect inView:(UIView *)view {
+    [self showInView:view];
+}
+
+- (void)showInRect:(CGRect __unused)rect inView:(UIView *)view animated:(BOOL)animated {
+    [self showInView:view animated:animated];
+}
+
+@end

+ 43 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDAnimation.h

@@ -0,0 +1,43 @@
+//
+//  JGProgressHUDAnimation.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@class JGProgressHUD;
+
+/**
+ You may subclass this class to create a custom progress indicator view.
+ */
+@interface JGProgressHUDAnimation : NSObject
+
+/** Convenience initializer. */
++ (instancetype __nonnull)animation;
+
+/** The HUD using this animation. */
+@property (nonatomic, weak, readonly, nullable) JGProgressHUD *progressHUD;
+
+/**
+ The @c progressHUD is hidden from screen with @c alpha = 1 and @c hidden = @c YES. Ideally, you should prepare the HUD for presentation, then set @c hidden to @c NO on the @c progressHUD and then perform the animation.
+  @post Call @c animationFinished.
+ */
+- (void)show NS_REQUIRES_SUPER;
+
+/**
+ The @c progressHUD wis visible on screen with @c alpha = 1 and @c hidden = @c NO. You should only perform the animation in this method, the @c progressHUD itself will take care of hiding itself and removing itself from superview.
+ @post Call @c animationFinished.
+ */
+- (void)hide NS_REQUIRES_SUPER;
+
+/**
+ @pre This method should only be called at the end of a @c show or @c hide animaiton.
+ @attention ALWAYS call this method after completing a @c show or @c hide animation.
+ */
+- (void)animationFinished NS_REQUIRES_SUPER;
+
+@end

+ 48 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDAnimation.m

@@ -0,0 +1,48 @@
+//
+//  JGProgressHUDAnimation.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import "JGProgressHUDAnimation.h"
+#import "JGProgressHUD.h"
+
+@interface JGProgressHUD (Private)
+
+- (void)animationDidFinish:(BOOL)presenting;
+
+@end
+
+@interface JGProgressHUDAnimation () {
+    BOOL _presenting;
+}
+
+@property (nonatomic, weak) JGProgressHUD *progressHUD;
+
+@end
+
+@implementation JGProgressHUDAnimation
+
+#pragma mark - Initializers
+
++ (instancetype)animation {
+    return [[self alloc] init];
+}
+
+#pragma mark - Public methods
+
+- (void)show {
+    _presenting = YES;
+}
+
+- (void)hide {
+    _presenting = NO;
+}
+
+- (void)animationFinished {
+    [self.progressHUD animationDidFinish:_presenting];
+}
+
+@end

+ 21 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDErrorIndicatorView.h

@@ -0,0 +1,21 @@
+//
+//  JGProgressHUDErrorIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.08.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDImageIndicatorView.h"
+
+/**
+ An image indicator showing a cross, representing a failed operation.
+ */
+@interface JGProgressHUDErrorIndicatorView : JGProgressHUDImageIndicatorView
+
+/**
+ Default initializer for this class.
+ */
+- (instancetype __nonnull)init;
+
+@end

+ 52 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDErrorIndicatorView.m

@@ -0,0 +1,52 @@
+//
+//  JGProgressHUDErrorIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.08.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDErrorIndicatorView.h"
+#import "JGProgressHUD.h"
+
+@implementation JGProgressHUDErrorIndicatorView
+
+- (instancetype)initWithContentView:(UIView *__unused)contentView {
+    NSBundle *currentBundle = [NSBundle bundleForClass:[self class]];
+    NSURL *resourceBundleURL = [currentBundle URLForResource:@"JGProgressHUD" withExtension:@"bundle"];
+    NSBundle *resourceBundle = currentBundle;
+    if (resourceBundleURL) {
+        resourceBundle = [NSBundle bundleWithURL:resourceBundleURL] ?: currentBundle;
+    }
+
+    NSString *imgPath = [resourceBundle pathForResource:@"jg_hud_error" ofType:@"png"];
+    self = [super initWithImage:[[UIImage imageWithContentsOfFile:imgPath] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
+
+    return self;
+}
+
+- (instancetype)init {
+    return [self initWithContentView:nil];
+}
+
+- (void)updateAccessibility {
+    self.accessibilityLabel = NSLocalizedString(@"Error",);
+}
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {
+    [super setUpForHUDStyle:style vibrancyEnabled:vibrancyEnabled];
+
+    if (style == JGProgressHUDStyleDark) {
+        self.contentView.tintColor = [UIColor whiteColor];
+    }
+    else {
+        self.contentView.tintColor = [UIColor blackColor];
+    }
+}
+
+- (void)tintColorDidChange {
+    [super tintColorDidChange];
+    self.contentView.tintColor = self.tintColor;
+}
+
+@end

+ 30 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeAnimation.h

@@ -0,0 +1,30 @@
+//
+//  JGProgressHUDFadeAnimation.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDAnimation.h"
+
+/**
+ A simple fade animation that fades the HUD from alpha @c 0.0 to alpha @c 1.0.
+ */
+@interface JGProgressHUDFadeAnimation : JGProgressHUDAnimation
+
+/**
+ Duration of the animation.
+ 
+ @b Default: 0.4.
+ */
+@property (nonatomic, assign) NSTimeInterval duration;
+
+/**
+ Animation options
+ 
+ @b Default: UIViewAnimationOptionCurveEaseInOut.
+ */
+@property (nonatomic, assign) UIViewAnimationOptions animationOptions;
+
+@end

+ 56 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeAnimation.m

@@ -0,0 +1,56 @@
+//
+//  JGProgressHUDFadeAnimation.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import "JGProgressHUDFadeAnimation.h"
+#import "JGProgressHUD.h"
+
+@implementation JGProgressHUDFadeAnimation
+
+#pragma mark - Initializers
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        self.duration = 0.4;
+        self.animationOptions = UIViewAnimationOptionCurveEaseInOut;
+    }
+    return self;
+}
+
+- (void)setAnimationOptions:(UIViewAnimationOptions)animationOptions {
+    _animationOptions = (animationOptions | UIViewAnimationOptionBeginFromCurrentState);
+}
+
+#pragma mark - Showing
+
+- (void)show {
+    [super show];
+    
+    self.progressHUD.alpha = 0.0;
+    self.progressHUD.hidden = NO;
+    
+    [UIView animateWithDuration:self.duration delay:0.0 options:self.animationOptions animations:^{
+        self.progressHUD.alpha = 1.0;
+    } completion:^(BOOL __unused finished) {
+        [self animationFinished];
+    }];
+}
+
+#pragma mark - Hiding
+
+- (void)hide {
+    [super hide];
+    
+    [UIView animateWithDuration:self.duration delay:0.0 options:self.animationOptions animations:^{
+        self.progressHUD.alpha = 0.0;
+    } completion:^(BOOL __unused finished) {
+        [self animationFinished];
+    }];
+}
+
+@end

+ 37 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeZoomAnimation.h

@@ -0,0 +1,37 @@
+//
+//  JGProgressHUDFadeZoomAnimation.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import "JGProgressHUDAnimation.h"
+
+/**
+ An animation that fades in the HUD and expands the HUD from scale @c (0, 0) to a customizable scale, and finally to scale @c (1, 1), creating a bouncing effect.
+ */
+@interface JGProgressHUDFadeZoomAnimation : JGProgressHUDAnimation
+
+/**
+ Duration of the animation from or to the shrinked state.
+ 
+ @b Default: 0.2.
+ */
+@property (nonatomic, assign) NSTimeInterval shrinkAnimationDuaration;
+
+/**
+ Duration of the animation from or to the expanded state.
+ 
+ @b Default: 0.1.
+ */
+@property (nonatomic, assign) NSTimeInterval expandAnimationDuaration;
+
+/**
+ The scale to apply to the HUD when expanding.
+ 
+ @b Default: (1.1, 1.1).
+ */
+@property (nonatomic, assign) CGSize expandScale;
+
+@end

+ 77 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDFadeZoomAnimation.m

@@ -0,0 +1,77 @@
+//
+//  JGProgressHUDFadeZoomAnimation.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDFadeZoomAnimation.h"
+#import "JGProgressHUD.h"
+
+@implementation JGProgressHUDFadeZoomAnimation
+
+#pragma mark - Initializers
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        self.shrinkAnimationDuaration = 0.2;
+        self.expandAnimationDuaration = 0.1;
+        self.expandScale = CGSizeMake(1.1, 1.1);
+    }
+    return self;
+}
+
+#pragma mark - Showing
+
+- (void)show {
+    [super show];
+    
+    self.progressHUD.alpha = 0.0;
+    self.progressHUD.HUDView.transform = CGAffineTransformMakeScale(0.1, 0.1);
+    
+    NSTimeInterval totalDuration = self.expandAnimationDuaration+self.shrinkAnimationDuaration;
+    
+    self.progressHUD.hidden = NO;
+    
+    [UIView animateWithDuration:totalDuration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut) animations:^{
+        self.progressHUD.alpha = 1.0;
+    } completion:nil];
+    
+    [UIView animateWithDuration:self.shrinkAnimationDuaration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) animations:^{
+        self.progressHUD.HUDView.transform = CGAffineTransformMakeScale(self.expandScale.width, self.expandScale.height);
+    } completion:^(BOOL __unused _finished) {
+        [UIView animateWithDuration:self.expandAnimationDuaration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState) animations:^{
+            self.progressHUD.HUDView.transform = CGAffineTransformIdentity;
+        } completion:^(BOOL __unused __finished) {
+            [self animationFinished];
+        }];
+    }];
+}
+
+#pragma mark - Hiding
+
+- (void)hide {
+    [super hide];
+    
+    NSTimeInterval totalDuration = self.expandAnimationDuaration+self.shrinkAnimationDuaration;
+    
+    [UIView animateWithDuration:totalDuration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut) animations:^{
+        self.progressHUD.alpha = 0.0;
+    } completion:nil];
+    
+    [UIView animateWithDuration:self.expandAnimationDuaration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) animations:^{
+        self.progressHUD.HUDView.transform = CGAffineTransformMakeScale(self.expandScale.width, self.expandScale.height);
+    } completion:^(BOOL __unused _finished) {
+        [UIView animateWithDuration:self.shrinkAnimationDuaration delay:0.0 options:(UIViewAnimationOptions)(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState) animations:^{
+            self.progressHUD.HUDView.transform = CGAffineTransformMakeScale(0.1, 0.1);
+        } completion:^(BOOL __unused __finished) {
+            self.progressHUD.HUDView.transform = CGAffineTransformIdentity;
+            
+            [self animationFinished];
+        }];
+    }];
+}
+
+@end

+ 25 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDImageIndicatorView.h

@@ -0,0 +1,25 @@
+//
+//  JGProgressHUDImageIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 05.08.15.
+//  Copyright (c) 2015 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDIndicatorView.h"
+
+/**
+ An indicator for displaying custom images. Supports animated images.
+ 
+ You may subclass this class to create a custom image indicator view.
+ */
+@interface JGProgressHUDImageIndicatorView : JGProgressHUDIndicatorView
+
+/**
+ Initializes the indicator view with an UIImageView showing the @c image.
+ 
+ @param image The image to show in the indicator view.
+ */
+- (instancetype __nonnull)initWithImage:(UIImage *__nonnull)image;
+
+@end

+ 21 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDImageIndicatorView.m

@@ -0,0 +1,21 @@
+//
+//  JGProgressHUDImageIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 05.08.15.
+//  Copyright (c) 2015 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDImageIndicatorView.h"
+
+@implementation JGProgressHUDImageIndicatorView
+
+- (instancetype)initWithImage:(UIImage *)image {
+    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
+    
+    self = [super initWithContentView:imageView];
+    
+    return self;
+}
+
+@end

+ 28 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndeterminateIndicatorView.h

@@ -0,0 +1,28 @@
+//
+//  JGProgressHUDIndeterminateIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.07.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUD-Defines.h"
+#import "JGProgressHUDIndicatorView.h"
+
+/**
+ An indeterminate progress indicator showing a @c UIActivityIndicatorView.
+ */
+@interface JGProgressHUDIndeterminateIndicatorView : JGProgressHUDIndicatorView
+
+/**
+ Initializes the indicator view and sets the correct color to match the HUD style.
+ */
+- (instancetype __nonnull)initWithHUDStyle:(JGProgressHUDStyle)style __attribute((deprecated(("This initializer is no longer needed. Use the init initializer method."))));
+
+/**
+ Set the color of the activity indicator view.
+ @param color The color to apply to the activity indicator view.
+ */
+- (void)setColor:(UIColor *__nonnull)color;
+
+@end

+ 44 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndeterminateIndicatorView.m

@@ -0,0 +1,44 @@
+//
+//  JGProgressHUDIndeterminateIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.07.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDIndeterminateIndicatorView.h"
+
+@implementation JGProgressHUDIndeterminateIndicatorView
+
+- (instancetype)init {
+    UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
+    [activityIndicatorView startAnimating];
+    
+    self = [super initWithContentView:activityIndicatorView];
+    return self;
+}
+
+- (instancetype)initWithHUDStyle:(JGProgressHUDStyle)style {
+    return [self init];
+}
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {
+    [super setUpForHUDStyle:style vibrancyEnabled:vibrancyEnabled];
+    
+    if (style != JGProgressHUDStyleDark) {
+        self.color = [UIColor blackColor];
+    }
+    else {
+        self.color = [UIColor whiteColor];
+    }
+}
+
+- (void)setColor:(UIColor *)color {
+    [(UIActivityIndicatorView *)self.contentView setColor:color];
+}
+
+- (void)updateAccessibility {
+    self.accessibilityLabel = NSLocalizedString(@"Indeterminate progress",);
+}
+
+@end

+ 57 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndicatorView.h

@@ -0,0 +1,57 @@
+//
+//  JGProgressHUDIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import "JGProgressHUD-Defines.h"
+
+/** You may subclass this class to create a custom progress indicator view. */
+@interface JGProgressHUDIndicatorView : UIView
+
+/**
+ Designated initializer for this class.
+ 
+ @param contentView The content view to place on the container view (the container is the JGProgressHUDIndicatorView).
+ */
+- (instancetype __nonnull)initWithContentView:(UIView *__nullable)contentView;
+
+/** Use this method to set up the indicator view to fit the HUD style and vibrancy setting. This method is called by @c JGProgressHUD when the indicator view is added to the HUD and when the HUD's @c vibrancyEnabled property changes. This method may be called multiple times with different values. The default implementation does nothing. */
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled;
+
+/** Ranges from 0.0 to 1.0. */
+@property (nonatomic, assign) float progress;
+
+/**
+ Adjusts the current progress shown by the receiver, optionally animating the change.
+ 
+ The current progress is represented by a floating-point value between 0.0 and 1.0, inclusive, where 1.0 indicates the completion of the task. The default value is 0.0. Values less than 0.0 and greater than 1.0 are pinned to those limits.
+ 
+ @param progress The new progress value.
+ @param animated YES if the change should be animated, NO if the change should happen immediately.
+ */
+- (void)setProgress:(float)progress animated:(BOOL)animated;
+
+/**
+ The content view which displays the progress.
+ */
+@property (nonatomic, strong, readonly, nullable) UIView *contentView;
+
+/** Schedules an accessibility update on the next run loop. */
+- (void)setNeedsAccessibilityUpdate;
+
+/**
+ Runs @c updateAccessibility immediately if an accessibility update has been scheduled (through @c setNeedsAccessibilityUpdate) but has not executed yet.
+ */
+- (void)updateAccessibilityIfNeeded;
+
+/**
+ Override to set custom accessibility properties. This method gets called once when initializing the view and after calling @c setNeedsAccessibilityUpdate.
+ */
+- (void)updateAccessibility;
+
+@end

+ 103 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDIndicatorView.m

@@ -0,0 +1,103 @@
+//
+//  JGProgressHUDIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import "JGProgressHUDIndicatorView.h"
+#import "JGProgressHUD.h"
+
+@interface JGProgressHUDIndicatorView () {
+    BOOL _accessibilityUpdateScheduled;
+}
+
++ (void)runBlock:(void (^)(void))block;
+
+@end
+
+static void runOnNextRunLoop(void (^block)(void)) {
+    [[NSRunLoop currentRunLoop] performSelector:@selector(runBlock:) target:[JGProgressHUDIndicatorView class] argument:(id)block order:0 modes:@[NSRunLoopCommonModes]];
+}
+
+@implementation JGProgressHUDIndicatorView
+
+#pragma mark - Initializers
+
+- (instancetype)initWithFrame:(CGRect __unused)frame {
+    return [self init];
+}
+
+- (instancetype)init {
+    return [self initWithContentView:nil];
+}
+
+- (instancetype)initWithContentView:(UIView *)contentView {
+    self = [super initWithFrame:(contentView ? contentView.frame : CGRectMake(0.0, 0.0, 50.0, 50.0))];
+    if (self) {
+        self.opaque = NO;
+        self.backgroundColor = [UIColor clearColor];
+        
+        self.isAccessibilityElement = YES;
+        [self setNeedsAccessibilityUpdate];
+        
+        if (contentView) {
+            _contentView = contentView;
+            
+            [self addSubview:self.contentView];
+        }
+    }
+    return self;
+}
+
+#pragma mark - Setup
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {}
+
+#pragma mark - Accessibility
+
++ (void)runBlock:(void (^)(void))block {
+    if (block != nil) {
+        block();
+    }
+}
+
+- (void)setNeedsAccessibilityUpdate {
+    if (!_accessibilityUpdateScheduled) {
+        _accessibilityUpdateScheduled = YES;
+        
+        runOnNextRunLoop(^{
+            [self updateAccessibilityIfNeeded];
+        });
+    }
+}
+
+- (void)updateAccessibilityIfNeeded {
+    if (_accessibilityUpdateScheduled) {
+        [self updateAccessibility];
+        _accessibilityUpdateScheduled = NO;
+    }
+}
+
+- (void)updateAccessibility {
+    self.accessibilityLabel = [NSLocalizedString(@"Loading",) stringByAppendingFormat:@" %.f %%", self.progress];
+}
+
+#pragma mark - Getters & Setters
+
+- (void)setProgress:(float)progress {
+    [self setProgress:progress animated:NO];
+}
+
+- (void)setProgress:(float)progress animated:(__unused BOOL)animated {
+    if (fequal(self.progress, progress)) {
+        return;
+    }
+    
+    _progress = progress;
+    
+    [self setNeedsAccessibilityUpdate];
+}
+
+@end

+ 38 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDPieIndicatorView.h

@@ -0,0 +1,38 @@
+//
+//  JGProgressHUDPieIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.07.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUD-Defines.h"
+#import "JGProgressHUDIndicatorView.h"
+
+/**
+ A pie shaped determinate progress indicator.
+ */
+@interface JGProgressHUDPieIndicatorView : JGProgressHUDIndicatorView
+
+/**
+ Initializes the indicator view and sets the correct color to match the HUD style.
+ */
+- (instancetype __nonnull)initWithHUDStyle:(JGProgressHUDStyle)style __attribute((deprecated(("This initializer is no longer needed. Use the init initializer method."))));
+
+/**
+ Tint color of the Pie.
+ @attention Custom values need to be set after assigning the indicator view to @c JGProgressHUD's @c indicatorView property.
+ 
+ @b Default: White for JGProgressHUDStyleDark, otherwise black.
+ */
+@property (nonatomic, strong, nonnull) UIColor *color;
+
+/**
+ The background fill color inside the pie.
+ @attention Custom values need to be set after assigning the indicator view to @c JGProgressHUD's @c indicatorView property.
+ 
+ @b Default: Dark gray for JGProgressHUDStyleDark, otherwise light gray.
+ */
+@property (nonatomic, strong, nonnull) UIColor *fillColor;
+
+@end

+ 171 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDPieIndicatorView.m

@@ -0,0 +1,171 @@
+//
+//  JGProgressHUDPieIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.07.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDPieIndicatorView.h"
+
+@interface JGProgressHUDPieIndicatorLayer : CALayer
+
+@property (nonatomic, assign) float progress;
+
+@property (nonatomic, weak) UIColor *color;
+
+@property (nonatomic, weak) UIColor *fillColor;
+
+@end
+
+@implementation JGProgressHUDPieIndicatorLayer
+
+@dynamic progress, color, fillColor;
+
++ (BOOL)needsDisplayForKey:(NSString *)key {
+    return ([key isEqualToString:@"progress"] || [key isEqualToString:@"color"] || [key isEqualToString:@"fillColor"] || [super needsDisplayForKey:key]);
+}
+
+- (id <CAAction>)actionForKey:(NSString *)key {
+    if ([key isEqualToString:@"progress"]) {
+        CABasicAnimation *progressAnimation = [CABasicAnimation animation];
+        progressAnimation.fromValue = [self.presentationLayer valueForKey:key];
+        
+        progressAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+        
+        return progressAnimation;
+    }
+    
+    return [super actionForKey:key];
+}
+
+- (void)drawInContext:(CGContextRef)ctx {
+    UIGraphicsPushContext(ctx);
+    
+    CGRect rect = self.bounds;
+    
+    CGPoint center = CGPointMake(rect.origin.x + (CGFloat)floor(rect.size.width/2.0), rect.origin.y + (CGFloat)floor(rect.size.height/2.0));
+    CGFloat lineWidth = 2.0;
+    CGFloat radius = (CGFloat)floor(MIN(rect.size.width, rect.size.height)/2.0)-lineWidth;
+    
+    UIBezierPath *borderPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0.0 endAngle:2.0*(CGFloat)M_PI clockwise:NO];
+    
+    [borderPath setLineWidth:lineWidth];
+    
+    if (self.fillColor) {
+        [self.fillColor setFill];
+        
+        [borderPath fill];
+    }
+    
+    [self.color set];
+    
+    [borderPath stroke];
+    
+    if (self.progress > 0.0) {
+        UIBezierPath *processPath = [UIBezierPath bezierPath];
+        
+        [processPath setLineWidth:radius];
+        
+        CGFloat startAngle = -((CGFloat)M_PI/2.0);
+        CGFloat endAngle = startAngle + 2.0 * (CGFloat)M_PI * self.progress;
+        
+        [processPath addArcWithCenter:center radius:radius/2.0 startAngle:startAngle endAngle:endAngle clockwise:YES];
+        
+        [processPath stroke];
+    }
+    
+    UIGraphicsPopContext();
+}
+
+@end
+
+
+@implementation JGProgressHUDPieIndicatorView
+
+#pragma mark - Initializers
+
+- (instancetype)init {
+    self = [super initWithContentView:nil];
+    
+    if (self) {
+        self.layer.contentsScale = [UIScreen mainScreen].scale;
+        [self.layer setNeedsDisplay];
+        
+        self.color = [UIColor clearColor];
+        self.fillColor = [UIColor clearColor];
+    }
+    
+    return self;
+}
+
+- (instancetype)initWithHUDStyle:(JGProgressHUDStyle)style {
+    return [self init];
+}
+
+- (instancetype)initWithContentView:(UIView *)contentView {
+    return [self init];
+}
+
+#pragma mark - Getters & Setters
+
+- (void)setColor:(UIColor *)tintColor {
+    if ([tintColor isEqual:self.color]) {
+        return;
+    }
+    
+    _color = tintColor;
+    
+    [(JGProgressHUDPieIndicatorLayer *)self.layer setColor:self.color];
+}
+
+- (void)setFillColor:(UIColor *)fillColor {
+    if ([fillColor isEqual:self.fillColor]) {
+        return;
+    }
+    
+    _fillColor = fillColor;
+    
+    [(JGProgressHUDPieIndicatorLayer *)self.layer setFillColor:self.fillColor];
+}
+
+- (void)setProgress:(float)progress animated:(BOOL)animated {
+    if (fequal(self.progress, progress)) {
+        return;
+    }
+    
+    [super setProgress:progress animated:animated];
+    
+    [CATransaction begin];
+    [CATransaction setAnimationDuration:(animated ? 0.3 : 0.0)];
+    
+    [(JGProgressHUDPieIndicatorLayer *)self.layer setProgress:progress];
+    
+    [CATransaction commit];
+}
+
+#pragma mark - Overrides
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {
+    [super setUpForHUDStyle:style vibrancyEnabled:vibrancyEnabled];
+    
+    if (style == JGProgressHUDStyleDark) {
+        self.color = [UIColor colorWithWhite:1.0 alpha:1.0];
+        self.fillColor = [UIColor colorWithWhite:0.2 alpha:1.0];
+    }
+    else {
+        self.color = [UIColor blackColor];
+        if (style == JGProgressHUDStyleLight) {
+            self.fillColor = [UIColor colorWithWhite:0.85 alpha:1.0];
+        }
+        else {
+            self.fillColor = [UIColor colorWithWhite:0.9 alpha:1.0];
+        }
+    }
+}
+
++ (Class)layerClass {
+    return [JGProgressHUDPieIndicatorLayer class];
+}
+
+@end

+ 53 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDRingIndicatorView.h

@@ -0,0 +1,53 @@
+//
+//  JGProgressHUDRingIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUD-Defines.h"
+#import "JGProgressHUDIndicatorView.h"
+
+/**
+ A ring shaped determinate progress indicator.
+ */
+@interface JGProgressHUDRingIndicatorView : JGProgressHUDIndicatorView
+
+/**
+ Initializes the indicator view and sets the correct color to match the HUD style.
+ */
+- (instancetype __nonnull)initWithHUDStyle:(JGProgressHUDStyle)style __attribute((deprecated(("This initializer is no longer needed. Use the init initializer method."))));
+
+/**
+ Background color of the ring.
+ @attention Custom values need to be set after assigning the indicator view to @c JGProgressHUD's @c indicatorView property.
+ 
+ @b Default: Black for JGProgressHUDStyleDark, light gray otherwise.
+ */
+@property (nonatomic, strong, nonnull) UIColor *ringBackgroundColor;
+
+/**
+ Progress color of the progress ring.
+ @attention Custom values need to be set after assigning the indicator view to @c JGProgressHUD's @c indicatorView property.
+ 
+ @b Default: White for JGProgressHUDStyleDark, otherwise black.
+ */
+@property (nonatomic, strong, nonnull) UIColor *ringColor;
+
+/**
+ Sets if the progress ring should have a rounded line cap.
+ 
+ @b Default: NO.
+ */
+@property (nonatomic, assign) BOOL roundProgressLine;
+
+/**
+ Width of the ring.
+ 
+ @b Default: 3.0.
+ */
+@property (nonatomic, assign) CGFloat ringWidth;
+
+@end
+

+ 194 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDRingIndicatorView.m

@@ -0,0 +1,194 @@
+//
+//  JGProgressHUDRingIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 20.7.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//  
+
+#import "JGProgressHUDRingIndicatorView.h"
+
+
+@interface JGProgressHUDRingIndicatorLayer : CALayer
+
+@property (nonatomic, assign) float progress;
+
+@property (nonatomic, weak) UIColor *ringColor;
+@property (nonatomic, weak) UIColor *ringBackgroundColor;
+
+@property (nonatomic, assign) BOOL roundProgressLine;
+
+@property (nonatomic, assign) CGFloat ringWidth;
+
+@end
+
+@implementation JGProgressHUDRingIndicatorLayer
+
+@dynamic progress, ringBackgroundColor, ringColor, ringWidth, roundProgressLine;
+
++ (BOOL)needsDisplayForKey:(NSString *)key {
+    return ([key isEqualToString:@"progress"] || [key isEqualToString:@"ringColor"] || [key isEqualToString:@"ringBackgroundColor"] || [key isEqualToString:@"roundProgressLine"] || [key isEqualToString:@"ringWidth"] || [super needsDisplayForKey:key]);
+}
+
+- (id <CAAction>)actionForKey:(NSString *)key {
+    if ([key isEqualToString:@"progress"]) {
+        CABasicAnimation *progressAnimation = [CABasicAnimation animation];
+        progressAnimation.fromValue = [self.presentationLayer valueForKey:key];
+        
+        progressAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+        
+        return progressAnimation;
+    }
+    
+    return [super actionForKey:key];
+}
+
+- (void)drawInContext:(CGContextRef)ctx {
+    UIGraphicsPushContext(ctx);
+    
+    CGRect rect = self.bounds;
+    
+    CGPoint center = CGPointMake(rect.origin.x + (CGFloat)floor(rect.size.width/2.0), rect.origin.y + (CGFloat)floor(rect.size.height/2.0));
+    CGFloat lineWidth = self.ringWidth;
+    CGFloat radius = (CGFloat)floor(MIN(rect.size.width, rect.size.height)/2.0) - lineWidth;
+    
+    //Background
+    [self.ringBackgroundColor setStroke];
+    
+    UIBezierPath *borderPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0.0 endAngle:2.0*(CGFloat)M_PI clockwise:NO];
+    
+    [borderPath setLineWidth:lineWidth];
+    [borderPath stroke];
+    
+    //Progress
+    [self.ringColor setStroke];
+    
+    if (self.progress > 0.0) {
+        UIBezierPath *processPath = [UIBezierPath bezierPath];
+        
+        [processPath setLineWidth:lineWidth];
+        [processPath setLineCapStyle:(self.roundProgressLine ? kCGLineCapRound : kCGLineCapSquare)];
+        
+        CGFloat startAngle = -((CGFloat)M_PI / 2.0);
+        CGFloat endAngle = startAngle + 2.0 * (CGFloat)M_PI * self.progress;
+        
+        [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
+        
+        [processPath stroke];
+    }
+    
+    UIGraphicsPopContext();
+}
+
+@end
+
+
+@implementation JGProgressHUDRingIndicatorView
+
+#pragma mark - Initializers
+
+- (instancetype)init {
+    self = [super initWithContentView:nil];;
+    
+    if (self) {
+        self.layer.contentsScale = [UIScreen mainScreen].scale;
+        [self.layer setNeedsDisplay];
+        
+        self.ringWidth = 3.0;
+        self.ringColor = [UIColor clearColor];
+        self.ringBackgroundColor = [UIColor clearColor];
+    }
+    
+    return self;
+}
+
+- (instancetype)initWithHUDStyle:(JGProgressHUDStyle)style {
+    return [self init];
+}
+
+- (instancetype)initWithContentView:(UIView *)contentView {
+    return [self init];
+}
+
+#pragma mark - Getters & Setters
+
+- (void)setRoundProgressLine:(BOOL)roundProgressLine {
+    if (roundProgressLine == self.roundProgressLine) {
+        return;
+    }
+    
+    _roundProgressLine = roundProgressLine;
+    
+    [(JGProgressHUDRingIndicatorLayer *)self.layer setRoundProgressLine:self.roundProgressLine];
+}
+
+- (void)setRingColor:(UIColor *)tintColor {
+    if ([tintColor isEqual:self.ringColor]) {
+        return;
+    }
+    
+    _ringColor = tintColor;
+    
+    [(JGProgressHUDRingIndicatorLayer *)self.layer setRingColor:self.ringColor];
+}
+
+- (void)setRingBackgroundColor:(UIColor *)backgroundTintColor {
+    if ([backgroundTintColor isEqual:self.ringBackgroundColor]) {
+        return;
+    }
+    
+    _ringBackgroundColor = backgroundTintColor;
+    
+    [(JGProgressHUDRingIndicatorLayer *)self.layer setRingBackgroundColor:self.ringBackgroundColor];
+}
+
+- (void)setRingWidth:(CGFloat)ringWidth {
+    if (fequal(ringWidth, self.ringWidth)) {
+        return;
+    }
+    
+    _ringWidth = ringWidth;
+    
+    [(JGProgressHUDRingIndicatorLayer *)self.layer setRingWidth:self.ringWidth];
+}
+
+- (void)setProgress:(float)progress animated:(BOOL)animated {
+    if (fequal(self.progress, progress)) {
+        return;
+    }
+    
+    [super setProgress:progress animated:animated];
+    
+    [CATransaction begin];
+    [CATransaction setAnimationDuration:(animated ? 0.3 : 0.0)];
+    
+    [(JGProgressHUDRingIndicatorLayer *)self.layer setProgress:self.progress];
+    
+    [CATransaction commit];
+}
+
+#pragma mark - Overrides
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {
+    [super setUpForHUDStyle:style vibrancyEnabled:vibrancyEnabled];
+    
+    if (style == JGProgressHUDStyleDark) {
+        self.ringColor = [UIColor colorWithWhite:1.0 alpha:1.0];
+        self.ringBackgroundColor = [UIColor colorWithWhite:0.0 alpha:1.0];
+    }
+    else {
+        self.ringColor = [UIColor blackColor];
+        if (style == JGProgressHUDStyleLight) {
+            self.ringBackgroundColor = [UIColor colorWithWhite:0.85 alpha:1.0];
+        }
+        else {
+            self.ringBackgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
+        }
+    }
+}
+
++ (Class)layerClass {
+    return [JGProgressHUDRingIndicatorLayer class];
+}
+
+@end

+ 37 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDShadow.h

@@ -0,0 +1,37 @@
+//
+//  JGProgressHUDShadow.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 25.09.17.
+//  Copyright © 2017 Jonas Gessner. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+/**
+ A wrapper representing properties of a shadow.
+ */
+@interface JGProgressHUDShadow : NSObject
+
+- (instancetype __nonnull)initWithColor:(UIColor *__nonnull)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(float)opacity;
+
+/** Convenience initializer. */
++ (instancetype __nonnull)shadowWithColor:(UIColor *__nonnull)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(float)opacity;
+
+/**
+ The color of the shadow. Colors created from patterns are currently NOT supported.
+ */
+@property (nonatomic, strong, readonly, nonnull) UIColor *color;
+
+/** The shadow offset. */
+@property (nonatomic, assign, readonly) CGSize offset;
+
+/** The blur radius used to create the shadow. */
+@property (nonatomic, assign, readonly) CGFloat radius;
+
+/**
+ The opacity of the shadow. Specifying a value outside the  [0,1] range will give undefined results.
+ */
+@property (nonatomic, assign, readonly) float opacity;
+
+@end

+ 30 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDShadow.m

@@ -0,0 +1,30 @@
+//
+//  JGProgressHUDShadow.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 25.09.17.
+//  Copyright © 2017 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDShadow.h"
+
+@implementation JGProgressHUDShadow
+
++ (instancetype)shadowWithColor:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(float)opacity {
+    return [[self alloc] initWithColor:color offset:offset radius:radius opacity:opacity];
+}
+
+- (instancetype)initWithColor:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius opacity:(float)opacity {
+    self = [super init];
+    
+    if (self) {
+        _color = color;
+        _offset = offset;
+        _radius = radius;
+        _opacity = opacity;
+    }
+    
+    return self;
+}
+
+@end

+ 21 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDSuccessIndicatorView.h

@@ -0,0 +1,21 @@
+//
+//  JGProgressHUDSuccessIndicatorView.h
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.08.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDImageIndicatorView.h"
+
+/**
+ An image indicator showing a checkmark, representing a failed operation.
+ */
+@interface JGProgressHUDSuccessIndicatorView : JGProgressHUDImageIndicatorView
+
+/**
+ Default initializer for this class.
+ */
+- (instancetype __nonnull)init;
+
+@end

+ 52 - 0
Pods/JGProgressHUD/JGProgressHUD/JGProgressHUD/JGProgressHUDSuccessIndicatorView.m

@@ -0,0 +1,52 @@
+//
+//  JGProgressHUDSuccessIndicatorView.m
+//  JGProgressHUD
+//
+//  Created by Jonas Gessner on 19.08.14.
+//  Copyright (c) 2014 Jonas Gessner. All rights reserved.
+//
+
+#import "JGProgressHUDSuccessIndicatorView.h"
+#import "JGProgressHUD.h"
+
+@implementation JGProgressHUDSuccessIndicatorView
+
+- (instancetype)initWithContentView:(UIView *__unused)contentView {
+    NSBundle *currentBundle = [NSBundle bundleForClass:[self class]];
+    NSURL *resourceBundleURL = [currentBundle URLForResource:@"JGProgressHUD" withExtension:@"bundle"];
+    NSBundle *resourceBundle = currentBundle;
+    if (resourceBundleURL) {
+        resourceBundle = [NSBundle bundleWithURL:resourceBundleURL] ?: currentBundle;
+    }
+
+    NSString *imgPath = [resourceBundle pathForResource:@"jg_hud_success" ofType:@"png"];
+    self = [super initWithImage:[[UIImage imageWithContentsOfFile:imgPath] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
+
+    return self;
+}
+
+- (instancetype)init {
+    return [self initWithContentView:nil];
+}
+
+- (void)updateAccessibility {
+    self.accessibilityLabel = NSLocalizedString(@"Success",);
+}
+
+- (void)setUpForHUDStyle:(JGProgressHUDStyle)style vibrancyEnabled:(BOOL)vibrancyEnabled {
+    [super setUpForHUDStyle:style vibrancyEnabled:vibrancyEnabled];
+
+    if (style == JGProgressHUDStyleDark) {
+        self.contentView.tintColor = [UIColor whiteColor];
+    }
+    else {
+        self.contentView.tintColor = [UIColor blackColor];
+    }
+}
+
+- (void)tintColorDidChange {
+    [super tintColorDidChange];
+    self.contentView.tintColor = self.tintColor;
+}
+
+@end

BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error.png


BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error@2x.png


BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_error@3x.png


BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success.png


BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success@2x.png


BIN
Pods/JGProgressHUD/JGProgressHUD/Resources/jg_hud_success@3x.png


+ 20 - 0
Pods/JGProgressHUD/LICENSE.txt

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2018 Jonas Gessner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 174 - 0
Pods/JGProgressHUD/README.md

@@ -0,0 +1,174 @@
+JGProgressHUD
+---------------
+
+An elegant and simple progress HUD for iOS and tvOS.<br/>
+<p align="center">
+<img src="Examples/Screenshots/Presentation.png" style='height: 100%; width: 100%; object-fit: contain'/>
+</p>
+
+Overview
+---------------
+
+__The ultimate progress HUD for iOS and tvOS is here: JGProgressHUD!__
+
+- Plug and play: Simple integration, easy to use, few lines of code required.
+- Easily customizable (custom animations, indicator views and more).
+- Uses `UIVisualEffectView` and `UIMotionEffect` for a native look.
+- Uses AutoLayout to provide a fully dynamic layout.
+- Detects and repositions when keyboard appears/disappears.
+- Well documented and maintained.
+- Voice Over/`UIAccessibility` support.
+- Backward compatibility to iOS 8.
+- Can be used with Swift and Objective C.
+- And most importantly, it looks good!
+
+Download the source code and open the <a href="Examples">Examples</a> project to try JGProgressHUD and see all of its features in action!
+
+[![GitHub release](https://img.shields.io/github/release/JonasGessner/JGProgressHUD.svg)](https://github.com/JonasGessner/JGProgressHUD/releases)
+[![GitHub license](https://img.shields.io/github/license/JonasGessner/JGProgressHUD.svg)](https://github.com/JonasGessner/JGProgressHUD/blob/master/LICENSE.txt)
+[![CocoaPods](https://img.shields.io/cocoapods/v/JGProgressHUD.svg)](https://cocoapods.org/pods/JGProgressHUD)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+
+Examples
+--------------
+
+<p align="center">
+<img src="Examples/Screenshots/demo1.gif" width="250"/>
+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
+<img src="Examples/Screenshots/demo2.gif" width="250"/>
+</p>
+
+#### Showing indeterminate progress:
+
+Objective C:
+
+```objc
+JGProgressHUD *HUD = [JGProgressHUD progressHUDWithStyle:JGProgressHUDStyleDark];
+HUD.textLabel.text = @"Loading";
+[HUD showInView:self.view];
+[HUD dismissAfterDelay:3.0];
+```
+
+Swift:
+
+```swift
+let hud = JGProgressHUD(style: .dark)
+hud.textLabel.text = "Loading"
+hud.show(in: self.view)
+hud.dismiss(afterDelay: 3.0)
+```
+
+This displays a dark HUD with an activity indicator and the title "Loading". The HUD is presented with a fade animation and is dismissed after 3 seconds with a fade animation.
+
+#### Showing an error message:
+
+```objc
+JGProgressHUD *HUD = [JGProgressHUD progressHUDWithStyle:JGProgressHUDStyleDark];
+HUD.textLabel.text = @"Error";
+HUD.indicatorView = [[JGProgressHUDErrorIndicatorView alloc] init]; //JGProgressHUDSuccessIndicatorView is also available
+[HUD showInView:self.view];
+[HUD dismissAfterDelay:3.0];
+```
+
+#### Showing determinate progress:
+
+```objc
+JGProgressHUD *HUD = [JGProgressHUD progressHUDWithStyle:JGProgressHUDStyleDark];
+HUD.indicatorView = [[JGProgressHUDPieIndicatorView alloc] init]; //Or JGProgressHUDRingIndicatorView
+HUD.progress = 0.5f;
+[HUD showInView:self.view];
+[HUD dismissAfterDelay:3.0];
+```
+
+__Important:__ You should always show `JGProgressHUD` in a `UIViewController` view.
+
+Download the source code and open the <a href="Examples">Examples</a> project to explore more use cases for JGProgressHUD.
+
+Customization
+-----------
+### Styles:
+JGProgressHUD can be displayed in 3 styles:
+
+- __Extra Light__
+- __Light__
+- __Dark__
+
+### Indicator Views:
+By default a HUD will display an indeterminate progress indicator. The indicator view can be completely hidden by setting the `indicatorView` property to `nil`. These indicator views are available by default:
+
+- __Indeterminate progress indicator__
+- __Pie progress indicator__
+- __Ring progress indicator__
+- __Success indicator__
+- __Error indicator__
+- __Image indicator__
+
+Custom indicator views can be created by subclassing `JGProgressHUDIndicatorView`.<br/>
+
+
+### Animations:
+By default a HUD will use a fade animation. Several parameters can be altered such as animation duration or animation curve. A HUD can be displayed without animation and different animations can be used. These animations are available by default:
+
+- __Fade__
+- __Zoom and Fade__
+
+Custom animations can be created by subclassing `JGProgressHUDAnimation`.
+
+To dim the content behind the HUD set your dim color as `backgroundColor` of your `JGProgressHUD` instance.
+
+Installation
+--------------
+### CocoaPods:
+In your `Podfile` add:
+
+```
+pod 'JGProgressHUD'
+```
+
+### Carthage:
+In your `Cartfile` add:
+
+```
+github "JonasGessner/JGProgressHUD"
+```
+
+### Manual Installation (Framework):
+
+1. Drag the `JGProgressHUD.xcodeproj` file into your Xcode project.
+2. Add `JGProgressHUD.framework` to "Embedded Binaries" in the "General" tab of your target.
+
+Then import the module where you want to use it:
+
+```objc
+@import JGProgressHUD;
+```
+
+Swift:
+
+```swift
+import JGProgressHUD
+```
+
+See the <a href="Examples">Examples</a> project for an example implementation of JGProgressHUD as framework.
+
+Requirements
+------------
+
+- Base SDK of iOS/tvOS 11.0 or higher.
+- Deployment target of iOS 8.0, tvOS 9.0 or higher.
+
+JGProgressHUD can also be used by projects written in Swift. See <a href="https://github.com/JonasGessner/JGProgressHUD#installation">Installation</a> for details.
+
+Documentation
+----------------
+Detailed documentation can be found on <a href="http://jonasgessner.github.io/JGProgressHUD/">here</a>.<br/><br/>
+Each class and method is well documented, making it easy to quickly get a good overview. To start, see <a href="JGProgressHUD/JGProgressHUD/JGProgressHUD.h">JGProgressHUD.h</a>.
+
+License
+---------
+MIT License.<br/>
+© 2014-2018, Jonas Gessner.
+
+Credits
+----------
+Created and maintained by Jonas Gessner, © 2014-2018.<br/>

+ 13 - 1
Pods/Manifest.lock

@@ -1,24 +1,36 @@
 PODS:
+  - JGProgressHUD (2.0.3)
   - MessageInputBar/Core (0.4.1)
   - MessageKit (2.0.0):
     - MessageInputBar/Core
   - openssl-ios-bitcode (1.0.210)
+  - QuickTableViewController (1.0.0)
+  - ReachabilitySwift (4.3.0)
 
 DEPENDENCIES:
+  - JGProgressHUD
   - MessageKit (= 2.0.0)
   - openssl-ios-bitcode (= 1.0.210)
+  - QuickTableViewController
+  - ReachabilitySwift
 
 SPEC REPOS:
   https://github.com/cocoapods/specs.git:
+    - JGProgressHUD
     - MessageInputBar
     - MessageKit
     - openssl-ios-bitcode
+    - QuickTableViewController
+    - ReachabilitySwift
 
 SPEC CHECKSUMS:
+  JGProgressHUD: 12b20a8f4ffe05258f8635c1ab92816e451f904d
   MessageInputBar: e81c7535347f1f7b923de7080409a535a004b6e4
   MessageKit: 29c1c87e5a396d2ca7a3f712e5171dc9aba42a1e
   openssl-ios-bitcode: c833701a4488bd43de0051db41cfa75f6fef8109
+  QuickTableViewController: a49fb6dc5623b9dbe301a03ac5c563099e234fbc
+  ReachabilitySwift: 408477d1b6ed9779dba301953171e017c31241f3
 
-PODFILE CHECKSUM: 738743efb43b4f6b89816b656cd3f132c9e93608
+PODFILE CHECKSUM: 4cc25fb37ef9036decc6e11a25a0ea70c96e2149
 
 COCOAPODS: 1.5.3

File diff suppressed because it is too large
+ 738 - 434
Pods/Pods.xcodeproj/project.pbxproj


+ 21 - 0
Pods/QuickTableViewController/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 bcylin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 275 - 0
Pods/QuickTableViewController/README.md

@@ -0,0 +1,275 @@
+# QuickTableViewController
+
+[![Build Status](https://travis-ci.org/bcylin/QuickTableViewController.svg?branch=master)](https://travis-ci.org/bcylin/QuickTableViewController)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/QuickTableViewController.svg)](https://cocoapods.org/pods/QuickTableViewController)
+![Platform](https://img.shields.io/cocoapods/p/QuickTableViewController.svg)
+[![codecov](https://codecov.io/gh/bcylin/QuickTableViewController/branch/master/graph/badge.svg)](https://codecov.io/gh/bcylin/QuickTableViewController)
+![Swift 4.1](https://img.shields.io/badge/Swift-4.1-orange.svg)
+
+A simple way to create a table view for settings, including:
+
+* Table view cells with `UISwitch`
+* Table view cells with center aligned text for tap actions
+* A section that provides mutually exclusive options
+* Actions performed when the row reacts to the user interaction
+* Customizable table view cell image, cell style and accessory type
+
+<img src="https://bcylin.github.io/QuickTableViewController/img/screenshots.png" width="80%"></img>
+
+## Usage
+
+Set up `tableContents` in `viewDidLoad`:
+
+```swift
+import QuickTableViewController
+
+class ViewController: QuickTableViewController {
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+
+    tableContents = [
+      Section(title: "Switch", rows: [
+        SwitchRow(title: "Setting 1", switchValue: true, action: { _ in }),
+        SwitchRow(title: "Setting 2", switchValue: false, action: { _ in }),
+      ]),
+
+      Section(title: "Tap Action", rows: [
+        TapActionRow(title: "Tap action", action: { [weak self] in self?.showAlert($0) })
+      ]),
+
+      Section(title: "Navigation", rows: [
+        NavigationRow(title: "CellStyle.default", subtitle: .none, icon: .named("gear")),
+        NavigationRow(title: "CellStyle", subtitle: .belowTitle(".subtitle"), icon: .named("globe")),
+        NavigationRow(title: "CellStyle", subtitle: .rightAligned(".value1"), icon: .named("time"), action: { _ in }),
+        NavigationRow(title: "CellStyle", subtitle: .leftAligned(".value2"))
+      ]),
+
+      RadioSection(title: "Radio Buttons", options: [
+        OptionRow(title: "Option 1", isSelected: true, action: didToggleOption()),
+        OptionRow(title: "Option 2", isSelected: false, action: didToggleOption()),
+        OptionRow(title: "Option 3", isSelected: false, action: didToggleOption())
+      ], footer: "See RadioSection for more details.")
+    ]
+  }
+
+  // MARK: - Actions
+
+  private func showAlert(_ sender: Row) {
+    // ...
+  }
+
+  private func didToggleOption() -> (Row) -> Void {
+    return { [weak self] row in
+      // ...
+    }
+  }
+
+}
+```
+
+### NavigationRow
+
+#### Subtitle Styles
+
+```swift
+NavigationRow(title: "UITableViewCellStyle.default", subtitle: .none)
+NavigationRow(title: "UITableViewCellStyle", subtitle: .belowTitle(".subtitle")
+NavigationRow(title: "UITableViewCellStyle", subtitle: .rightAligned(".value1")
+NavigationRow(title: "UITableViewCellStyle", subtitle: .leftAligned(".value2"))
+```
+
+#### Disclosure Indicator
+
+* A `NavigationRow` with an `action` will be displayed in a table view cell with `.disclosureIndicator`.
+* The `action` will be invoked when the table view cell is selected.
+
+#### Images
+
+```swift
+enum Icon {
+  case named(String)
+  case image(UIImage)
+  case images(normal: UIImage, highlighted: UIImage)
+}
+```
+
+* Images in table view cells can be set by specifying the `icon` of each row.
+* Table view cells in `UITableViewCellStyle.value2` will not show the image view.
+
+### SwitchRow
+
+* A `SwitchRow` is representing a table view cell with a `UISwitch` as its `accessoryView`.
+* The `action` will be invoked when the switch value changes.
+* The subtitle is disabled in `SwitchRow `.
+
+### TapActionRow
+
+* A `TapActionRow` is representing a button-like table view cell.
+* The `action` will be invoked when the table view cell is selected.
+* The icon and subtitle are disabled in `TapActionRow`.
+
+### OptionRow
+
+* An `OptionRow` is representing a table view cell with `.checkmark`.
+* The subtitle is disabled in `OptionRow`.
+* The `action` will be invoked when the selected state is toggled.
+
+```swift
+let didToggleOption: (Row) -> Void = { [weak self] in
+  if let option = $0 as? OptionRowCompatible, option.isSelected {
+    // to exclude the option that's toggled off
+  }
+}
+```
+
+### RadioSection
+
+* `OptionRow` can be used with or without `RadioSection`, which allows only one selected option.
+* Setting `alwaysSelectsOneOption` to true will keep one of the options selected.
+
+## Customization
+
+### Rows
+
+All rows must conform to [`Row`](https://github.com/bcylin/QuickTableViewController/blob/develop/Source/Protocol/Row.swift) and [`RowStyle`](https://github.com/bcylin/QuickTableViewController/blob/develop/Source/Protocol/RowStyle.swift). Additional interface to work with specific types of rows are represented as different [protocols](https://github.com/bcylin/QuickTableViewController/blob/develop/Source/Protocol/RowCompatible.swift):
+
+* `NavigationRowCompatible`
+* `OptionRowCompatible`
+* `SwitchRowCompatible`
+* `TapActionRowCompatible`
+
+### Cell Classes
+
+A customized table view cell type can be specified to rows during initialization.
+
+```swift
+// Default is UITableViewCell.
+NavigationRow<CustomCell>(title: "Navigation", subtitle: .none)
+
+// Default is SwitchCell.
+SwitchRow<CustomSwitchCell>(title: "Switch", switchValue: true, action: { _ in })
+
+// Default is TapActionCell.
+TapActionRow<CustomTapActionCell>(title: "Tap", action: { _ in })
+
+// Default is UITableViewCell.
+OptionRow<CustomOptionCell>(title: "Option", isSelected: true, action: { _ in })
+```
+
+Since the rows carry different cell types, they can be matched using either the concrete types or the related protocol:
+
+```swift
+let action: (Row) -> Void = {
+  switch $0 {
+  case let option as OptionRow<CustomOptionCell>:
+    // only matches the option rows with a specific cell type
+  case let option as OptionRowCompatible:
+    // matches all option rows
+  default:
+    break
+  }
+}
+```
+
+### Overwrite Default Configuration
+
+You can use `register(_:forCellReuseIdentifier:)` to specify custom cell types for the [table view](https://github.com/bcylin/QuickTableViewController/blob/develop/Source/QuickTableViewController.swift#L100-L102) to use. See [CustomizationViewController](https://github.com/bcylin/QuickTableViewController/blob/develop/Example-iOS/ViewControllers/CustomizationViewController.swift) for the cell reuse identifiers of different rows.
+
+Table view cell classes that conform to `Configurable` can take the customization during `tableView(_:cellForRowAt:)`:
+
+```swift
+protocol Configurable {
+  func configure(with row: Row & RowStyle)
+}
+```
+
+Additional setups can also be added to each row using the `customize` closure:
+
+```swift
+protocol RowStyle {
+  var customize: ((UITableViewCell, Row & RowStyle) -> Void)? { get }
+}
+```
+
+The `customize` closure overwrites the `Configurable` setup.
+
+### UIAppearance
+
+As discussed in issue [#12](https://github.com/bcylin/QuickTableViewController/issues/12), UIAppearance customization works when the cell is dequeued from the storyboard. One way to work around this is to register nib objects to the table view. Check out [AppearanceViewController](https://github.com/bcylin/QuickTableViewController/blob/develop/Example-iOS/ViewControllers/AppearanceViewController.swift) for the setup.
+
+## tvOS Differences
+
+* `UISwitch` is replaced by a checkmark in `SwitchCell`.
+* `TapActionCell` does not use center aligned text.
+* Cell image view's left margin is 0.
+
+## Limitation
+
+> When to use **QuickTableViewController**?
+
+QuickTableViewController is good for presenting static table contents, where the sections and rows don't change dynamically after `viewDidLoad`.
+
+It's possible to update the table contents by replacing a specific section or row. Using different styles on each row requires additional configuration as described in the [Customization](#customization) section.
+
+> When **not** to use it?
+
+QuickTableViewController is not designed for inserting and deleting rows. It doesn't handle table view reload animation either. If your table view needs to update dynamically, you might want to consider other solutions such as [IGListKit](https://github.com/Instagram/IGListKit).
+
+## Documentation
+
+* [QuickTableViewController Reference](https://bcylin.github.io/QuickTableViewController)
+* [Example Project](https://github.com/bcylin/QuickTableViewController/tree/develop/Example)
+
+## Requirements
+
+QuickTableViewController | iOS  | tvOS | Xcode | Swift
+------------------------ | :--: | :--: | :---: | :---:
+`~> 0.1.0`               | 8.0+ | -    | 6.4   | 1.2
+`~> 0.2.0`               | 8.0+ | -    | 7.0   | 2.0
+`~> 0.3.0`               | 8.0+ | -    | 7.3   | 2.2
+`~> 0.4.0`               | 8.0+ | -    | 8.0   | 2.3
+`~> 0.5.0`               | 8.0+ | -    | 8.0   | 3.0
+`~> 0.6.0`               | 8.0+ | -    | 8.3   | 3.1
+`~> 0.7.0`               | 8.0+ | -    | 9.0   | 3.2
+`~> 0.8.0`               | 8.0+ | -    | 9.1   | 4.0
+`~> 0.9.0`               | 8.0+ | -    | 9.3   | 4.1
+`~> 1.0.0`               | 8.0+ | 9.0+ | 9.4   | 4.1
+
+## Installation
+
+### Use [CocoaPods](http://guides.cocoapods.org/)
+
+Create a `Podfile` with the following specification and run `pod install`.
+
+```rb
+platform :ios, '8.0'
+use_frameworks!
+
+pod 'QuickTableViewController'
+```
+
+### Use [Carthage](https://github.com/Carthage/Carthage)
+
+Create a `Cartfile` with the following specification and run `carthage update QuickTableViewController`.
+Follow the [instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) to add the framework to your project.
+
+```
+github "bcylin/QuickTableViewController"
+```
+
+### Use Git Submodule
+
+```
+git submodule add -b master git@github.com:bcylin/QuickTableViewController.git Dependencies/QuickTableViewController
+```
+
+* Drag **QuickTableViewController.xcodeproj** to your app project as a subproject.
+* On your application target's **Build Phases** settings tab, add **QuickTableViewController-iOS** to **Target Dependencies**.
+
+## License
+
+QuickTableViewController is released under the MIT license.
+See [LICENSE](https://github.com/bcylin/QuickTableViewController/blob/master/LICENSE) for more details.
+Image source: [iconmonstr](http://iconmonstr.com/license/).

+ 78 - 0
Pods/QuickTableViewController/Source/Model/Icon.swift

@@ -0,0 +1,78 @@
+//
+//  Icon.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A struct that represents the image used in a row.
+public enum Icon: Equatable {
+
+  /// Icon with an image of the given name for the normal state.
+  /// The "-highlighted" suffix is appended to the name for the highlighted image.
+  case named(String)
+  /// Icon with an image for the normal state.
+  case image(UIImage)
+  /// Icon with images for the normal and highlighted states.
+  case images(normal: UIImage, highlighted: UIImage)
+
+  /// The image for the normal state.
+  public var image: UIImage? {
+    switch self {
+    case let .named(name):
+      return UIImage(named: name)
+    case let .image(image):
+      return image
+    case let .images(normal: image, highlighted: _):
+      return image
+    }
+  }
+
+  /// The image for the highlighted state.
+  public var highlightedImage: UIImage? {
+    switch self {
+    case let .named(name):
+      return UIImage(named: name + "-highlighted")
+    case .image:
+      return nil
+    case let .images(normal: _, highlighted: image):
+      return image
+    }
+  }
+
+  // MARK: Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal images and highlighted images.
+  public static func == (lhs: Icon, rhs: Icon) -> Bool {
+    switch (lhs, rhs) {
+    case let (.named(left), .named(right)):
+      return left == right
+    default:
+      return
+        lhs.image == rhs.image &&
+        lhs.highlightedImage == rhs.highlightedImage
+    }
+  }
+
+}

+ 120 - 0
Pods/QuickTableViewController/Source/Model/RadioSection.swift

@@ -0,0 +1,120 @@
+//
+//  RadioSection.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 17/08/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import Foundation
+
+/// A section that allows only one option selected in a table view.
+open class RadioSection: Section {
+
+  // MARK: - Initializer
+
+  /// Initializes a section with a title, containing rows and an optional footer.
+  public init(title: String?, options: [OptionRowCompatible], footer: String? = nil) {
+    self.options = options
+    super.init(title: title, rows: [], footer: footer)
+  }
+
+  private override init(title: String?, rows: [Row & RowStyle], footer: String? = nil) {
+    fatalError("init with title, rows, and footer is not supported")
+  }
+
+  // MARK: - Section
+
+  /// The array of rows in the section.
+  open override var rows: [Row & RowStyle] {
+    get {
+      return options
+    }
+    set {
+      options = newValue as? [OptionRowCompatible] ?? options
+    }
+  }
+
+  // MARK: - RadioSection
+
+  /// A boolean that indicates whether there's always one option selected.
+  open var alwaysSelectsOneOption: Bool = false {
+    didSet {
+      if alwaysSelectsOneOption && selectedOption == nil {
+        options.first?.isSelected = true
+      }
+    }
+  }
+
+  /// The array of options in the section. It's identical to the `rows`.
+  open private(set) var options: [OptionRowCompatible]
+
+  /// Returns the selected index, or nil when nothing is selected.
+  open var indexOfSelectedOption: Int? {
+    return options.index { $0.isSelected }
+  }
+
+  /// Returns the selected option row, or nil when nothing is selected.
+  open var selectedOption: OptionRowCompatible? {
+    if let index = indexOfSelectedOption {
+      return options[index]
+    } else {
+      return nil
+    }
+  }
+
+  /// Toggle the selection of the given option and keep options mutually exclusive.
+  /// If `alwaysSelectOneOption` is set to true, it will not deselect the current selection.
+  ///
+  /// - Parameter option: The option to flip the `isSelected` state.
+  /// - Returns: The indexes of changed options.
+  open func toggle(_ option: OptionRowCompatible) -> IndexSet {
+    if option.isSelected && alwaysSelectsOneOption {
+      return []
+    }
+
+    defer {
+      option.isSelected = !option.isSelected
+    }
+
+    if option.isSelected {
+      // Deselect the selected option.
+      return options.index(where: { $0 === option }).map { [$0] } ?? []
+    }
+
+    var toggledIndexes: IndexSet = []
+
+    for (index, element) in options.enumerated() {
+      switch element {
+      case let target where target === option:
+        toggledIndexes.insert(index)
+      case _ where element.isSelected:
+        toggledIndexes.insert(index)
+        element.isSelected = false
+      default:
+        break
+      }
+    }
+
+    return toggledIndexes
+  }
+
+}

+ 52 - 0
Pods/QuickTableViewController/Source/Model/Section.swift

@@ -0,0 +1,52 @@
+//
+//  Section.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import Foundation
+
+/// A class that represents a section in a table view.
+open class Section {
+
+  // MARK: - Initializer
+
+  /// Initializes a section with a nullable title, containing rows and an optional footer.
+  public init(title: String?, rows: [Row & RowStyle], footer: String? = nil) {
+    self.title = title
+    self.rows = rows
+    self.footer = footer
+  }
+
+  // MARK: - Properties
+
+  /// The text of the section title.
+  open let title: String?
+
+  /// The array of rows in the section.
+  open var rows: [Row & RowStyle]
+
+  /// The text of the section footer.
+  open var footer: String?
+
+}

+ 93 - 0
Pods/QuickTableViewController/Source/Model/Subtitle.swift

@@ -0,0 +1,93 @@
+//
+//  Subtitle.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// An enum that represents a subtitle text with `UITableViewCellStyle`.
+public enum Subtitle: Equatable {
+
+  /// Does not show a subtitle as `UITableViewCellStyle.default`.
+  case none
+  /// Shows the associated text in `UITableViewCellStyle.subtitle`.
+  case belowTitle(String)
+  /// Shows the associated text in `UITableViewCellStyle.value1`.
+  case rightAligned(String)
+  /// Shows the associated text in `UITableViewCellStyle.value2`.
+  case leftAligned(String)
+
+  /// Returns the corresponding table view cell style.
+  public var style: UITableViewCellStyle {
+    switch self {
+    case .none:          return .default
+    case .belowTitle:    return .subtitle
+    case .rightAligned:  return .value1
+    case .leftAligned:   return .value2
+    }
+  }
+
+  /// Returns the associated text of the case.
+  public var text: String? {
+    switch self {
+    case let .belowTitle(text):   return text
+    case let .rightAligned(text): return text
+    case let .leftAligned(text):  return text
+    default:                      return nil
+    }
+  }
+
+  // MARK: Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal texts in the same `Subtitle`.
+  public static func == (lhs: Subtitle, rhs: Subtitle) -> Bool {
+    switch (lhs, rhs) {
+    case (.none, .none):
+      return true
+    case let (.belowTitle(a), .belowTitle(b)):
+      return a == b
+    case let (.rightAligned(a), .rightAligned(b)):
+      return a == b
+    case let (.leftAligned(a), .leftAligned(b)):
+      return a == b
+    default:
+      return false
+    }
+  }
+
+}
+
+
+internal extension UITableViewCellStyle {
+
+  var stringValue: String {
+    switch self {
+    case .default:  return ".default"
+    case .subtitle: return ".subtitle"
+    case .value1:   return ".value1"
+    case .value2:   return ".value2"
+    }
+  }
+
+}

+ 50 - 0
Pods/QuickTableViewController/Source/Protocol/Configurable.swift

@@ -0,0 +1,50 @@
+//
+//  Configurable.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 30/07/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// Any type that conforms to this protocol is able to take `Row & RowStyle` as the configuration.
+public protocol Configurable {
+  /// Configure the receiver with an instance that conforms to `Row & RowStyle`.
+  func configure(with row: Row & RowStyle)
+}
+
+
+extension UITableViewCell {
+
+  internal func defaultSetUp(with row: Row & RowStyle) {
+    textLabel?.text = row.title
+    detailTextLabel?.text = row.subtitle?.text
+
+    // Reset the accessory view in case the cell is reused.
+    accessoryView = nil
+    accessoryType = row.accessoryType
+
+    imageView?.image = row.icon?.image
+    imageView?.highlightedImage = row.icon?.highlightedImage
+  }
+
+}

+ 77 - 0
Pods/QuickTableViewController/Source/Protocol/Reusable.swift

@@ -0,0 +1,77 @@
+//
+//  Reusable.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 21/08/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+extension UITableViewCell: Reusable {}
+
+
+internal protocol Reusable {
+  static var reuseIdentifier: String { get }
+}
+
+
+internal extension Reusable {
+
+  internal static var reuseIdentifier: String {
+    let type = String(describing: self)
+    return type.matches(of: String.typeDescriptionPattern).last ?? type
+  }
+
+}
+
+
+internal extension String {
+
+  internal static var typeDescriptionPattern: String {
+    // For the types in the format of "(CustomCell in _B5334F301B8CC6AA00C64A6D)"
+    return "^\\(([\\w\\d]+)\\sin\\s_[0-9A-F]+\\)$"
+  }
+
+  internal func matches(of pattern: String) -> [String] {
+    let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
+    #if swift(>=3.2)
+      let fullText = NSRange(location: 0, length: count)
+    #else
+      let fullText = NSRange(location: 0, length: characters.count)
+    #endif
+
+    guard let matches = regex?.matches(in: self, options: [], range: fullText) else {
+      return []
+    }
+
+    return matches.reduce([]) { accumulator, match in
+      accumulator + (0..<match.numberOfRanges).map {
+        #if swift(>=4)
+          return (self as NSString).substring(with: match.range(at: $0))
+        #else
+          return (self as NSString).substring(with: match.rangeAt($0))
+        #endif
+      }
+    }
+  }
+
+}

+ 41 - 0
Pods/QuickTableViewController/Source/Protocol/Row.swift

@@ -0,0 +1,41 @@
+//
+//  Row.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import Foundation
+
+/// Any type that conforms to this protocol is capable of representing a row in a table view.
+public protocol Row: class {
+
+  /// The title text of the row.
+  var title: String { get }
+
+  /// The subtitle text of the row.
+  var subtitle: Subtitle? { get }
+
+  /// A closure related to the action of the row.
+  var action: ((Row) -> Void)? { get }
+
+}

+ 48 - 0
Pods/QuickTableViewController/Source/Protocol/RowCompatible.swift

@@ -0,0 +1,48 @@
+//
+//  RowCompatible.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 10/12/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import Foundation
+
+/// This protocol defines the compatible interface of a `NavigationRow` regardless of its associated cell type.
+public protocol NavigationRowCompatible: Row, RowStyle {}
+
+
+/// This protocol defines the compatible interface of a `TapActionRow` regardless of its associated cell type.
+public protocol TapActionRowCompatible: Row, RowStyle {}
+
+
+/// This protocol defines the compatible interface of an `OptionRow` regardless of its associated cell type.
+public protocol OptionRowCompatible: Row, RowStyle {
+  /// The state of selection.
+  var isSelected: Bool { get set }
+}
+
+
+/// This protocol defines the compatible interface of a `SwitchRow` regardless of its associated cell type.
+public protocol SwitchRowCompatible: Row, RowStyle {
+  /// The state of the switch.
+  var switchValue: Bool { get set }
+}

+ 53 - 0
Pods/QuickTableViewController/Source/Protocol/RowStyle.swift

@@ -0,0 +1,53 @@
+//
+//  RowStyle.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 30/07/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// Any type that conforms to this protocol carries the info for the UI.
+public protocol RowStyle {
+
+  /// The type of the table view cell to display the row.
+  var cellType: UITableViewCell.Type { get }
+
+  /// The reuse identifier of the table view cell to display the row.
+  var cellReuseIdentifier: String { get }
+
+  /// The style of the table view cell to display the row.
+  var cellStyle: UITableViewCellStyle { get }
+
+  /// The icon of the row.
+  var icon: Icon? { get }
+
+  /// The type of standard accessory view the cell should use.
+  var accessoryType: UITableViewCellAccessoryType { get }
+
+  /// The flag that indicates whether the table view cell should trigger the action when selected.
+  var isSelectable: Bool { get }
+
+  /// The additional customization during cell configuration.
+  var customize: ((UITableViewCell, Row & RowStyle) -> Void)? { get }
+
+}

+ 187 - 0
Pods/QuickTableViewController/Source/QuickTableViewController.swift

@@ -0,0 +1,187 @@
+//
+//  QuickTableViewController.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 25/08/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A table view controller that shows `tableContents` as formatted sections and rows.
+open class QuickTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
+
+  /// A Boolean value indicating if the controller clears the selection when the collection view appears.
+  open var clearsSelectionOnViewWillAppear = true
+
+  /// Returns the table view managed by the controller object.
+  open private(set) var tableView: UITableView = UITableView(frame: .zero, style: .grouped)
+
+  /// The layout of sections and rows to display in the table view.
+  open var tableContents: [Section] = [] {
+    didSet {
+      tableView.reloadData()
+    }
+  }
+
+  // MARK: - Initialization
+
+  /**
+   Initializes a table view controller to manage a table view of a given style.
+
+   - parameter style: A constant that specifies the style of table view that the controller object is to manage (`.plain` or `.grouped`).
+
+   - returns: An initialized `QuickTableViewController` object.
+   */
+  public convenience init(style: UITableViewStyle) {
+    self.init(nibName: nil, bundle: nil)
+    tableView = UITableView(frame: .zero, style: style)
+  }
+
+  deinit {
+    tableView.dataSource = nil
+    tableView.delegate = nil
+  }
+
+  // MARK: - UIViewController
+
+  open override func viewDidLoad() {
+    super.viewDidLoad()
+    view.addSubview(tableView)
+    tableView.frame = view.bounds
+    tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+    tableView.rowHeight = UITableViewAutomaticDimension
+    tableView.estimatedRowHeight = 44
+    tableView.dataSource = self
+    tableView.delegate = self
+  }
+
+  open override func viewWillAppear(_ animated: Bool) {
+    super.viewWillAppear(animated)
+    if let indexPath = tableView.indexPathForSelectedRow, clearsSelectionOnViewWillAppear {
+      tableView.deselectRow(at: indexPath, animated: true)
+    }
+  }
+
+  // MARK: - UITableViewDataSource
+
+  open func numberOfSections(in tableView: UITableView) -> Int {
+    return tableContents.count
+  }
+
+  open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+    return tableContents[section].rows.count
+  }
+
+  open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+    return tableContents[section].title
+  }
+
+  open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+    let row = tableContents[indexPath.section].rows[indexPath.row]
+    let cell =
+      tableView.dequeueReusableCell(withIdentifier: row.cellReuseIdentifier) ??
+      row.cellType.init(style: row.cellStyle, reuseIdentifier: row.cellReuseIdentifier)
+
+    cell.defaultSetUp(with: row)
+    (cell as? Configurable)?.configure(with: row)
+    #if os(iOS)
+      (cell as? SwitchCell)?.delegate = self
+    #endif
+    row.customize?(cell, row)
+
+    return cell
+  }
+
+  open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
+    return tableContents[section].footer
+  }
+
+  // MARK: - UITableViewDelegate
+
+  open func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
+    return tableContents[indexPath.section].rows[indexPath.row].isSelectable
+  }
+
+  open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+    let section = tableContents[indexPath.section]
+    let row = section.rows[indexPath.row]
+
+    switch (section, row) {
+    case let (radio as RadioSection, option as OptionRowCompatible):
+      let changes: [IndexPath] = radio.toggle(option).map {
+        IndexPath(row: $0, section: indexPath.section)
+      }
+      if changes.isEmpty {
+        tableView.deselectRow(at: indexPath, animated: false)
+      } else {
+        tableView.reloadData()
+      }
+
+    case let (_, option as OptionRowCompatible):
+      // Allow OptionRow to be used without RadioSection.
+      option.isSelected = !option.isSelected
+      tableView.reloadData()
+
+    #if os(tvOS)
+    case let (_, row as SwitchRowCompatible):
+      // SwitchRow on tvOS behaves like OptionRow.
+      row.switchValue = !row.switchValue
+      tableView.reloadData()
+    #endif
+
+    case (_, is TapActionRowCompatible):
+      tableView.deselectRow(at: indexPath, animated: true)
+      // Avoid some unwanted animation when the action also involves table view reload.
+      DispatchQueue.main.async {
+        row.action?(row)
+      }
+
+    case let (_, row) where row.isSelectable:
+      DispatchQueue.main.async {
+        row.action?(row)
+      }
+
+    default:
+      break
+    }
+  }
+
+}
+
+
+#if os(iOS)
+extension QuickTableViewController: SwitchCellDelegate {
+
+  // MARK: - SwitchCellDelegate
+
+  open func switchCell(_ cell: SwitchCell, didToggleSwitch isOn: Bool) {
+    guard
+      let indexPath = tableView.indexPath(for: cell),
+      let row = tableContents[indexPath.section].rows[indexPath.row] as? SwitchRowCompatible
+    else {
+      return
+    }
+    row.switchValue = isOn
+  }
+
+}
+#endif

+ 102 - 0
Pods/QuickTableViewController/Source/Rows/NavigationRow.swift

@@ -0,0 +1,102 @@
+//
+//  NavigationRow.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A class that represents a row that triggers certain navigation when selected.
+open class NavigationRow<T: UITableViewCell>: NavigationRowCompatible, Equatable {
+
+  // MARK: - Initializer
+
+  /// Initializes a `NavigationRow` with a title and a subtitle.
+  /// The icon, customization and action closures are optional.
+  public init(
+    title: String,
+    subtitle: Subtitle,
+    icon: Icon? = nil,
+    customization: ((UITableViewCell, Row & RowStyle) -> Void)? = nil,
+    action: ((Row) -> Void)? = nil
+  ) {
+    self.title = title
+    self.subtitle = subtitle
+    self.icon = icon
+    self.customize = customization
+    self.action = action
+  }
+
+  // MARK: - Row
+
+  /// The title text of the row.
+  public let title: String
+
+  /// The subtitle text of the row.
+  public let subtitle: Subtitle?
+
+  /// A closure that will be invoked when the row is selected.
+  public let action: ((Row) -> Void)?
+
+  // MARK: - RowStyle
+
+  /// The type of the table view cell to display the row.
+  public let cellType: UITableViewCell.Type = T.self
+
+  /// Returns the reuse identifier of the table view cell to display the row.
+  public var cellReuseIdentifier: String {
+    return T.reuseIdentifier + (subtitle?.style.stringValue ?? "")
+  }
+
+  /// Returns the table view cell style for the specified subtitle.
+  public var cellStyle: UITableViewCellStyle {
+    return subtitle?.style ?? .default
+  }
+
+  /// The icon of the row.
+  public let icon: Icon?
+
+  /// Returns `.disclosureIndicator` when action is not nil, otherwise returns `.none`.
+  public var accessoryType: UITableViewCellAccessoryType {
+    return (action == nil) ? .none : .disclosureIndicator
+  }
+
+  /// The `NavigationRow` is selectable when action is not nil.
+  public var isSelectable: Bool {
+    return action != nil
+  }
+
+  /// The additional customization during cell configuration.
+  public let customize: ((UITableViewCell, Row & RowStyle) -> Void)?
+
+  // MARK: Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal titles, subtitles and icons.
+  public static func == (lhs: NavigationRow, rhs: NavigationRow) -> Bool {
+    return
+      lhs.title == rhs.title &&
+      lhs.subtitle == rhs.subtitle &&
+      lhs.icon == rhs.icon
+  }
+
+}

+ 110 - 0
Pods/QuickTableViewController/Source/Rows/OptionRow.swift

@@ -0,0 +1,110 @@
+//
+//  OptionRow.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 30/07/2017.
+//  Copyright © 2017 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A class that represents a row of selectable option.
+open class OptionRow<T: UITableViewCell>: OptionRowCompatible, Equatable {
+
+  // MARK: - Initializer
+
+  /// Initializes an `OptionRow` with a title, a selection state and an action closure.
+  /// The icon and the customization closure are optional.
+  public init(
+    title: String,
+    isSelected: Bool,
+    icon: Icon? = nil,
+    customization: ((UITableViewCell, Row & RowStyle) -> Void)? = nil,
+    action: ((Row) -> Void)?
+  ) {
+    self.title = title
+    self.isSelected = isSelected
+    self.icon = icon
+    self.customize = customization
+    self.action = action
+  }
+
+  // MARK: - OptionRowCompatible
+
+  /// The state of selection.
+  public var isSelected: Bool = false {
+    didSet {
+      guard isSelected != oldValue else {
+        return
+      }
+      DispatchQueue.main.async {
+        self.action?(self)
+      }
+    }
+  }
+
+  // MARK: - Row
+
+  /// The title text of the row.
+  public let title: String
+
+  /// Subtitle is disabled in `OptionRow`.
+  public let subtitle: Subtitle? = nil
+
+  /// A closure that will be invoked when the `isSelected` is changed.
+  public let action: ((Row) -> Void)?
+
+  // MARK: - RowStyle
+
+  /// The type of the table view cell to display the row.
+  public let cellType: UITableViewCell.Type = T.self
+
+  /// The reuse identifier of the table view cell to display the row. The default value is **UITableViewCell**.
+  public let cellReuseIdentifier: String = T.reuseIdentifier
+
+  /// The cell style is `.default`.
+  public let cellStyle: UITableViewCellStyle = .default
+
+  /// The icon of the row.
+  public let icon: Icon?
+
+  /// Returns `.checkmark` when the row is selected, otherwise returns `.none`.
+  public var accessoryType: UITableViewCellAccessoryType {
+    return isSelected ? .checkmark : .none
+  }
+
+  /// `OptionRow` is always selectable.
+  public let isSelectable: Bool = true
+
+  /// Additional customization during cell configuration.
+  public let customize: ((UITableViewCell, Row & RowStyle) -> Void)?
+
+  // MARK: - Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal titles, selection states, and icons.
+  public static func == (lhs: OptionRow, rhs: OptionRow) -> Bool {
+    return
+      lhs.title == rhs.title &&
+      lhs.isSelected == rhs.isSelected &&
+      lhs.icon == rhs.icon
+  }
+
+}

+ 122 - 0
Pods/QuickTableViewController/Source/Rows/SwitchRow.swift

@@ -0,0 +1,122 @@
+//
+//  SwitchRow.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A class that represents a row with a switch.
+open class SwitchRow<T: SwitchCell>: SwitchRowCompatible, Equatable {
+
+  // MARK: - Initializer
+
+  /// Initializes a `SwitchRow` with a title, a switch state and an action closure.
+  /// The icon and the customization closure are optional.
+  public init(
+    title: String,
+    switchValue: Bool,
+    icon: Icon? = nil,
+    customization: ((UITableViewCell, Row & RowStyle) -> Void)? = nil,
+    action: ((Row) -> Void)?
+  ) {
+    self.title = title
+    self.switchValue = switchValue
+    self.icon = icon
+    self.customize = customization
+    self.action = action
+  }
+
+  // MARK: - SwitchRowCompatible
+
+  /// The state of the switch.
+  public var switchValue: Bool = false {
+    didSet {
+      guard switchValue != oldValue else {
+        return
+      }
+      DispatchQueue.main.async {
+        self.action?(self)
+      }
+    }
+  }
+
+  // MARK: - Row
+
+  /// The title text of the row.
+  public let title: String
+
+  /// The subtitle is disabled in `SwitchRow`.
+  public let subtitle: Subtitle? = nil
+
+  /// A closure that will be invoked when the `switchValue` is changed.
+  public let action: ((Row) -> Void)?
+
+  // MARK: - RowStyle
+
+  /// The type of the table view cell to display the row.
+  public let cellType: UITableViewCell.Type = T.self
+
+  /// The reuse identifier of the table view cell to display the row. The default value is **SwitchCell**.
+  public let cellReuseIdentifier: String = T.reuseIdentifier
+
+  /// The cell style is `.default`.
+  public let cellStyle: UITableViewCellStyle = .default
+
+  /// The icon of the row.
+  public let icon: Icon?
+
+  #if os(iOS)
+
+  /// The default accessory type is `.none`.
+  public let accessoryType: UITableViewCellAccessoryType = .none
+
+  /// The `SwitchRow` should not be selectable.
+  public let isSelectable: Bool = false
+
+  #elseif os(tvOS)
+
+  /// Returns `.checkmark` when the `switchValue` is on, otherwise returns `.none`.
+  public var accessoryType: UITableViewCellAccessoryType {
+    return switchValue ? .checkmark : .none
+  }
+
+  /// The `SwitchRow` is selectable on tvOS.
+  public let isSelectable: Bool = true
+
+  #endif
+
+  /// The additional customization during cell configuration.
+  public let customize: ((UITableViewCell, Row & RowStyle) -> Void)?
+
+  // MARK: - Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal titles, switch values, and icons.
+  public static func == (lhs: SwitchRow, rhs: SwitchRow) -> Bool {
+    return
+      lhs.title == rhs.title &&
+      lhs.switchValue == rhs.switchValue &&
+      lhs.icon == rhs.icon
+  }
+
+}

+ 89 - 0
Pods/QuickTableViewController/Source/Rows/TapActionRow.swift

@@ -0,0 +1,89 @@
+//
+//  TapActionRow.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 01/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A class that represents a row that triggers certain action when selected.
+open class TapActionRow<T: TapActionCell>: TapActionRowCompatible, Equatable {
+
+  // MARK: - Initializer
+
+  /// Initializes a `TapActionRow` with a title, an action closure,
+  /// and an optional customization closure.
+  public init(
+    title: String,
+    customization: ((UITableViewCell, Row & RowStyle) -> Void)? = nil,
+    action: ((Row) -> Void)?
+  ) {
+    self.title = title
+    self.customize = customization
+    self.action = action
+  }
+
+  // MARK: - Row
+
+  /// The title text of the row.
+  public let title: String
+
+  /// The subtitle is disabled in `TapActionRow`.
+  public let subtitle: Subtitle? = nil
+
+  /// A closure that will be invoked when the row is selected.
+  public let action: ((Row) -> Void)?
+
+  // MARK: - RowStyle
+
+  /// The type of the table view cell to display the row.
+  public let cellType: UITableViewCell.Type = T.self
+
+  /// The reuse identifier of the table view cell to display the row. The default value is **TapActionCell**.
+  public let cellReuseIdentifier: String = T.reuseIdentifier
+
+  /// The cell style is `.default`.
+  public let cellStyle: UITableViewCellStyle = .default
+
+  /// The default icon is nil.
+  public let icon: Icon? = nil
+
+  /// The default accessory type is `.none`.
+  public let accessoryType: UITableViewCellAccessoryType = .none
+
+  /// The `TapActionRow` is selectable when action is not nil.
+  public var isSelectable: Bool {
+    return action != nil
+  }
+
+  /// The additional customization during cell configuration.
+  public let customize: ((UITableViewCell, Row & RowStyle) -> Void)?
+
+  // MARK: - Equatable
+
+  /// Returns true iff `lhs` and `rhs` have equal titles and subtitles.
+  public static func == (lhs: TapActionRow, rhs: TapActionRow) -> Bool {
+    return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle
+  }
+
+}

+ 111 - 0
Pods/QuickTableViewController/Source/Views/SwitchCell.swift

@@ -0,0 +1,111 @@
+//
+//  SwitchCell.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 03/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// The `SwitchCellDelegate` protocol allows the adopting delegate to respond to the UI interaction. Not available on tvOS.
+@available(tvOS, unavailable, message: "SwitchCellDelegate is not available on tvOS.")
+public protocol SwitchCellDelegate: class {
+  /// Tells the delegate that the switch control is toggled.
+  func switchCell(_ cell: SwitchCell, didToggleSwitch isOn: Bool)
+}
+
+
+/// A `UITableViewCell` subclass that shows a `UISwitch` as the `accessoryView`.
+open class SwitchCell: UITableViewCell, Configurable {
+
+  #if os(iOS)
+
+  /// A `UISwitch` as the `accessoryView`. Not available on tvOS.
+  @available(tvOS, unavailable, message: "switchControl is not available on tvOS.")
+  public private(set) lazy var switchControl: UISwitch = {
+    let control = UISwitch()
+    control.addTarget(self, action: #selector(SwitchCell.didToggleSwitch(_:)), for: .valueChanged)
+    return control
+  }()
+
+  #endif
+
+  /// The switch cell's delegate object, which should conform to `SwitchCellDelegate`. Not available on tvOS.
+  @available(tvOS, unavailable, message: "SwitchCellDelegate is not available on tvOS.")
+  open weak var delegate: SwitchCellDelegate?
+
+  // MARK: - Initializer
+
+  /**
+   Overrides `UITableViewCell`'s designated initializer.
+
+   - parameter style:           A constant indicating a cell style.
+   - parameter reuseIdentifier: A string used to identify the cell object if it is to be reused for drawing multiple rows of a table view.
+
+   - returns: An initialized `SwitchCell` object.
+   */
+  public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
+    super.init(style: style, reuseIdentifier: reuseIdentifier)
+    setUpAppearance()
+  }
+
+  /**
+   Overrides the designated initializer that returns an object initialized from data in a given unarchiver.
+
+   - parameter aDecoder: An unarchiver object.
+
+   - returns: `self`, initialized using the data in decoder.
+   */
+  public required init?(coder aDecoder: NSCoder) {
+    super.init(coder: aDecoder)
+    setUpAppearance()
+  }
+
+  // MARK: - Configurable
+
+  /// Set up the switch control (iOS) or accessory type (tvOS) with the provided row.
+  open func configure(with row: Row & RowStyle) {
+    #if os(iOS)
+      if let row = row as? SwitchRowCompatible {
+        switchControl.isOn = row.switchValue
+      }
+      accessoryView = switchControl
+    #elseif os(tvOS)
+      accessoryView = nil
+      accessoryType = row.accessoryType
+    #endif
+  }
+
+  // MARK: - Private
+
+  @available(tvOS, unavailable, message: "UISwitch is not available on tvOS.")
+  @objc
+  private func didToggleSwitch(_ sender: UISwitch) {
+    delegate?.switchCell(self, didToggleSwitch: sender.isOn)
+  }
+
+  private func setUpAppearance() {
+    textLabel?.numberOfLines = 0
+    detailTextLabel?.numberOfLines = 0
+  }
+
+}

+ 79 - 0
Pods/QuickTableViewController/Source/Views/TapActionCell.swift

@@ -0,0 +1,79 @@
+//
+//  TapActionCell.swift
+//  QuickTableViewController
+//
+//  Created by Ben on 03/09/2015.
+//  Copyright (c) 2015 bcylin.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in all
+//  copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//  SOFTWARE.
+//
+
+import UIKit
+
+/// A `UITableViewCell` subclass with the title text center aligned.
+open class TapActionCell: UITableViewCell {
+
+  // TapActionCell on tvOS does not need customization.
+  #if os(iOS)
+
+  // MARK: - Initializer
+
+  /**
+   Overrides `UITableViewCell`'s designated initializer.
+
+   - parameter style:           Unused. It always uses `UITableViewCellStyle.default`.
+   - parameter reuseIdentifier: A string used to identify the cell object if it is to be reused for drawing multiple rows of a table view.
+
+   - returns: An initialized `TapActionCell` object.
+   */
+  public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
+    super.init(style: .default, reuseIdentifier: reuseIdentifier)
+    setUpAppearance()
+  }
+
+  /**
+   Overrides the designated initializer that returns an object initialized from data in a given unarchiver.
+
+   - parameter aDecoder: An unarchiver object.
+
+   - returns: `self`, initialized using the data in decoder.
+   */
+  public required init?(coder aDecoder: NSCoder) {
+    super.init(coder: aDecoder)
+    setUpAppearance()
+  }
+
+  // MARK: UIView
+
+  open override func tintColorDidChange() {
+    super.tintColorDidChange()
+    textLabel?.textColor = tintColor
+  }
+
+  // MARK: Private Methods
+
+  private func setUpAppearance() {
+    textLabel?.numberOfLines = 0
+    textLabel?.textAlignment = .center
+    textLabel?.textColor = tintColor
+  }
+
+  #endif
+
+}

+ 19 - 0
Pods/ReachabilitySwift/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2016 Ashley Mills
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 202 - 0
Pods/ReachabilitySwift/README.md

@@ -0,0 +1,202 @@
+# Reachability.swift
+
+Reachability.swift is a replacement for Apple's Reachability sample, re-written in Swift with closures.
+
+It is compatible with **iOS** (8.0 - 11.0), **OSX** (10.9 - 10.13) and **tvOS** (9.0 - 11.0)
+
+Inspired by https://github.com/tonymillion/Reachability
+
+## Supporting **Reachability.swift**
+Keeping **Reachability.swift** up-to-date is a time consuming task. Making updates, reviewing pull requests, responding to issues and answering emails all take time. 
+
+If you're an iOS developer who's looking for a quick and easy way to create App Store screenshots, please try out my app [Screenshot Producer](https://itunes.apple.com/app/apple-store/id1252374855?pt=215893&ct=reachability&mt=8)…
+
+ Devices | Layout | Copy | Localize | Export      
+:------:|:------:|:------:|:------:|:------:
+![](http://is2.mzstatic.com/image/thumb/Purple118/v4/64/af/55/64af55bc-2ef0-691c-f5f3-4963685f7f63/source/552x414bb.jpg) |  ![](http://is4.mzstatic.com/image/thumb/Purple128/v4/fb/4c/bd/fb4cbd2f-dd04-22ba-4fdf-5ac652693fb8/source/552x414bb.jpg) |  ![](http://is1.mzstatic.com/image/thumb/Purple118/v4/5a/4f/cf/5a4fcfdf-ca04-0307-9f2e-83178e8ad90d/source/552x414bb.jpg) |  ![](http://is4.mzstatic.com/image/thumb/Purple128/v4/17/ea/56/17ea562e-e045-96e7-fcac-cfaaf4f499fd/source/552x414bb.jpg) |  ![](http://is4.mzstatic.com/image/thumb/Purple118/v4/59/9e/dd/599edd50-f05c-f413-8e88-e614731fd828/source/552x414bb.jpg)
+
+And don't forget to **★** the repo. This increases its visibility and encourages others to contribute.
+
+Thanks
+Ash
+
+# IMPORTANT
+
+## Version 4.0 breaking changes
+
+### CocoaPods:
+
+If you're adding **Reachability.swift** using CocoaPods, note that the framework name has changed from `ReachabilitySwift` to `Reachability` (for consistency with Carthage)
+
+### Previously:
+
+```swift
+enum NetworkStatus {
+    case notReachable, reachableViaWiFi, reachableViaWWAN
+}
+var currentReachabilityStatus: NetworkStatus
+```
+
+### Now:
+
+```swift
+enum Connection {
+    case none, wifi, cellular
+}
+var connection: Connection
+```
+
+### Other changes:
+
+- `reachableOnWWAN` has been renamed to `allowsCellularConnection`
+
+- `reachability.currentReachabilityString` has been deprecated. Use `"\(reachability.connection)"` instead.
+
+- `isReachable` has been deprecated. Use `connection != .none` instead.
+
+- `isReachableViaWWAN` has been deprecated. Use `connection == .cellular` instead.
+
+- The notification for reachability changes has been renamed from `ReachabilityChangedNotification` to `Notification.Name.reachabilityChanged`
+
+- All closure callbacks and notification are fired on the main queue (including when `startNotifier()` is called)
+
+
+## Got a problem?
+
+Please read https://github.com/ashleymills/Reachability.swift/blob/master/CONTRIBUTING.md before raising an issue.
+
+## Installation
+### Manual
+Just drop the **Reachability.swift** file into your project. That's it!
+
+### CocoaPods
+[CocoaPods][] is a dependency manager for Cocoa projects. To install Reachability.swift with CocoaPods:
+
+ 1. Make sure CocoaPods is [installed][CocoaPods Installation].
+
+ 2. Update your Podfile to include the following:
+
+    ``` ruby
+    use_frameworks!
+    pod 'ReachabilitySwift'
+    ```
+
+ 3. Run `pod install`.
+
+[CocoaPods]: https://cocoapods.org
+[CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started
+ 
+ 4. In your code import Reachability like so:
+   `import Reachability`
+
+### Carthage
+[Carthage][] is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
+To install Reachability.swift with Carthage:
+
+1. Install Carthage via [Homebrew][]
+  ```bash
+  $ brew update
+  $ brew install carthage
+  ```
+
+2. Add `github "ashleymills/Reachability.swift"` to your Cartfile.
+
+3. Run `carthage update`.
+
+4. Drag `Reachability.framework` from the `Carthage/Build/iOS/` directory to the `Linked Frameworks and Libraries` section of your Xcode project’s `General` settings.
+
+5. Add `$(SRCROOT)/Carthage/Build/iOS/Reachability.framework` to `Input Files` of Run Script Phase for Carthage.
+
+6. In your code import Reachability like so:
+`import Reachability`
+
+
+[Carthage]: https://github.com/Carthage/Carthage
+[Homebrew]: http://brew.sh
+[Photo Flipper]: https://itunes.apple.com/app/apple-store/id749627884?pt=215893&ct=GitHubReachability&mt=8
+
+## Example - closures
+
+NOTE: All closures are run on the **main queue**.
+
+```swift
+//declare this property where it won't go out of scope relative to your listener
+let reachability = Reachability()!
+
+reachability.whenReachable = { reachability in
+    if reachability.connection == .wifi {
+        print("Reachable via WiFi")
+    } else {
+        print("Reachable via Cellular")
+    }
+}
+reachability.whenUnreachable = { _ in
+    print("Not reachable")
+}
+
+do {
+    try reachability.startNotifier()
+} catch {
+    print("Unable to start notifier")
+}
+```
+
+and for stopping notifications
+
+```swift
+reachability.stopNotifier()
+```
+
+## Example - notifications
+
+NOTE: All notifications are delivered on the **main queue**.
+
+```swift
+//declare this property where it won't go out of scope relative to your listener
+let reachability = Reachability()!
+
+//declare this inside of viewWillAppear
+
+     NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
+    do{
+      try reachability.startNotifier()
+    }catch{
+      print("could not start reachability notifier")
+    }
+```
+
+and
+
+```swift
+@objc func reachabilityChanged(note: Notification) {
+
+  let reachability = note.object as! Reachability
+
+  switch reachability.connection {
+  case .wifi:
+      print("Reachable via WiFi")
+  case .cellular:
+      print("Reachable via Cellular")
+  case .none:
+    print("Network not reachable")
+  }
+}
+```
+
+and for stopping notifications
+
+```swift
+reachability.stopNotifier()
+NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
+```
+
+## Want to help?
+
+Got a bug fix, or a new feature? Create a pull request and go for it!
+
+## Let me know!
+
+If you use **Reachability.swift**, please let me know about your app and I'll put a link [here…](https://github.com/ashleymills/Reachability.swift/wiki/Apps-using-Reachability.swift) and tell your friends!
+
+Cheers,
+Ash

+ 316 - 0
Pods/ReachabilitySwift/Sources/Reachability.swift

@@ -0,0 +1,316 @@
+/*
+Copyright (c) 2014, Ashley Mills
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import SystemConfiguration
+import Foundation
+
+public enum ReachabilityError: Error {
+    case FailedToCreateWithAddress(sockaddr_in)
+    case FailedToCreateWithHostname(String)
+    case UnableToSetCallback
+    case UnableToSetDispatchQueue
+    case UnableToGetInitialFlags
+}
+
+@available(*, unavailable, renamed: "Notification.Name.reachabilityChanged")
+public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification")
+
+public extension Notification.Name {
+    public static let reachabilityChanged = Notification.Name("reachabilityChanged")
+}
+
+public class Reachability {
+
+    public typealias NetworkReachable = (Reachability) -> ()
+    public typealias NetworkUnreachable = (Reachability) -> ()
+
+    @available(*, unavailable, renamed: "Connection")
+    public enum NetworkStatus: CustomStringConvertible {
+        case notReachable, reachableViaWiFi, reachableViaWWAN
+        public var description: String {
+            switch self {
+            case .reachableViaWWAN: return "Cellular"
+            case .reachableViaWiFi: return "WiFi"
+            case .notReachable: return "No Connection"
+            }
+        }
+    }
+
+    public enum Connection: CustomStringConvertible {
+        case none, wifi, cellular
+        public var description: String {
+            switch self {
+            case .cellular: return "Cellular"
+            case .wifi: return "WiFi"
+            case .none: return "No Connection"
+            }
+        }
+    }
+
+    public var whenReachable: NetworkReachable?
+    public var whenUnreachable: NetworkUnreachable?
+
+    @available(*, deprecated: 4.0, renamed: "allowsCellularConnection")
+    public let reachableOnWWAN: Bool = true
+
+    /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`)
+    public var allowsCellularConnection: Bool
+
+    // The notification center on which "reachability changed" events are being posted
+    public var notificationCenter: NotificationCenter = NotificationCenter.default
+
+    @available(*, deprecated: 4.0, renamed: "connection.description")
+    public var currentReachabilityString: String {
+        return "\(connection)"
+    }
+
+    @available(*, unavailable, renamed: "connection")
+    public var currentReachabilityStatus: Connection {
+        return connection
+    }
+
+    public var connection: Connection {
+        if flags == nil {
+            try? setReachabilityFlags()
+        }
+        
+        switch flags?.connection {
+        case .none?, nil: return .none
+        case .cellular?: return allowsCellularConnection ? .cellular : .none
+        case .wifi?: return .wifi
+        }
+    }
+
+    fileprivate var isRunningOnDevice: Bool = {
+        #if targetEnvironment(simulator)
+            return false
+        #else
+            return true
+        #endif
+    }()
+
+    fileprivate var notifierRunning = false
+    fileprivate let reachabilityRef: SCNetworkReachability
+    fileprivate let reachabilitySerialQueue: DispatchQueue
+    fileprivate(set) var flags: SCNetworkReachabilityFlags? {
+        didSet {
+            guard flags != oldValue else { return }
+            reachabilityChanged()
+        }
+    }
+
+    required public init(reachabilityRef: SCNetworkReachability, queueQoS: DispatchQoS = .default, targetQueue: DispatchQueue? = nil) {
+        self.allowsCellularConnection = true
+        self.reachabilityRef = reachabilityRef
+        self.reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue)
+    }
+
+    public convenience init?(hostname: String, queueQoS: DispatchQoS = .default, targetQueue: DispatchQueue? = nil) {
+        guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { return nil }
+        self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue)
+    }
+
+    public convenience init?(queueQoS: DispatchQoS = .default, targetQueue: DispatchQueue? = nil) {
+        var zeroAddress = sockaddr()
+        zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size)
+        zeroAddress.sa_family = sa_family_t(AF_INET)
+
+        guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { return nil }
+
+        self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue)
+    }
+
+    deinit {
+        stopNotifier()
+    }
+}
+
+public extension Reachability {
+
+    // MARK: - *** Notifier methods ***
+    func startNotifier() throws {
+        guard !notifierRunning else { return }
+
+        let callback: SCNetworkReachabilityCallBack = { (reachability, flags, info) in
+            guard let info = info else { return }
+
+            let reachability = Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue()
+            reachability.flags = flags
+        }
+
+        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
+        context.info = UnsafeMutableRawPointer(Unmanaged<Reachability>.passUnretained(self).toOpaque())
+        if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) {
+            stopNotifier()
+            throw ReachabilityError.UnableToSetCallback
+        }
+
+        if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) {
+            stopNotifier()
+            throw ReachabilityError.UnableToSetDispatchQueue
+        }
+
+        // Perform an initial check
+        try setReachabilityFlags()
+
+        notifierRunning = true
+    }
+
+    func stopNotifier() {
+        defer { notifierRunning = false }
+
+        SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
+        SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
+    }
+
+    // MARK: - *** Connection test methods ***
+    @available(*, deprecated: 4.0, message: "Please use `connection != .none`")
+    var isReachable: Bool {
+        return connection != .none
+    }
+
+    @available(*, deprecated: 4.0, message: "Please use `connection == .cellular`")
+    var isReachableViaWWAN: Bool {
+        // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
+        return connection == .cellular
+    }
+
+    @available(*, deprecated: 4.0, message: "Please use `connection == .wifi`")
+    var isReachableViaWiFi: Bool {
+        return connection == .wifi
+    }
+
+    var description: String {
+        guard let flags = flags else { return "unavailable flags" }
+        let W = isRunningOnDevice ? (flags.isOnWWANFlagSet ? "W" : "-") : "X"
+        let R = flags.isReachableFlagSet ? "R" : "-"
+        let c = flags.isConnectionRequiredFlagSet ? "c" : "-"
+        let t = flags.isTransientConnectionFlagSet ? "t" : "-"
+        let i = flags.isInterventionRequiredFlagSet ? "i" : "-"
+        let C = flags.isConnectionOnTrafficFlagSet ? "C" : "-"
+        let D = flags.isConnectionOnDemandFlagSet ? "D" : "-"
+        let l = flags.isLocalAddressFlagSet ? "l" : "-"
+        let d = flags.isDirectFlagSet ? "d" : "-"
+
+        return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
+    }
+}
+
+fileprivate extension Reachability {
+
+    func setReachabilityFlags() throws {
+        try reachabilitySerialQueue.sync { [unowned self] in
+            var flags = SCNetworkReachabilityFlags()
+            if !SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags) {
+                self.stopNotifier()
+                throw ReachabilityError.UnableToGetInitialFlags
+            }
+            
+            self.flags = flags
+        }
+    }
+    
+    func reachabilityChanged() {
+        let block = connection != .none ? whenReachable : whenUnreachable
+
+        DispatchQueue.main.async { [weak self] in
+            guard let strongSelf = self else { return }
+            block?(strongSelf)
+            strongSelf.notificationCenter.post(name: .reachabilityChanged, object: strongSelf)
+        }
+    }
+}
+
+extension SCNetworkReachabilityFlags {
+
+    typealias Connection = Reachability.Connection
+
+    var connection: Connection {
+        guard isReachableFlagSet else { return .none }
+
+        // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi
+        #if targetEnvironment(simulator)
+        return .wifi
+        #else
+        var connection = Connection.none
+
+        if !isConnectionRequiredFlagSet {
+            connection = .wifi
+        }
+
+        if isConnectionOnTrafficOrDemandFlagSet {
+            if !isInterventionRequiredFlagSet {
+                connection = .wifi
+            }
+        }
+
+        if isOnWWANFlagSet {
+            connection = .cellular
+        }
+
+        return connection
+        #endif
+    }
+
+    var isOnWWANFlagSet: Bool {
+        #if os(iOS)
+        return contains(.isWWAN)
+        #else
+        return false
+        #endif
+    }
+    var isReachableFlagSet: Bool {
+        return contains(.reachable)
+    }
+    var isConnectionRequiredFlagSet: Bool {
+        return contains(.connectionRequired)
+    }
+    var isInterventionRequiredFlagSet: Bool {
+        return contains(.interventionRequired)
+    }
+    var isConnectionOnTrafficFlagSet: Bool {
+        return contains(.connectionOnTraffic)
+    }
+    var isConnectionOnDemandFlagSet: Bool {
+        return contains(.connectionOnDemand)
+    }
+    var isConnectionOnTrafficOrDemandFlagSet: Bool {
+        return !intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty
+    }
+    var isTransientConnectionFlagSet: Bool {
+        return contains(.transientConnection)
+    }
+    var isLocalAddressFlagSet: Bool {
+        return contains(.isLocalAddress)
+    }
+    var isDirectFlagSet: Bool {
+        return contains(.isDirect)
+    }
+    var isConnectionRequiredAndTransientFlagSet: Bool {
+        return intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]
+    }
+}

+ 26 - 0
Pods/Target Support Files/JGProgressHUD/Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>2.0.3</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/JGProgressHUD/JGProgressHUD-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_JGProgressHUD : NSObject
+@end
+@implementation PodsDummy_JGProgressHUD
+@end

+ 12 - 0
Pods/Target Support Files/JGProgressHUD/JGProgressHUD-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 29 - 0
Pods/Target Support Files/JGProgressHUD/JGProgressHUD-umbrella.h

@@ -0,0 +1,29 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "JGProgressHUD-Defines.h"
+#import "JGProgressHUD.h"
+#import "JGProgressHUDAnimation.h"
+#import "JGProgressHUDErrorIndicatorView.h"
+#import "JGProgressHUDFadeAnimation.h"
+#import "JGProgressHUDFadeZoomAnimation.h"
+#import "JGProgressHUDImageIndicatorView.h"
+#import "JGProgressHUDIndeterminateIndicatorView.h"
+#import "JGProgressHUDIndicatorView.h"
+#import "JGProgressHUDPieIndicatorView.h"
+#import "JGProgressHUDRingIndicatorView.h"
+#import "JGProgressHUDShadow.h"
+#import "JGProgressHUDSuccessIndicatorView.h"
+
+FOUNDATION_EXPORT double JGProgressHUDVersionNumber;
+FOUNDATION_EXPORT const unsigned char JGProgressHUDVersionString[];
+

+ 6 - 0
Pods/Target Support Files/JGProgressHUD/JGProgressHUD.modulemap

@@ -0,0 +1,6 @@
+framework module JGProgressHUD {
+  umbrella header "JGProgressHUD-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 9 - 0
Pods/Target Support Files/JGProgressHUD/JGProgressHUD.xcconfig

@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JGProgressHUD
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/JGProgressHUD
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES

+ 24 - 0
Pods/Target Support Files/JGProgressHUD/ResourceBundle-JGProgressHUD-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>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>2.0.3</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 72 - 0
Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-acknowledgements.markdown

@@ -1,6 +1,30 @@
 # Acknowledgements
 This application makes use of the following third party libraries:
 
+## JGProgressHUD
+
+The MIT License (MIT)
+
+Copyright (c) 2014-2018 Jonas Gessner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
 ## MessageInputBar
 
 MIT License
@@ -51,6 +75,54 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
 
+## QuickTableViewController
+
+The MIT License (MIT)
+
+Copyright (c) 2015 bcylin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+## ReachabilitySwift
+
+Copyright (c) 2016 Ashley Mills
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
 ## openssl-ios-bitcode
 
 

+ 90 - 0
Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-acknowledgements.plist

@@ -12,6 +12,36 @@
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2014-2018 Jonas Gessner
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>JGProgressHUD</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>MIT License
@@ -74,6 +104,66 @@ SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2015 bcylin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>QuickTableViewController</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>Copyright (c) 2016 Ashley Mills
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>ReachabilitySwift</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>

+ 6 - 0
Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-frameworks.sh

@@ -143,12 +143,18 @@ strip_invalid_archs() {
 
 
 if [[ "$CONFIGURATION" == "Debug" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/JGProgressHUD/JGProgressHUD.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/MessageInputBar/MessageInputBar.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/QuickTableViewController/QuickTableViewController.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework"
 fi
 if [[ "$CONFIGURATION" == "Release" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/JGProgressHUD/JGProgressHUD.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/MessageInputBar/MessageInputBar.framework"
   install_framework "${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/QuickTableViewController/QuickTableViewController.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework"
 fi
 if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
   wait

+ 3 - 3
Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.debug.xcconfig

@@ -1,11 +1,11 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
-FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar" "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JGProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar" "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit" "${PODS_CONFIGURATION_BUILD_DIR}/QuickTableViewController" "${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
 HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
 LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/openssl-ios-bitcode/lib"
-OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar/MessageInputBar.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit/MessageKit.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
-OTHER_LDFLAGS = $(inherited) -ObjC -l"crypto" -l"ssl" -framework "MessageInputBar" -framework "MessageKit"
+OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/JGProgressHUD/JGProgressHUD.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar/MessageInputBar.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit/MessageKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/QuickTableViewController/QuickTableViewController.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/Reachability.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"crypto" -l"ssl" -framework "JGProgressHUD" -framework "MessageInputBar" -framework "MessageKit" -framework "QuickTableViewController" -framework "Reachability"
 OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 3 - 3
Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios.release.xcconfig

@@ -1,11 +1,11 @@
 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
-FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar" "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit"
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/JGProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar" "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit" "${PODS_CONFIGURATION_BUILD_DIR}/QuickTableViewController" "${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift"
 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
 HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
 LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/openssl-ios-bitcode/lib"
-OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar/MessageInputBar.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit/MessageKit.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
-OTHER_LDFLAGS = $(inherited) -ObjC -l"crypto" -l"ssl" -framework "MessageInputBar" -framework "MessageKit"
+OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/JGProgressHUD/JGProgressHUD.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageInputBar/MessageInputBar.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MessageKit/MessageKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/QuickTableViewController/QuickTableViewController.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift/Reachability.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/openssl-ios-bitcode"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"crypto" -l"ssl" -framework "JGProgressHUD" -framework "MessageInputBar" -framework "MessageKit" -framework "QuickTableViewController" -framework "Reachability"
 OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
 PODS_BUILD_DIR = ${BUILD_DIR}
 PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

+ 26 - 0
Pods/Target Support Files/QuickTableViewController/Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/QuickTableViewController/QuickTableViewController-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_QuickTableViewController : NSObject
+@end
+@implementation PodsDummy_QuickTableViewController
+@end

+ 12 - 0
Pods/Target Support Files/QuickTableViewController/QuickTableViewController-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 16 - 0
Pods/Target Support Files/QuickTableViewController/QuickTableViewController-umbrella.h

@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double QuickTableViewControllerVersionNumber;
+FOUNDATION_EXPORT const unsigned char QuickTableViewControllerVersionString[];
+

+ 6 - 0
Pods/Target Support Files/QuickTableViewController/QuickTableViewController.modulemap

@@ -0,0 +1,6 @@
+framework module QuickTableViewController {
+  umbrella header "QuickTableViewController-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 9 - 0
Pods/Target Support Files/QuickTableViewController/QuickTableViewController.xcconfig

@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/QuickTableViewController
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/QuickTableViewController
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES

+ 26 - 0
Pods/Target Support Files/ReachabilitySwift/Info.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.3.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>

+ 5 - 0
Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_ReachabilitySwift : NSObject
+@end
+@implementation PodsDummy_ReachabilitySwift
+@end

+ 12 - 0
Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 16 - 0
Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift-umbrella.h

@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double ReachabilityVersionNumber;
+FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[];
+

+ 6 - 0
Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.modulemap

@@ -0,0 +1,6 @@
+framework module Reachability {
+  umbrella header "ReachabilitySwift-umbrella.h"
+
+  export *
+  module * { export * }
+}

+ 10 - 0
Pods/Target Support Files/ReachabilitySwift/ReachabilitySwift.xcconfig

@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "SystemConfiguration"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/ReachabilitySwift
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES

+ 31 - 4
deltachat-ios.xcodeproj/project.pbxproj

@@ -63,6 +63,7 @@
 		78E45E4221D3DB4000D4B15E /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E45E4121D3DB4000D4B15E /* UIViewController+Extension.swift */; };
 		78E45E4421D3F14A00D4B15E /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E45E4321D3F14A00D4B15E /* UIImage+Extension.swift */; };
 		78E45E4C21D404AE00D4B15E /* CustomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E45E4B21D404AE00D4B15E /* CustomCell.swift */; };
+		78ED838321D5379000243125 /* TextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78ED838221D5379000243125 /* TextFieldCell.swift */; };
 		7A0052A11FBC50C40048C3BF /* CredentialsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0052A01FBC50C40048C3BF /* CredentialsController.swift */; };
 		7A0052C81FBE6CB40048C3BF /* NewContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0052C71FBE6CB40048C3BF /* NewContactController.swift */; };
 		7A451D941FB1B1DB00177250 /* wrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = 7A451D921FB1B1DB00177250 /* wrapper.c */; };
@@ -94,7 +95,6 @@
 		AEACE2E11FB3271700DCDD78 /* SampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEACE2E01FB3271700DCDD78 /* SampleData.swift */; };
 		AEACE2E31FB32B5C00DCDD78 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEACE2E21FB32B5C00DCDD78 /* Constants.swift */; };
 		AEACE2E51FB32E1900DCDD78 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEACE2E41FB32E1900DCDD78 /* Utils.swift */; };
-		AEACE2E91FB34D9100DCDD78 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEACE2E81FB34D9100DCDD78 /* ContactViewController.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -176,6 +176,7 @@
 		7092474020B3869500AF8799 /* ContactProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactProfileViewController.swift; sourceTree = "<group>"; };
 		70B08FCC21073B910097D3EA /* NewGroupMemberChoiceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGroupMemberChoiceController.swift; sourceTree = "<group>"; };
 		70B8882D2091B8550074812E /* ContactCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactCell.swift; sourceTree = "<group>"; };
+		78C7036A21D46752005D4525 /* deltachat-ios.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "deltachat-ios.entitlements"; sourceTree = "<group>"; };
 		78E45E2121D1768900D4B15E /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = "deltachat-ios/libraries/deltachat-core/src"; sourceTree = "<group>"; };
 		78E45E2821D176C300D4B15E /* dc_jobthread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dc_jobthread.h; path = "deltachat-ios/libraries/deltachat-core/src/dc_jobthread.h"; sourceTree = "<group>"; };
 		78E45E2A21D176FB00D4B15E /* dc_jobthread.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dc_jobthread.c; path = "deltachat-ios/libraries/deltachat-core/src/dc_jobthread.c"; sourceTree = "<group>"; };
@@ -190,6 +191,7 @@
 		78E45E4121D3DB4000D4B15E /* UIViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; };
 		78E45E4321D3F14A00D4B15E /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = "<group>"; };
 		78E45E4B21D404AE00D4B15E /* CustomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCell.swift; sourceTree = "<group>"; };
+		78ED838221D5379000243125 /* TextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldCell.swift; sourceTree = "<group>"; };
 		7A0052A01FBC50C40048C3BF /* CredentialsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialsController.swift; sourceTree = "<group>"; };
 		7A0052C71FBE6CB40048C3BF /* NewContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewContactController.swift; sourceTree = "<group>"; };
 		7A451D921FB1B1DB00177250 /* wrapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wrapper.c; sourceTree = "<group>"; };
@@ -250,7 +252,6 @@
 		AEACE2E01FB3271700DCDD78 /* SampleData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleData.swift; sourceTree = "<group>"; };
 		AEACE2E21FB32B5C00DCDD78 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
 		AEACE2E41FB32E1900DCDD78 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
-		AEACE2E81FB34D9100DCDD78 /* ContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -381,6 +382,7 @@
 		7A9FB1421FB061E2001FEA36 /* deltachat-ios */ = {
 			isa = PBXGroup;
 			children = (
+				78C7036A21D46752005D4525 /* deltachat-ios.entitlements */,
 				AEACE2DE1FB3246400DCDD78 /* Message.swift */,
 				7A9FB15B1FB07364001FEA36 /* libraries */,
 				7A9FB1431FB061E2001FEA36 /* AppDelegate.swift */,
@@ -413,8 +415,8 @@
 				AEACE2E01FB3271700DCDD78 /* SampleData.swift */,
 				AEACE2E21FB32B5C00DCDD78 /* Constants.swift */,
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
-				AEACE2E81FB34D9100DCDD78 /* ContactViewController.swift */,
 				78E45E3B21D3D03700D4B15E /* TableViewCell.swift */,
+				78ED838221D5379000243125 /* TextFieldCell.swift */,
 			);
 			path = "deltachat-ios";
 			sourceTree = "<group>";
@@ -538,6 +540,17 @@
 						CreatedOnToolsVersion = 9.1;
 						LastSwiftMigration = 1000;
 						ProvisioningStyle = Automatic;
+						SystemCapabilities = {
+							com.apple.ApplicationGroups.iOS = {
+								enabled = 1;
+							};
+							com.apple.BackgroundModes = {
+								enabled = 1;
+							};
+							com.apple.Push = {
+								enabled = 1;
+							};
+						};
 					};
 				};
 			};
@@ -609,13 +622,19 @@
 			);
 			inputPaths = (
 				"${SRCROOT}/Pods/Target Support Files/Pods-deltachat-ios/Pods-deltachat-ios-frameworks.sh",
+				"${BUILT_PRODUCTS_DIR}/JGProgressHUD/JGProgressHUD.framework",
 				"${BUILT_PRODUCTS_DIR}/MessageInputBar/MessageInputBar.framework",
 				"${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework",
+				"${BUILT_PRODUCTS_DIR}/QuickTableViewController/QuickTableViewController.framework",
+				"${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JGProgressHUD.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageInputBar.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageKit.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/QuickTableViewController.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -653,7 +672,6 @@
 				78E45E2B21D176FB00D4B15E /* dc_jobthread.c in Sources */,
 				78E45E2921D176C400D4B15E /* dc_jobthread.h in Sources */,
 				7A79236E1FB0A2C800BC2DE5 /* misc.c in Sources */,
-				AEACE2E91FB34D9100DCDD78 /* ContactViewController.swift in Sources */,
 				7A0052A11FBC50C40048C3BF /* CredentialsController.swift in Sources */,
 				7070FB6B20FF345F000DC258 /* dc_job.c in Sources */,
 				7070FB7520FF345F000DC258 /* dc_strbuilder.c in Sources */,
@@ -689,6 +707,7 @@
 				7070FB8F20FF4118000DC258 /* dc_loginparam.c in Sources */,
 				7AE0A5491FC42F65005ECB4B /* NewChatViewController.swift in Sources */,
 				78E45E3A21D3CFBC00D4B15E /* SettingsController.swift in Sources */,
+				78ED838321D5379000243125 /* TextFieldCell.swift in Sources */,
 				78E45E3C21D3D03700D4B15E /* TableViewCell.swift in Sources */,
 				7070FB7220FF345F000DC258 /* dc_key.c in Sources */,
 				7070FB7420FF345F000DC258 /* dc_e2ee.c in Sources */,
@@ -864,6 +883,8 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements";
+				CODE_SIGN_IDENTITY = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
 				DEVELOPMENT_TEAM = EEQW58QXHC;
 				HEADER_SEARCH_PATHS = "deltachat-ios/libraries/deltachat-core/libs/netpgp/include";
@@ -874,9 +895,11 @@
 					"-lsasl2",
 					"-lz",
 					"-lcrypto",
+					"$(inherited)",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.jonasreinsch.deltachatios;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OBJC_BRIDGING_HEADER = "deltachat-ios/deltachat-ios-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 4.2;
@@ -890,6 +913,8 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
+				CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements";
+				CODE_SIGN_IDENTITY = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
 				DEVELOPMENT_TEAM = EEQW58QXHC;
 				HEADER_SEARCH_PATHS = "deltachat-ios/libraries/deltachat-core/libs/netpgp/include";
@@ -900,9 +925,11 @@
 					"-lsasl2",
 					"-lz",
 					"-lcrypto",
+					"$(inherited)",
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.jonasreinsch.deltachatios;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OBJC_BRIDGING_HEADER = "deltachat-ios/deltachat-ios-Bridging-Header.h";
 				SWIFT_VERSION = 4.2;
 				TARGETED_DEVICE_FAMILY = "1,2";

+ 0 - 5
deltachat-ios/AppCoordinator.swift

@@ -16,11 +16,6 @@ class AppCoordinator: Coordinator {
     let baseController = BaseController()
 
     func setupViewControllers(window: UIWindow) {
-        let ud = UserDefaults.standard
-        if ud.bool(forKey: Constants.Keys.deltachatUserProvidedCredentialsKey) {
-            initCore(withCredentials: false)
-        }
-        
         window.rootViewController = AppTabBarController()
         window.makeKeyAndVisible()
         // window.backgroundColor = UIColor.white

+ 206 - 51
deltachat-ios/AppDelegate.swift

@@ -8,16 +8,18 @@
 
 import UIKit
 import AudioToolbox
-
+import UserNotifications
+import Reachability
 
 var mailboxPointer:UnsafeMutablePointer<dc_context_t>!
 let dc_notificationChanged = Notification.Name(rawValue:"MrEventMsgsChanged")
+let dc_notificationStateChanged = Notification.Name(rawValue:"MrEventStateChanged")
 let dc_notificationIncoming = Notification.Name(rawValue:"MrEventIncomingMsg")
 
 @_silgen_name("callbackSwift")
 
 public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLong, data1String: UnsafePointer<Int8>, data2String: UnsafePointer<Int8>) -> UnsafePointer<Int8>? {
-    
+
     switch event {
     case DC_EVENT_HTTP_GET:
         let urlString = String(cString: data1String)
@@ -63,7 +65,7 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
                 }
             }
             let nc = NotificationCenter.default
-            
+
             DispatchQueue.main.async {
                 nc.post(name:Notification.Name(rawValue:"ProgressUpdated"),
                         object: nil,
@@ -71,7 +73,23 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
             }
         }
         return nil
-    case DC_EVENT_IS_OFFLINE:
+    case DC_EVENT_ERROR_NETWORK:
+        print("network error")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(name: dc_notificationStateChanged,
+                    object: nil,
+                    userInfo: ["state": "offline"])
+        }
+        return nil
+    case DC_EVENT_IMAP_CONNECTED, DC_EVENT_SMTP_CONNECTED:
+        print("connected")
+        let nc = NotificationCenter.default
+        DispatchQueue.main.async {
+            nc.post(name: dc_notificationStateChanged,
+                    object: nil,
+                    userInfo: ["state": "online"])
+        }
         return nil
     case DC_EVENT_MSGS_CHANGED, DC_EVENT_MSG_READ, DC_EVENT_MSG_DELIVERED:
         // TODO: reload all views
@@ -79,7 +97,7 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         // to set badge / notification
         print("change", event)
         let nc = NotificationCenter.default
-        
+
         DispatchQueue.main.async {
             nc.post(name:dc_notificationChanged,
                     object: nil,
@@ -94,7 +112,7 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
         // TODO: reload all views + set notification / badge
         // mrmailbox_get_fresh_msgs
         let nc = NotificationCenter.default
-        
+
         // let msg = MRMessage.init(id: Int(data2))
         // TODO: default summary
         // if let summary = msg.summary(chars: 32) {
@@ -108,31 +126,44 @@ public func callbackSwift(event: CInt, data1: CUnsignedLong, data2: CUnsignedLon
                         "date": Date()
                     ])
         }
-    case DC_EVENT_IMAP_CONNECTED:
-        print("imap connected", data2String)
-    case DC_EVENT_SMTP_CONNECTED:
-        print("smtp connected", data2String)
     case DC_EVENT_GET_STRING:
         break
     case DC_EVENT_SMTP_MESSAGE_SENT:
         print("smtp message sent", data2String)
     case DC_EVENT_MSG_DELIVERED:
         print("message delivered", data1, data2)
+    case DC_EVENT_IMEX_PROGRESS:
+        print("backup progress")
+    case DC_EVENT_IMEX_FILE_WRITTEN:
+        print("finished creating backup")
     default:
         print("unknown event", event, data1String, data2String)
     }
+
     return nil
 }
 
+enum ApplicationState {
+    case stopped
+    case running
+    case background
+    case backgroundFetch
+}
+
 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate {
     static let appCoordinator = AppCoordinator()
     static var progress:Float = 0
     static var lastErrorDuringConfig:String? = nil
     static var cancellableCredentialsController = false
+
+    var reachability = Reachability()!
     var window: UIWindow?
 
+    var state = ApplicationState.stopped
+
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+        print("---- launch ----")
         // Override point for customization after application launch.
 
         window = UIWindow(frame: UIScreen.main.bounds)
@@ -141,56 +172,179 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         }
         AppDelegate.appCoordinator.setupViewControllers(window: window)
 
+        UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
+
+        start()
+        open()
+
+        let ud = UserDefaults.standard
+        if ud.bool(forKey: Constants.Keys.deltachatUserProvidedCredentialsKey) {
+            initCore(withCredentials: false)
+        }
+
+        // registerForPushNotifications()
+
         return true
     }
-}
 
+    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+        print("---- background-fetch ----")
 
-func initCore(withCredentials: Bool, advancedMode:Bool = false, model:CredentialsModel? = nil, cancellableCredentialsUponFailure: Bool = false) {
-    AppDelegate.cancellableCredentialsController = cancellableCredentialsUponFailure
+        if mailboxPointer == nil {
+            //       - second param remains nil (user data for more than one mailbox)
+            mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
+            guard mailboxPointer != nil else {
+                fatalError("Error: dc_context_new returned nil")
+            }
+        }
+
+        if state == .background {
+            state = .backgroundFetch
 
-    let paths = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
-    let documentsPath = paths[0]
-    let dbfile = documentsPath + "/messenger.db"
-    print(dbfile)
-    
-    //       - second param remains nil (user data for more than one mailbox)
-    mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
-    guard mailboxPointer != nil else {
-        fatalError("Error: dc_context_new returned nil")
-    }
-    
-    DispatchQueue.global(qos: .background).async {
-        while true {
-            dc_perform_imap_jobs(mailboxPointer)
             dc_perform_imap_fetch(mailboxPointer)
-            dc_perform_imap_idle(mailboxPointer)
+            dc_perform_mvbox_fetch(mailboxPointer)
+
+            // TODO: actually set the right value depending on if we found sth
+            completionHandler(.newData)
+
+            state = .background
+        } else {
+            // only start a round of jobs if we are not already doing one
+            completionHandler(.noData)
         }
     }
-    
-    DispatchQueue.global(qos: .utility).async {
-        while true {
-            dc_perform_smtp_jobs(mailboxPointer)
-            dc_perform_smtp_idle(mailboxPointer)
+
+    func applicationWillEnterForeground(_ application: UIApplication) {
+        print("---- foreground ----")
+        start()
+    }
+
+    func applicationDidEnterBackground(_ application: UIApplication) {
+        print("---- background ----")
+        state = .background
+        stop()
+    }
+
+    func applicationWillTerminate(_ application: UIApplication) {
+        print("---- terminate ----")
+        state = .stopped
+        close()
+    }
+
+    private func open() {
+        let paths = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)
+        let documentsPath = paths[0]
+        let dbfile = documentsPath + "/messenger.db"
+        print(dbfile)
+
+        let _ = dc_open(mailboxPointer, dbfile, nil)
+    }
+
+    private func stop() {
+        dc_interrupt_imap_idle(mailboxPointer)
+        dc_interrupt_smtp_idle(mailboxPointer)
+        dc_interrupt_mvbox_idle(mailboxPointer)
+        dc_interrupt_sentbox_idle(mailboxPointer)
+
+        reachability.stopNotifier()
+        NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
+    }
+
+    private func close() {
+        dc_close(mailboxPointer)
+        mailboxPointer = nil
+
+        reachability.stopNotifier()
+        NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)
+    }
+
+    private func start() {
+        print("---- start ----")
+
+        if mailboxPointer == nil {
+            //       - second param remains nil (user data for more than one mailbox)
+            mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
+            guard mailboxPointer != nil else {
+                fatalError("Error: dc_context_new returned nil")
+            }
+        }
+
+        state = .running
+
+        DispatchQueue.global(qos: .background).async {
+            while self.state == .running {
+                dc_perform_imap_jobs(mailboxPointer)
+                dc_perform_imap_fetch(mailboxPointer)
+                dc_perform_imap_idle(mailboxPointer)
+            }
+        }
+
+        DispatchQueue.global(qos: .utility).async {
+            while self.state == .running {
+                dc_perform_smtp_jobs(mailboxPointer)
+                dc_perform_smtp_idle(mailboxPointer)
+            }
+        }
+
+        DispatchQueue.global(qos: .background).async {
+            while self.state == .running {
+                dc_perform_sentbox_fetch(mailboxPointer)
+                dc_perform_sentbox_idle(mailboxPointer)
+            }
+        }
+
+        DispatchQueue.global(qos: .background).async {
+            while self.state == .running {
+                dc_perform_mvbox_fetch(mailboxPointer)
+                dc_perform_mvbox_idle(mailboxPointer)
+            }
+        }
+
+        NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
+        do {
+            try reachability.startNotifier()
+        } catch {
+            print("could not start reachability notifier")
         }
     }
-    
-    DispatchQueue.global(qos: .background).async {
-        while true {
-            dc_perform_sentbox_fetch(mailboxPointer)
-            dc_perform_sentbox_idle(mailboxPointer)
+
+    @objc func reachabilityChanged(note: Notification) {
+        let reachability = note.object as! Reachability
+
+        switch reachability.connection {
+        case .wifi, .cellular:
+            print("Reachable", reachability.connection)
+            dc_maybe_network(mailboxPointer)
+
+            let nc = NotificationCenter.default
+            DispatchQueue.main.async {
+                nc.post(name: dc_notificationStateChanged,
+                        object: nil,
+                        userInfo: ["state": "online"])
+            }
+        case .none:
+            print("Network not reachable")
+            let nc = NotificationCenter.default
+            DispatchQueue.main.async {
+                nc.post(name: dc_notificationStateChanged,
+                        object: nil,
+                        userInfo: ["state": "offline"])
+            }
         }
     }
-    
-    DispatchQueue.global(qos: .background).async {
-        while true {
-            dc_perform_mvbox_fetch(mailboxPointer)
-            dc_perform_mvbox_idle(mailboxPointer)
+
+    func registerForPushNotifications() {
+        UNUserNotificationCenter.current()
+            .requestAuthorization(options: [.alert, .sound, .badge]) {
+                granted, error in
+                print("Permission granted: \(granted)")
         }
     }
-    
-    let _ = dc_open(mailboxPointer, dbfile, nil)
-    
+}
+
+func initCore(withCredentials: Bool, advancedMode:Bool = false, model:CredentialsModel? = nil, cancellableCredentialsUponFailure: Bool = false) {
+    AppDelegate.cancellableCredentialsController = cancellableCredentialsUponFailure
+
     if withCredentials {
         guard let model = model else {
             fatalError("withCredentials == true implies non-nil model")
@@ -214,7 +368,7 @@ func initCore(withCredentials: Bool, advancedMode:Bool = false, model:Credential
                 dc_set_config(mailboxPointer, "mail_port", imapPort)
             }
 
-            
+
             if let smtpLoginName = model.smtpLoginName {
                 dc_set_config(mailboxPointer, "send_user", smtpLoginName)
             }
@@ -227,7 +381,7 @@ func initCore(withCredentials: Bool, advancedMode:Bool = false, model:Credential
             if let smtpPort = model.smtpPort {
                 dc_set_config(mailboxPointer, "send_port", smtpPort)
             }
-            
+
             var flags:Int32 = 0
             if (model.smtpSecurity == .automatic) && (model.imapSecurity == .automatic) {
                 flags = DC_LP_AUTH_NORMAL
@@ -239,7 +393,7 @@ func initCore(withCredentials: Bool, advancedMode:Bool = false, model:Credential
                 } else if model.smtpSecurity == .starttls {
                     flags |= DC_LP_SMTP_SOCKET_STARTTLS
                 }
-                
+
                 if model.imapSecurity == .off {
                     flags |= DC_LP_IMAP_SOCKET_PLAIN
                 } else if model.imapSecurity == .ssltls {
@@ -255,17 +409,18 @@ func initCore(withCredentials: Bool, advancedMode:Bool = false, model:Credential
             let up = rp.bindMemory(to: Int8.self, capacity: 1)
             dc_set_config(mailboxPointer, "server_flags", up)
         }
-        
+
         // TODO: - handle failure, need to show credentials screen again
         dc_configure(mailboxPointer)
         // TODO: next two lines should move here in success case
         // UserDefaults.standard.set(true, forKey: Constants.Keys.deltachatUserProvidedCredentialsKey)
         // UserDefaults.standard.synchronize()
     }
-    
+
     addVibrationOnIncomingMessage()
 }
 
+
 func addVibrationOnIncomingMessage() {
     let nc = NotificationCenter.default
     nc.addObserver(forName:Notification.Name(rawValue:"MrEventIncomingMsg"),

+ 9 - 2
deltachat-ios/ChatListController.swift

@@ -93,6 +93,9 @@ class ChatListController: UIViewController {
         chatTable.dataSource = chatTableDataSource
         chatTableDelegate.chatPresenter = self
         chatTable.delegate = chatTableDelegate
+        
+        chatTable.rowHeight = 80
+        
         let newImage:UIImage = UIImage(named: "create_new")!
         newButton = UIBarButtonItem(image: newImage, landscapeImagePhone: nil, style: .plain, target: self, action: #selector(didPressNewChat))
     
@@ -158,7 +161,7 @@ class ChatTableDataSource: NSObject, UITableViewDataSource  {
         if let c = tableView.dequeueReusableCell(withIdentifier: "ChatCell") as? ContactCell {
             cell = c
         } else {
-            cell = ContactCell(style: .subtitle, reuseIdentifier: "ChatCell")
+            cell = ContactCell(style: .default, reuseIdentifier: "ChatCell")
         }
 
         let chatId = chatList.getChatId(index: row)
@@ -166,7 +169,11 @@ class ChatTableDataSource: NSObject, UITableViewDataSource  {
         let summary = chatList.summary(index: row)
         
         cell.nameLabel.text = chat.name
-        cell.setBackupImage(name: chat.name, color: chat.color)
+        if let img = chat.profileImage {
+            cell.setImage(img)
+        } else {
+            cell.setBackupImage(name: chat.name, color: chat.color)
+        }
 
         let result1 = summary.text1 ?? ""
         let result2 = summary.text2 ?? ""

+ 1 - 5
deltachat-ios/ChatViewController.swift

@@ -60,6 +60,7 @@ class ChatViewController: MessagesViewController {
                 self.messageList = self.messageIds.map(self.idToMessage)
                 self.messagesCollectionView.reloadData()
                 self.refreshControl.endRefreshing()
+                self.messagesCollectionView.scrollToBottom(animated: false)
             }
         }
     }
@@ -356,7 +357,6 @@ extension ChatViewController: MessagesDataSource {
 
         guard indexPath.section < messageList.count else { return nil }
         if let m = messageToMRMessage(message: messageList[indexPath.section]) {
-            print("state", m.stateOutDescription(), !isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message))
             if !isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message) {
                 return NSAttributedString(string: m.stateOutDescription(), attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
             }
@@ -607,17 +607,14 @@ extension ChatViewController: MessageLabelDelegate {
         if let escapedMapAddress = mapAddress.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
             // Use query, to handle malformed addresses
             if let url = URL(string: "http://maps.apple.com/?q=\(escapedMapAddress)") {
-                print("open address", url, addressComponents)
                 UIApplication.shared.open(url as URL)
             }
         }
     }
     
     func didSelectDate(_ date: Date) {
-        print("date open", date)
         let interval = date.timeIntervalSinceReferenceDate
         if let url = NSURL(string: "calshow:\(interval)") {
-            print("open", url)
             UIApplication.shared.open(url as URL)
         }
     }
@@ -671,7 +668,6 @@ extension ChatViewController: MessageInputBarDelegate {
         DispatchQueue.global().async {
             dc_send_text_msg(mailboxPointer, UInt32(self.chatId), text)
         }
-        print(text)
         inputBar.inputTextView.text = String()
     }
 }

+ 3 - 0
deltachat-ios/Constants.swift

@@ -24,5 +24,8 @@ struct Constants {
     static let primaryColor = UIColor(red: 81/255, green: 73/255, blue: 255/255, alpha: 1)
     static let messagePrimaryColor = UIColor(red: 234/255, green: 233/255, blue: 246/255, alpha: 1)
     static let messageSecondaryColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
+    
+    static let defaultShadow = UIImage(color: UIColor(hexString: "ff2b82"), size: CGSize(width: 1, height: 1))
+    static let onlineShadow = UIImage(color: UIColor(hexString: "3ed67e"), size: CGSize(width: 1, height: 1))
 }
 

+ 16 - 6
deltachat-ios/ContactCell.swift

@@ -26,13 +26,13 @@ class ContactCell: UITableViewCell {
     override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
         super.init(style: style, reuseIdentifier: reuseIdentifier)
    
-        //configure and layout initialsLabel
-        let initialsLabelSize:CGFloat = 60
+        // configure and layout initialsLabel
+        let initialsLabelSize: CGFloat = 48
         let initialsLabelCornerRadius = initialsLabelSize/2
-        let margin:CGFloat = 15
+        let margin: CGFloat = 15
         initialsLabel.textAlignment = NSTextAlignment.center
         initialsLabel.textColor = UIColor.white
-        initialsLabel.font = UIFont.systemFont(ofSize: 24)
+        initialsLabel.font = UIFont.systemFont(ofSize: 22)
         initialsLabel.translatesAutoresizingMaskIntoConstraints = false
         initialsLabel.widthAnchor.constraint(equalToConstant: initialsLabelSize).isActive = true
         initialsLabel.heightAnchor.constraint(equalToConstant: initialsLabelSize).isActive = true
@@ -41,22 +41,32 @@ class ContactCell: UITableViewCell {
         initialsLabel.layer.cornerRadius = initialsLabelCornerRadius
         initialsLabel.clipsToBounds = true
         
+        
         self.contentView.addSubview(initialsLabel)
         initialsLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: margin).isActive = true
+        initialsLabel.center = self.contentView.center
         initialsLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: margin).isActive = true
-        initialsLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -margin).isActive = true
+        initialsLabel.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -margin).isActive = true
         
         let myStackView = UIStackView()
         myStackView.translatesAutoresizingMaskIntoConstraints = false
+        myStackView.clipsToBounds = true
+        
         self.contentView.addSubview(myStackView)
         myStackView.leadingAnchor.constraint(equalTo: initialsLabel.trailingAnchor, constant: margin).isActive = true
         myStackView.centerYAnchor.constraint(equalTo: initialsLabel.centerYAnchor).isActive = true
+        myStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -margin).isActive = true
         myStackView.axis = .vertical
         myStackView.addArrangedSubview(nameLabel)
         myStackView.addArrangedSubview(emailLabel)
         
+        nameLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
+        nameLabel.lineBreakMode = .byTruncatingTail
+        nameLabel.textColor = UIColor(hexString: "2f3944")
+        
         emailLabel.font = UIFont.systemFont(ofSize: 14)
-        emailLabel.textColor = UIColor.gray
+        emailLabel.textColor = UIColor(hexString: "848ba7")
+        emailLabel.lineBreakMode = .byTruncatingTail
     }
     
     func setImage(_ img: UIImage) {

+ 11 - 3
deltachat-ios/ContactListController.swift

@@ -19,6 +19,9 @@ class ContactListController: UITableViewController {
         self.title = "Contacts"
         navigationController?.navigationBar.prefersLargeTitles = true
         
+        contactIds = Utils.getContactIds()
+        
+        tableView.rowHeight = 80
         tableView.register(ContactCell.self, forCellReuseIdentifier: contactCellReuseIdentifier)
     }
     
@@ -36,10 +39,12 @@ class ContactListController: UITableViewController {
     }
     
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        guard let cell:ContactCell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as? ContactCell else {
-            fatalError("shouldn't happen")
+        let cell:ContactCell
+        if let c = tableView.dequeueReusableCell(withIdentifier: "ChatCell") as? ContactCell {
+            cell = c
+        } else {
+            cell = ContactCell(style: .subtitle, reuseIdentifier: "ChatCell")
         }
-        
         let row = indexPath.row
         let contactRow = row
         
@@ -47,6 +52,9 @@ class ContactListController: UITableViewController {
         cell.nameLabel.text = contact.name
         cell.emailLabel.text = contact.email
         
+        // TODO: provider a nice selection
+        cell.selectionStyle = .none
+        
         if let img = contact.profileImage {
             cell.setImage(img)
         } else {

+ 0 - 72
deltachat-ios/ContactViewController.swift

@@ -1,72 +0,0 @@
-//
-//  ContactViewController.swift
-//  deltachat-ios
-//
-//  Created by Bastian van de Wetering on 08.11.17.
-//  Copyright © 2017 Jonas Reinsch. All rights reserved.
-//
-
-import UIKit
-
-class ContactViewController: UITableViewController {
-    var contactIds: [Int] = []
-    
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-
-        contactIds = Utils.getContactIds()
-        tableView.reloadData()
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        title = "Contacts"
-        navigationController?.navigationBar.prefersLargeTitles = true
-
-        let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(ContactViewController.addContact))
-        navigationItem.rightBarButtonItem = addButton
-    }
-    
-    @objc func addContact() {
-        let ncc = NewContactController()
-        let nav = UINavigationController(rootViewController: ncc)
-        present(nav, animated: true, completion: nil)
-    }
-    
-    override func didReceiveMemoryWarning() {
-        super.didReceiveMemoryWarning()
-        // Dispose of any resources that can be recreated.
-    }
-
-    
-    // MARK: - Table view data source
-    
-    override func numberOfSections(in tableView: UITableView) -> Int {
-        return 1
-    }
-    
-    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return contactIds.count
-    }
-    
-    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell:UITableViewCell
-        if let c = tableView.dequeueReusableCell(withIdentifier: String(describing: UITableViewCell.self)) {
-            cell = c
-        } else {
-            cell = UITableViewCell(style: .subtitle, reuseIdentifier: String(describing: UITableViewCell.self))
-        }
-        
-        let contact = MRContact(id: contactIds[indexPath.row])
-        
-        cell.textLabel?.text = contact.name
-        cell.detailTextLabel?.text = contact.email
-        
-        return cell
-    }
-}

+ 31 - 117
deltachat-ios/CredentialsController.swift

@@ -30,116 +30,30 @@ typealias CredentialsModel = (
     smtpSecurity:SecurityMode
 )
 
-class TextFieldCell:UITableViewCell {
-    let textField = UITextField()
-    
-    init(description: String, placeholder: String) {
-        super.init(style: .value1, reuseIdentifier: nil)
-        
-        textLabel?.text = "\(description):"
-        contentView.addSubview(textField)
-
-        textField.translatesAutoresizingMaskIntoConstraints = false
-        
-        // see: https://stackoverflow.com/a/35903650
-        // this makes the textField respect the trailing margin of
-        // the table view cell
-        let margins = contentView.layoutMarginsGuide
-        let trailing = margins.trailingAnchor
-        textField.trailingAnchor.constraint(equalTo: trailing).isActive = true
-        textField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
-        textField.textAlignment = .right
-
-        textField.placeholder = placeholder
-        
-        selectionStyle = .none
-        
-        textField.enablesReturnKeyAutomatically = true
-    }
-    
-    
-    override func setSelected(_ selected: Bool, animated: Bool) {
-        if selected {
-            textField.becomeFirstResponder()
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    static func makeEmailCell() -> TextFieldCell {
-        let emailCell = TextFieldCell(description: "Email", placeholder: "you@example.com")
-        
-        emailCell.textField.keyboardType = .emailAddress
-        // switch off quicktype
-        emailCell.textField.autocorrectionType = .no
-        emailCell.textField.autocapitalizationType = .none
-        
-        return emailCell
-    }
-    
-    static func makePasswordCell() -> TextFieldCell {
-        let passwordCell = TextFieldCell(description: "Password", placeholder: "your IMAP password")
-        
-        passwordCell.textField.textContentType = UITextContentType.password
-        passwordCell.textField.isSecureTextEntry = true
-        
-        return passwordCell
-    }
-    
-    static func makeNameCell() -> TextFieldCell {
-        let nameCell = TextFieldCell(description: "Name", placeholder: "new contacts nickname")
-        
-        nameCell.textField.autocapitalizationType = .words
-        nameCell.textField.autocorrectionType = .no
-        // .namePhonePad doesn't support autocapitalization
-        // see: https://stackoverflow.com/a/36365399
-        // therefore we use .default to capitalize the first character of the name
-        nameCell.textField.keyboardType = .default
-        
-        return nameCell
-    }
-    
-    static func makeConfigCell(label: String, placeholder: String) -> TextFieldCell {
-        let nameCell = TextFieldCell(description: label, placeholder: placeholder)
-        
-        nameCell.textField.autocapitalizationType = .words
-        nameCell.textField.autocorrectionType = .no
-        // .namePhonePad doesn't support autocapitalization
-        // see: https://stackoverflow.com/a/36365399
-        // therefore we use .default to capitalize the first character of the name
-        nameCell.textField.keyboardType = .default
-        
-        return nameCell
-    }
-    
-    
-}
 
 class CredentialsController: UITableViewController {
     let emailCell = TextFieldCell.makeEmailCell()
     let passwordCell = TextFieldCell.makePasswordCell()
-    
+
     let imapCellLoginName = TextFieldCell.makeConfigCell(label: "IMAP Login Name", placeholder: "Automatic")
     let imapCellServer = TextFieldCell.makeConfigCell(label: "IMAP Server", placeholder: "Automatic")
     let imapCellPort = TextFieldCell.makeConfigCell(label: "IMAP Port", placeholder: "Automatic")
     let imapCellSecurity = UITableViewCell(style: UITableViewCell.CellStyle.value1, reuseIdentifier: nil)
-    
+
     let smtpCellLoginName = TextFieldCell.makeConfigCell(label: "SMTP Login Name", placeholder: "Automatic")
     let smtpCellPassword = TextFieldCell.makeConfigCell(label: "SMTP Password", placeholder: "As above")
     let smtpCellServer = TextFieldCell.makeConfigCell(label: "SMTP Server", placeholder: "Automatic")
     let smtpCellPort = TextFieldCell.makeConfigCell(label: "SMTP Port", placeholder: "Automatic")
     let smtpCellSecurity = UITableViewCell(style: UITableViewCell.CellStyle.value1, reuseIdentifier: nil)
-    
+
     var doneButton:UIBarButtonItem?
     var advancedButton:UIBarButtonItem?
     let progressBar = UIProgressView(progressViewStyle: .default)
-    
+
     func readyForLogin() -> Bool {
         return Utils.isValid(model.email) && !model.password.isEmpty
     }
-    
+
     var advancedMode = false {
         didSet {
             if advancedMode {
@@ -162,10 +76,10 @@ class CredentialsController: UITableViewController {
             print(model)
         }
     }
-    
+
     let cells:[UITableViewCell]
     let isCancellable:Bool
-    
+
     init(isCancellable:Bool = false) {
         cells = [emailCell, passwordCell]
         self.isCancellable = isCancellable
@@ -174,17 +88,17 @@ class CredentialsController: UITableViewController {
         doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(didPressSaveAccountButton))
         doneButton?.isEnabled = false
         advancedButton = UIBarButtonItem(title: "Advanced", style: .done, target: self, action: #selector(didPressAdvancedButton))
-        
+
         let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didPressCancelButton))
-        
+
         if isCancellable {
             navigationItem.rightBarButtonItems = [doneButton!, cancelButton]
         } else {
             navigationItem.rightBarButtonItem = doneButton
         }
-        
+
         navigationItem.leftBarButtonItem = advancedButton
-        
+
         // FIXME: refactor: do not use target/action here for text field changes
         //        but text field delegate
         emailCell.textField.addTarget(self, action: #selector(emailTextChanged), for: UIControl.Event.editingChanged)
@@ -192,29 +106,29 @@ class CredentialsController: UITableViewController {
         imapCellLoginName.textField.addTarget(self, action: #selector(imapLoginNameChanged), for: .editingChanged)
         imapCellServer.textField.addTarget(self, action: #selector(imapServerChanged), for: .editingChanged)
         imapCellPort.textField.addTarget(self, action: #selector(imapPortChanged), for: .editingChanged)
-        
+
         smtpCellLoginName.textField.addTarget(self, action: #selector(smtpLoginNamedChanged), for: .editingChanged)
         smtpCellPassword.textField.addTarget(self, action: #selector(smtpPasswordChanged), for: .editingChanged)
         smtpCellServer.textField.addTarget(self, action: #selector(smtpServerChanged), for: .editingChanged)
         smtpCellPort.textField.addTarget(self, action: #selector(smtpPortChanged), for: .editingChanged)
-        
+
         emailCell.textField.textContentType = UITextContentType.emailAddress
         emailCell.textField.delegate = self
         passwordCell.textField.delegate = self
         emailCell.textField.returnKeyType = .next
         passwordCell.textField.returnKeyType = .done
     }
-    
+
     override func viewDidAppear(_ animated: Bool) {
         emailCell.textField.becomeFirstResponder()
     }
-    
+
     @objc func didPressCancelButton() {
         dismiss(animated: false) {
             AppDelegate.appCoordinator.setupInnerViewControllers()
         }
     }
-    
+
     @objc func didPressSaveAccountButton() {
         let m = model
         let a = advancedMode
@@ -222,26 +136,26 @@ class CredentialsController: UITableViewController {
             initCore(withCredentials: true, advancedMode: a, model: m, cancellableCredentialsUponFailure: self.isCancellable)
         }
     }
-    
+
     @objc func didPressAdvancedButton() {
         advancedMode = !advancedMode
     }
-    
+
     required init?(coder aDecoder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
-    
+
     override func viewDidLoad() {
         super.viewDidLoad()
-        
+
         title = "Settings"
         navigationController?.navigationBar.prefersLargeTitles = true
     }
-    
+
     override func numberOfSections(in tableView: UITableView) -> Int {
         return advancedMode ? 3 : 1
     }
-    
+
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         if section == 0 {
             return cells.count
@@ -254,7 +168,7 @@ class CredentialsController: UITableViewController {
         }
         return 0 // should never happen
     }
-    
+
     override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
         if section == 1 {
             return "IMAP"
@@ -264,7 +178,7 @@ class CredentialsController: UITableViewController {
         }
         return nil
     }
-    
+
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         let isIMAP = indexPath.section == 1
         let isSMTP = indexPath.section == 2
@@ -292,16 +206,16 @@ class CredentialsController: UITableViewController {
             self.present(actionSheet, animated: true, completion: {})
         }
     }
-    
+
 
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let section = indexPath.section
         let row = indexPath.row
-        
+
         if section == 0 {
             return cells[row]
         }
-        
+
         if section == 1 {
             if row == 0 {
                 return imapCellLoginName
@@ -338,7 +252,7 @@ class CredentialsController: UITableViewController {
             }
         }
         return UITableViewCell()
-        
+
     }
 
     override func didReceiveMemoryWarning() {
@@ -362,7 +276,7 @@ extension CredentialsController: UITextFieldDelegate {
                 self.didPressSaveAccountButton()
             }
         }
-        
+
         return true
     }
 }
@@ -370,12 +284,12 @@ extension CredentialsController: UITextFieldDelegate {
 extension CredentialsController {
     @objc func emailTextChanged() {
         let emailText = emailCell.textField.text ?? ""
-        
+
         model.email = emailText
     }
     @objc func passwordTextChanged() {
         let passwordText = passwordCell.textField.text ?? ""
-        
+
         model.password = passwordText
     }
     @objc func imapLoginNameChanged() {

+ 10 - 2
deltachat-ios/Info.plist

@@ -22,6 +22,16 @@
 	<string>5.1</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
+	<key>LSSupportsOpeningDocumentsInPlace</key>
+	<true/>
+	<key>NSCameraUsageDescription</key>
+	<string>Please allow access to camera</string>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>fetch</string>
+	</array>
+	<key>UIFileSharingEnabled</key>
+	<true/>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<key>UIRequiredDeviceCapabilities</key>
@@ -34,8 +44,6 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
-	<key>NSCameraUsageDescription</key>
-	<string>Please allow access to camera</string>
 	<key>UISupportedInterfaceOrientations~ipad</key>
 	<array>
 		<string>UIInterfaceOrientationPortrait</string>

+ 45 - 8
deltachat-ios/NavigationController.swift

@@ -9,6 +9,7 @@
 import UIKit
 
 final class NavigationController: UINavigationController {
+    var stateChangedObserver: Any?
     
     override var preferredStatusBarStyle: UIStatusBarStyle {
         return viewControllers.last?.preferredStatusBarStyle ?? .lightContent
@@ -18,34 +19,70 @@ final class NavigationController: UINavigationController {
         super.viewDidLoad()
         navigationBar.isTranslucent = false
         navigationBar.tintColor = .white
-        navigationBar.barTintColor = Constants.primaryColor
+        
         navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
+       
         if #available(iOS 11.0, *) {
+            navigationBar.prefersLargeTitles = true
             navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
+            navigationBar.barTintColor = Constants.primaryColor
+        } else {
+            navigationBar.setBackgroundImage(UIImage(), for: .default)
         }
-        navigationBar.shadowImage = UIImage()
-        navigationBar.setBackgroundImage(UIImage(), for: .default)
         view.backgroundColor = Constants.primaryColor
+        self.setShadow(nil)
+        
+        let nc = NotificationCenter.default
+        stateChangedObserver = nc.addObserver(
+            forName: dc_notificationStateChanged,
+            object: nil,
+            queue: nil) {
+                notification in
+                if let state = notification.userInfo?["state"] {
+                    self.setShadow(state as? String)
+                }
+        }
+    }
+    
+    private func setShadow(_ state: String?) {
+        switch state {
+        case "offline":
+            navigationBar.shadowImage = Constants.defaultShadow
+        case "online":
+            navigationBar.shadowImage = Constants.onlineShadow
+        default:
+            navigationBar.shadowImage = Constants.defaultShadow
+        }
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        
+        let nc = NotificationCenter.default
+        if let stateChangedObserver = self.stateChangedObserver {
+            nc.removeObserver(stateChangedObserver)
+        }
     }
     
     func setAppearanceStyle(to style: UIStatusBarStyle) {
+        self.setShadow(nil)
+        
         if style == .default {
-            navigationBar.shadowImage = UIImage()
-            navigationBar.barTintColor = Constants.primaryColor
             navigationBar.tintColor = .white
             navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
             if #available(iOS 11.0, *) {
+                navigationBar.prefersLargeTitles = true
                 navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
+                navigationBar.barTintColor = Constants.primaryColor
             }
         } else if style == .lightContent {
-            navigationBar.shadowImage = nil
-            navigationBar.barTintColor = .white
             navigationBar.tintColor = UIColor(red: 0, green: 0.5, blue: 1, alpha: 1)
             navigationBar.titleTextAttributes = [.foregroundColor: UIColor.black]
             if #available(iOS 11.0, *) {
+                navigationBar.prefersLargeTitles = true
                 navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.black]
+                navigationBar.barTintColor = .white
             }
         }
     }
-    
 }

+ 96 - 129
deltachat-ios/SettingsController.swift

@@ -9,149 +9,116 @@
 import UIKit
 import MessageKit
 import MessageInputBar
+import QuickTableViewController
+import JGProgressHUD
+
+final internal class SettingsViewController: QuickTableViewController {
+    let documentInteractionController = UIDocumentInteractionController()
 
-final internal class SettingsViewController: UITableViewController {
-    
     // MARK: - Properties
-    
+
     override var preferredStatusBarStyle: UIStatusBarStyle {
         return .lightContent
     }
-    
-    let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages"]
-    
-    // MARK: - Picker
-    
-    var messagesPicker = UIPickerView()
-    
-    @objc func onDoneWithPickerView() {
-        let selectedMessagesCount = messagesPicker.selectedRow(inComponent: 0)
-        view.endEditing(false)
-        tableView.reloadData()
-    }
-    
-    @objc func dismissPickerView() {
-        view.endEditing(false)
-    }
-    
-    private func configurePickerView() {
-        messagesPicker.dataSource = self
-        messagesPicker.delegate = self
-        messagesPicker.backgroundColor = .white
-        
-        messagesPicker.selectRow(0, inComponent: 0, animated: false)
-    }
-    
-    // MARK: - Toolbar
-    
-    var messagesToolbar = UIToolbar()
-    
-    private func configureToolbar() {
-        let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(onDoneWithPickerView))
-        let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
-        let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(dismissPickerView))
-        messagesToolbar.items = [cancelButton, spaceButton, doneButton]
-        messagesToolbar.sizeToFit()
-    }
-    
+
     // MARK: - View lifecycle
-    
+
     override func viewDidLoad() {
         super.viewDidLoad()
         title = "Settings"
-            
-        tableView.register(TextFieldTableViewCell.self, forCellReuseIdentifier: TextFieldTableViewCell.identifier)
-        tableView.tableFooterView = UIView()
-        configurePickerView()
-        configureToolbar()
-    }
-    
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-        if #available(iOS 11.0, *) {
-            navigationController?.navigationBar.prefersLargeTitles = true
-        }
-    }
-    
-    override func viewWillDisappear(_ animated: Bool) {
-        super.viewWillDisappear(animated)
-        if #available(iOS 11.0, *) {
-            navigationController?.navigationBar.prefersLargeTitles = false
-        }
-    }
-    
-    // MARK: - TableViewDelegate & TableViewDataSource
-    
-    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return cells.count
-    }
-    
-    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cellValue = cells[indexPath.row]
-        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell()
-        cell.textLabel?.text = cells[indexPath.row]
-        
-        switch cellValue {
-        case "Mock messages count":
-            return configureTextFieldTableViewCell(at: indexPath)
-        default:
-            let switchView = UISwitch(frame: .zero)
-            switchView.isOn = UserDefaults.standard.bool(forKey: cellValue)
-            switchView.tag = indexPath.row
-            switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
-            cell.accessoryView = switchView
-        }
-        return cell
-    }
-    
-    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        tableView.deselectRow(at: indexPath, animated: true)
-        
-        let cell = tableView.cellForRow(at: indexPath)
-        
-        cell?.contentView.subviews.forEach {
-            if $0 is UITextField {
-                $0.becomeFirstResponder()
-            }
-        }
+
+        documentInteractionController.delegate = self as? UIDocumentInteractionControllerDelegate
+
+        tableContents = [
+          Section(
+            title: "Basics",
+            rows: [
+              NavigationRow(title: "Email", subtitle: .rightAligned(MRConfig.addr ?? ""), action: { _ in }),
+              NavigationRow(title: "Password", subtitle: .rightAligned("********"), action: { _ in }),
+            ]),
+
+          Section(
+            title: "User Details",
+            rows: [
+              NavigationRow(title: "Display Name", subtitle: .rightAligned(MRConfig.displayname ?? ""), action: { _ in }),
+              NavigationRow(title: "Status", subtitle: .rightAligned(MRConfig.selfstatus ?? ""), action: { _ in }),
+            ]),
+
+          Section(
+            title: "Advanced",
+            rows: [
+              NavigationRow(title: "Server", subtitle: .rightAligned(MRConfig.mailServer ?? ""), action: { _ in }),
+              NavigationRow(title: "User", subtitle: .rightAligned(MRConfig.mailUser ?? ""), action: { _ in }),
+              NavigationRow(title: "Port", subtitle: .rightAligned(MRConfig.mailPort ?? ""), action: { _ in }),
+              NavigationRow(title: "Send Server", subtitle: .rightAligned(MRConfig.sendServer ?? ""), action: { _ in }),
+              NavigationRow(title: "Send User", subtitle: .rightAligned(MRConfig.sendUser ?? ""), action: { _ in }),
+              NavigationRow(title: "Send Port", subtitle: .rightAligned(MRConfig.sendPort ?? ""), action: { _ in }),
+              NavigationRow(title: "Send Password", subtitle: .rightAligned("********"), action: { _ in }),
+
+            ]),
+
+          Section(
+            title: "Flags",
+            rows: [
+              SwitchRow(title: "E2EE enabled", switchValue: MRConfig.e2eeEnabled, action: { _ in }),
+              SwitchRow(title: "MDNS enabled", switchValue: MRConfig.mdnsEnabled, action: { _ in }),
+              SwitchRow(title: "Watch Inbox", switchValue: MRConfig.inboxWatch, action: { _ in }),
+              SwitchRow(title: "Watch Sentbox", switchValue: MRConfig.sentboxWatch, action: { _ in }),
+              SwitchRow(title: "Watch Mvbox", switchValue: MRConfig.mvboxWatch, action: { _ in }),
+              SwitchRow(title: "Move to Mvbox", switchValue: MRConfig.mvboxMove, action: { _ in }),
+              SwitchRow(title: "Save Mime Headers", switchValue: MRConfig.saveMimeHeaders, action: { _ in }),
+            ]),
+
+          Section(
+            title: "Backups",
+            rows: [
+              TapActionRow(title: "Create backup", action: { [weak self] in self?.createBackup($0) }),
+              TapActionRow(title: "Restore from backup", action: { [weak self] in self?.restoreBackup($0) })
+            ]),
+        ]
     }
-    
-    // MARK: - Helper
-    
-    private func configureTextFieldTableViewCell(at indexPath: IndexPath) -> TextFieldTableViewCell {
-        if let cell = tableView.dequeueReusableCell(withIdentifier: TextFieldTableViewCell.identifier, for: indexPath) as? TextFieldTableViewCell {
-            cell.mainLabel.text = "Mock messages count:"
-            
-            let messagesCount = 0
-            cell.textField.text = "\(messagesCount)"
+
+    // MARK: - Actions
+
+    private func createBackup(_ sender: Row) {
+        if let documents = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.delta.chat.ios")?.path {
+            print("create backup in", documents)
+            dc_imex(mailboxPointer, DC_IMEX_EXPORT_BACKUP, documents, nil)
             
-            cell.textField.inputView = messagesPicker
-            cell.textField.inputAccessoryView = messagesToolbar
+            let hud = JGProgressHUD(style: .dark)
+            hud.textLabel.text = "Creating Backup"
+            hud.show(in: self.view)
             
-            return cell
+            // TODO: dismiss when actually done
+            hud.dismiss(afterDelay: 2.0)
         }
-        return TextFieldTableViewCell()
     }
-    
-    @objc func switchChanged(_ sender: UISwitch!) {
-        let cell = cells[sender.tag]
-        
-        UserDefaults.standard.set(sender.isOn, forKey: cell)
-    }
-}
 
-// MARK: - UIPickerViewDelegate, UIPickerViewDataSource
-extension SettingsViewController: UIPickerViewDelegate, UIPickerViewDataSource {
-    
-    func numberOfComponents(in pickerView: UIPickerView) -> Int {
-        return 1
-    }
-    
-    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
-        return 100
-    }
-    
-    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
-        return "\(row)"
+    private func restoreBackup(_ sender: Row) {
+        if let documents = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.delta.chat.ios")?.path {
+            print("looking for backup in", documents)
+
+            if let file = dc_imex_has_backup(mailboxPointer, documents) {
+                // Close as we are resetting the world
+                dc_close(mailboxPointer)
+
+                mailboxPointer = dc_context_new(callback_ios, nil, "iOS")
+                guard mailboxPointer != nil else {
+                    fatalError("Error: dc_context_new returned nil")
+                }
+
+                dc_imex(mailboxPointer, DC_IMEX_IMPORT_BACKUP, file, nil)
+                
+                let hud = JGProgressHUD(style: .dark)
+                hud.textLabel.text = "Restoring Backup"
+                hud.show(in: self.view)
+                
+                // TODO: dismiss when actually done
+                hud.dismiss(afterDelay: 2.0)
+            } else {
+                let alert = UIAlertController(title: "Can not restore", message: "No Backup found", preferredStyle: .alert)
+                self.present(alert, animated: true, completion: nil)
+            }
+        }
     }
 }

+ 94 - 0
deltachat-ios/TextFieldCell.swift

@@ -0,0 +1,94 @@
+//
+//  TextFieldCell.swift
+//  deltachat-ios
+//
+//  Created by Friedel Ziegelmayer on 27.12.18.
+//  Copyright © 2018 Jonas Reinsch. All rights reserved.
+//
+
+import UIKit
+
+class TextFieldCell:UITableViewCell {
+    let textField = UITextField()
+    
+    init(description: String, placeholder: String) {
+        super.init(style: .value1, reuseIdentifier: nil)
+        
+        textLabel?.text = "\(description):"
+        contentView.addSubview(textField)
+        
+        textField.translatesAutoresizingMaskIntoConstraints = false
+        
+        // see: https://stackoverflow.com/a/35903650
+        // this makes the textField respect the trailing margin of
+        // the table view cell
+        let margins = contentView.layoutMarginsGuide
+        let trailing = margins.trailingAnchor
+        textField.trailingAnchor.constraint(equalTo: trailing).isActive = true
+        textField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
+        textField.textAlignment = .right
+        
+        textField.placeholder = placeholder
+        
+        selectionStyle = .none
+        
+        textField.enablesReturnKeyAutomatically = true
+    }
+    
+    
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        if selected {
+            textField.becomeFirstResponder()
+        }
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    static func makeEmailCell() -> TextFieldCell {
+        let emailCell = TextFieldCell(description: "Email", placeholder: "you@example.com")
+        
+        emailCell.textField.keyboardType = .emailAddress
+        // switch off quicktype
+        emailCell.textField.autocorrectionType = .no
+        emailCell.textField.autocapitalizationType = .none
+        
+        return emailCell
+    }
+    
+    static func makePasswordCell() -> TextFieldCell {
+        let passwordCell = TextFieldCell(description: "Password", placeholder: "your IMAP password")
+        
+        passwordCell.textField.textContentType = UITextContentType.password
+        passwordCell.textField.isSecureTextEntry = true
+        
+        return passwordCell
+    }
+    
+    static func makeNameCell() -> TextFieldCell {
+        let nameCell = TextFieldCell(description: "Name", placeholder: "new contacts nickname")
+        
+        nameCell.textField.autocapitalizationType = .words
+        nameCell.textField.autocorrectionType = .no
+        // .namePhonePad doesn't support autocapitalization
+        // see: https://stackoverflow.com/a/36365399
+        // therefore we use .default to capitalize the first character of the name
+        nameCell.textField.keyboardType = .default
+        
+        return nameCell
+    }
+    
+    static func makeConfigCell(label: String, placeholder: String) -> TextFieldCell {
+        let nameCell = TextFieldCell(description: label, placeholder: placeholder)
+        
+        nameCell.textField.autocapitalizationType = .words
+        nameCell.textField.autocorrectionType = .no
+        // .namePhonePad doesn't support autocapitalization
+        // see: https://stackoverflow.com/a/36365399
+        // therefore we use .default to capitalize the first character of the name
+        nameCell.textField.keyboardType = .default
+        
+        return nameCell
+    }
+}

+ 10 - 0
deltachat-ios/UIImage+Extension.swift

@@ -21,5 +21,15 @@ extension UIImage {
         let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
         return scaledImage!
     }
+    public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
+        let rect = CGRect(origin: .zero, size: size)
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
+        color.setFill()
+        UIRectFill(rect)
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
     
+        guard let cgImage = image?.cgImage else { return nil }
+        self.init(cgImage: cgImage)
+    }
 }

+ 343 - 45
deltachat-ios/Wrapper.swift

@@ -11,7 +11,7 @@ import UIKit
 
 class MRContact {
     private var contactPointer: UnsafeMutablePointer<dc_contact_t>
-    
+
     var name: String {
         if contactPointer.pointee.name == nil {
             return email
@@ -22,22 +22,22 @@ class MRContact {
         }
         return name
     }
-    
+
     var email: String {
         if contactPointer.pointee.addr == nil {
             return "error: no email in contact"
         }
         return String(cString: contactPointer.pointee.addr)
     }
-    
+
     var isVerified: Bool {
         return dc_contact_is_verified(contactPointer) == 1
     }
-    
+
     var isBlocked: Bool {
         return dc_contact_is_blocked(contactPointer) == 1
     }
-    
+
     lazy var profileImage: UIImage? = { [unowned self] in
         let file = dc_contact_get_profile_image(contactPointer)
         if let cFile = file {
@@ -55,22 +55,22 @@ class MRContact {
             }
             return nil
         }
-        
+
         return nil
     }()
-    
+
     var color: UIColor {
         return UIColor(netHex: Int(dc_contact_get_color(contactPointer)))
     }
-    
+
     var id: Int {
         return Int(contactPointer.pointee.id)
     }
-    
+
     init(id: Int) {
         contactPointer = dc_get_contact(mailboxPointer, UInt32(id))
     }
-    
+
     deinit {
         dc_contact_unref(contactPointer)
     }
@@ -79,36 +79,36 @@ class MRContact {
 
 class MRMessage {
     private var messagePointer: UnsafeMutablePointer<dc_msg_t>
-    
+
     var id: Int {
         return Int(messagePointer.pointee.id)
     }
-    
+
     var fromContactId: Int {
         return Int(messagePointer.pointee.from_id)
     }
-    
+
     lazy var fromContact: MRContact = {
         return MRContact(id: fromContactId)
     }()
-    
+
     var toContactId: Int {
         return Int(messagePointer.pointee.to_id)
     }
-    
+
     var chatId: Int {
         return Int(messagePointer.pointee.chat_id)
     }
-    
+
     var text: String? {
         return String(cString: messagePointer.pointee.text)
     }
-    
+
     var mimeType: String? {
         guard let result = dc_msg_get_filemime(messagePointer) else { return nil }
         return String(cString: result)
     }
-    
+
     lazy var image: UIImage? = { [unowned self] in
         let filetype = dc_msg_get_viewtype(messagePointer)
         let file = dc_msg_get_file(messagePointer)
@@ -130,12 +130,12 @@ class MRMessage {
             return nil
         }
     }()
-    
+
     // MR_MSG_*
     var type: Int {
         return Int(messagePointer.pointee.type)
     }
-    
+
     // MR_STATE_*
     var state: Int {
         return Int(messagePointer.pointee.state)
@@ -155,128 +155,426 @@ class MRMessage {
             return "Unknown"
         }
     }
-    
+
     var timestamp: Int64 {
         return Int64(messagePointer.pointee.timestamp)
     }
-    
+
     init(id: Int) {
         messagePointer = dc_get_msg(mailboxPointer, UInt32(id))
     }
-    
+
     func summary(chars: Int) -> String? {
         guard let result = dc_msg_get_summarytext(messagePointer, Int32(chars)) else { return nil }
 
         return String(cString: result)
     }
-    
+
     deinit {
         dc_msg_unref(messagePointer)
     }
 }
 
 class MRChat {
-    
+
     var chatPointer: UnsafeMutablePointer<dc_chat_t>
-    
+
     var id: Int {
         return Int(chatPointer.pointee.id)
     }
-    
+
     var name: String {
         if chatPointer.pointee.name == nil {
             return "Error - no name"
         }
         return String(cString: chatPointer.pointee.name)
     }
-    
+
     var type: Int {
         return Int(chatPointer.pointee.type)
     }
-    
+
     var color: UIColor {
         return UIColor(netHex: Int(dc_chat_get_color(chatPointer)))
     }
 
+    lazy var profileImage: UIImage? = { [unowned self] in
+        let file = dc_chat_get_profile_image(chatPointer)
+        if let cFile = file {
+            let filename = String(cString: cFile)
+            let path: URL = URL.init(fileURLWithPath: filename, isDirectory: false)
+            if path.isFileURL {
+                do {
+                    let data = try Data(contentsOf: path)
+                    let image = UIImage(data: data)
+                    return image
+                } catch {
+                    print("failed to load image", error, filename)
+                    return nil
+                }
+            }
+            return nil
+        }
+
+        return nil
+    }()
+
     init(id: Int) {
         chatPointer = dc_get_chat(mailboxPointer, UInt32(id))
     }
-    
+
     deinit {
         dc_chat_unref(chatPointer)
     }
 }
 
 class MRPoorText {
-    
+
     private var poorTextPointer: UnsafeMutablePointer<dc_lot_t>
-    
+
     var text1: String? {
         if poorTextPointer.pointee.text1 == nil {
             return nil
         }
         return String(cString: poorTextPointer.pointee.text1)
     }
-    
+
     var text2: String? {
         if poorTextPointer.pointee.text2 == nil {
             return nil
         }
         return String(cString: poorTextPointer.pointee.text2)
     }
-    
+
     var text1Meaning: Int {
         return Int(poorTextPointer.pointee.text1_meaning)
     }
-    
+
     var timeStamp: Int {
         return Int(poorTextPointer.pointee.timestamp)
     }
-    
+
     var state: Int {
         return Int(poorTextPointer.pointee.state)
     }
-    
+
     // takes ownership of specified pointer
     init(poorTextPointer: UnsafeMutablePointer<dc_lot_t>) {
         self.poorTextPointer = poorTextPointer
     }
-    
+
     deinit {
         dc_lot_unref(poorTextPointer)
     }
 }
 
 class MRChatList {
-    
+
     private var chatListPointer: UnsafeMutablePointer<dc_chatlist_t>
-    
+
     var length: Int {
         return dc_chatlist_get_cnt(chatListPointer)
         //return Int(chatListPointer.pointee.m_cnt)
     }
-    
+
     // takes ownership of specified pointer
     init(chatListPointer: UnsafeMutablePointer<dc_chatlist_t>) {
         self.chatListPointer = chatListPointer
     }
-    
+
     func getChatId(index: Int) -> Int {
         return Int(dc_chatlist_get_chat_id(self.chatListPointer, index))
     }
-    
+
     func getMessageId(index: Int) -> Int {
         return Int(dc_chatlist_get_msg_id(self.chatListPointer, index))
     }
-    
+
     func summary(index: Int) -> MRPoorText {
         guard let poorTextPointer = dc_chatlist_get_summary(self.chatListPointer, index, nil) else {
             fatalError("poor text pointer was nil")
         }
         return MRPoorText(poorTextPointer: poorTextPointer)
     }
-    
+
     deinit {
         dc_chatlist_unref(chatListPointer)
     }
 }
+
+func strToBool(_ value: String?) -> Bool {
+    if let vStr = value {
+        if let vInt = Int(vStr) {
+            return vInt == 1
+        }
+        return false
+    }
+
+    return false
+}
+
+class MRConfig {
+    class private func getOptStr(_ key: String) -> String? {
+        let p = dc_get_config(mailboxPointer, key)
+
+        if let pSafe = p {
+            return String(cString: pSafe)
+        }
+
+        return nil
+    }
+
+    class private func setOptStr(_ key: String, _ value: String?) {
+        if let v = value {
+            dc_set_config(mailboxPointer, key, v)
+        }
+    }
+
+    class private func getBool(_ key: String) -> Bool {
+        return strToBool(self.getOptStr(key))
+    }
+
+    class private func setBool(_ key: String, _ value: Bool) {
+        let vStr = value ? "1" : "0"
+        self.setOptStr(key, vStr)
+    }
+
+    /**
+     *  Address to display (always needed)
+     */
+    class var addr: String? {
+        set {
+            setOptStr("addr", newValue)
+        }
+        get {
+            return getOptStr("addr")
+        }
+    }
+
+    /**
+     *  IMAP-server, guessed if left out
+     */
+    class var mailServer: String? {
+        set {
+            setOptStr("mail_server", newValue)
+        }
+        get {
+            return getOptStr("mail_server")
+        }
+    }
+
+    /**
+     *  IMAP-username, guessed if left out
+     */
+    class var mailUser: String? {
+        set {
+            setOptStr("mail_user", newValue)
+        }
+        get {
+            return getOptStr("mail_user")
+        }
+    }
+
+    /**
+     *  IMAP-password (always needed)
+     */
+    class var mailPw: String? {
+        set {
+            setOptStr("mail_pw", newValue)
+        }
+        get {
+            return getOptStr("mail_pw")
+        }
+    }
+
+    /**
+     *  IMAP-port, guessed if left out
+     */
+    class var mailPort: String? {
+        set {
+            setOptStr("mail_port", newValue)
+        }
+        get {
+            return getOptStr("mail_port")
+        }
+    }
+
+    /**
+     *  SMTP-server, guessed if left out
+     */
+    class var sendServer: String? {
+        set {
+            setOptStr("send_server", newValue)
+        }
+        get {
+            return getOptStr("send_server")
+        }
+    }
+    /**
+     *  SMTP-user, guessed if left out
+     */
+    class var sendUser: String? {
+        set {
+            setOptStr("send_user", newValue)
+        }
+        get {
+            return getOptStr("send_user")
+        }
+    }
+
+    /**
+     *  SMTP-password, guessed if left out
+     */
+    class var sendPw: String? {
+        set {
+            setOptStr("send_pw", newValue)
+        }
+        get {
+            return getOptStr("send_pw")
+        }
+    }
+
+    /**
+     * SMTP-port, guessed if left out
+     */
+    class var sendPort: String? {
+        set {
+            setOptStr("send_port", newValue)
+        }
+        get {
+            return getOptStr("send_port")
+        }
+    }
+
+    /**
+     * IMAP-/SMTP-flags as a combination of DC_LP flags, guessed if left out
+     */
+    class var serverFlags: String? {
+        set {
+            setOptStr("server_flags", newValue)
+        }
+        get {
+            return getOptStr("server_flags")
+        }
+    }
+
+    /**
+     * Own name to use when sending messages. MUAs are allowed to spread this way eg. using CC, defaults to empty
+     */
+    class var displayname: String? {
+        set {
+            setOptStr("displayname", newValue)
+        }
+        get {
+            return getOptStr("displayname")
+        }
+    }
+
+    /**
+     * Own status to display eg. in email footers, defaults to a standard text
+     */
+    class var selfstatus: String? {
+        set {
+            setOptStr("selfstatus", newValue)
+        }
+        get {
+            return getOptStr("selfstatus")
+        }
+    }
+
+    /**
+     * File containing avatar. Will be copied to blob directory. NULL to remove the avatar. It is planned for future versions to send this image together with the next messages.
+     */
+    class var selfavatar: String? {
+        set {
+            setOptStr("selfavatar", newValue)
+        }
+        get {
+            return getOptStr("selfavatar")
+        }
+    }
+
+    /**
+     * 0=no end-to-end-encryption, 1=prefer end-to-end-encryption (default)
+     */
+    class var e2eeEnabled: Bool {
+        set {
+            setBool("e2ee_enabled", newValue)
+        }
+        get {
+            return getBool("e2ee_enabled")
+        }
+    }
+
+    /**
+     * 0=do not send or request read receipts, 1=send and request read receipts (default)
+     */
+    class var mdnsEnabled: Bool {
+        set {
+            setBool("mdns_enabled", newValue)
+        }
+        get {
+            return getBool("mdns_enabled")
+        }
+    }
+
+    /**
+     * 1=watch INBOX-folder for changes (default), 0=do not watch the INBOX-folder
+     */
+    class var inboxWatch: Bool {
+        set {
+            setBool("inbox_watch", newValue)
+        }
+        get {
+            return getBool("inbox_watch")
+        }
+    }
+
+    /**
+     * 1=watch Sent-folder for changes (default), 0=do not watch the Sent-folder
+     */
+    class var sentboxWatch: Bool {
+        set {
+            setBool("sentbox_watch", newValue)
+        }
+        get {
+            return getBool("sentbox_watch")
+        }
+    }
+
+    /**
+     * 1=watch DeltaChat-folder for changes (default), 0=do not watch the DeltaChat-folder
+     */
+    class var mvboxWatch: Bool {
+        set {
+            setBool("mvbox_watch", newValue)
+        }
+        get {
+            return getBool("mvbox_watch")
+        }
+    }
+
+    /**
+     * 1=heuristically detect chat-messages and move them to the DeltaChat-folder, 0=do not move chat-messages
+     */
+    class var mvboxMove: Bool {
+        set {
+            setBool("mvbox_move", newValue)
+        }
+        get {
+            return getBool("mvbox_move")
+        }
+    }
+
+    /**
+     * 1=save mime headers and make dc_get_mime_headers() work for subsequent calls, 0=do not save mime headers (default)
+     */
+    class var saveMimeHeaders: Bool {
+        set {
+            setBool("save_mime_headers", newValue)
+        }
+        get {
+            return getBool("save_mime_headers")
+        }
+    }
+}

+ 12 - 0
deltachat-ios/deltachat-ios.entitlements

@@ -0,0 +1,12 @@
+<?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>aps-environment</key>
+	<string>development</string>
+	<key>com.apple.security.application-groups</key>
+	<array>
+		<string>group.delta.chat.ios</string>
+	</array>
+</dict>
+</plist>

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