فهرست منبع

add SDWebImageSVGKitPlugin

cyberta 3 سال پیش
والد
کامیت
45ab8a6838
100فایلهای تغییر یافته به همراه15854 افزوده شده و 1425 حذف شده
  1. 2 0
      Podfile
  2. 27 2
      Podfile.lock
  3. 14 0
      Pods/CocoaLumberjack/LICENSE
  4. 286 0
      Pods/CocoaLumberjack/README.md
  5. 57 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m
  6. 205 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m
  7. 133 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m
  8. 683 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m
  9. 31 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h
  10. 1800 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m
  11. 1308 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m
  12. 21 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m
  13. 118 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m
  14. 1492 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m
  15. 57 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m
  16. 185 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m
  17. 269 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m
  18. 204 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m
  19. 111 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m
  20. 104 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h
  21. 75 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h
  22. 54 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h
  23. 46 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h
  24. 63 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h
  25. 127 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h
  26. 30 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h
  27. 119 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h
  28. 117 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h
  29. 228 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h
  30. 27 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h
  31. 530 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h
  32. 82 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h
  33. 927 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h
  34. 101 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h
  35. 30 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h
  36. 60 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h
  37. 55 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h
  38. 186 0
      Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h
  39. 63 0
      Pods/Local Podspecs/SVGKit.podspec.json
  40. 27 2
      Pods/Manifest.lock
  41. 2359 1421
      Pods/Pods.xcodeproj/project.pbxproj
  42. 19 0
      Pods/SDWebImageSVGKitPlugin/LICENSE
  43. 144 0
      Pods/SDWebImageSVGKitPlugin/README.md
  44. 21 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDImageSVGKCoder.h
  45. 130 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDImageSVGKCoder.m
  46. 37 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDSVGKImage.h
  47. 146 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDSVGKImage.m
  48. 34 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDWebImageSVGKitDefine.h
  49. 156 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDWebImageSVGKitDefine.m
  50. 169 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SVGKImageView+WebCache.h
  51. 102 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SVGKImageView+WebCache.m
  52. 19 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Module/SDWebImageSVGKitPlugin.h
  53. 6 0
      Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Module/SDWebImageSVGKitPlugin.modulemap
  54. 8 0
      Pods/SVGKit/LICENSE
  55. 122 0
      Pods/SVGKit/README.mdown
  56. 6 0
      Pods/SVGKit/SVGKitLibrary/SVGKit-iOS/SVGKit.modulemap
  57. 43 0
      Pods/SVGKit/Source/AppKit additions/SVGKImageRep.h
  58. 293 0
      Pods/SVGKit/Source/AppKit additions/SVGKImageRep.m
  59. 42 0
      Pods/SVGKit/Source/DOM classes/Core DOM/AppleSucksDOMImplementation.h
  60. 33 0
      Pods/SVGKit/Source/DOM classes/Core DOM/AppleSucksDOMImplementation.m
  61. 37 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Attr.h
  62. 54 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Attr.m
  63. 18 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CDATASection.h
  64. 20 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CDATASection.m
  65. 102 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue.h
  66. 296 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue.m
  67. 7 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue_ConfigurablePixelsPerInch.h
  68. 45 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSRule.h
  69. 12 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSRule.m
  70. 7 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList+Mutable.h
  71. 18 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList.h
  72. 33 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList.m
  73. 49 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleDeclaration.h
  74. 165 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleDeclaration.m
  75. 24 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleRule.h
  76. 37 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleRule.m
  77. 31 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleSheet.h
  78. 92 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleSheet.m
  79. 35 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue.h
  80. 25 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue.m
  81. 17 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSValueList.h
  82. 47 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSValueList.m
  83. 7 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue_ForSubclasses.h
  84. 48 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CharacterData.h
  85. 41 0
      Pods/SVGKit/Source/DOM classes/Core DOM/CharacterData.m
  86. 21 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Comment.h
  87. 21 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Comment.m
  88. 1 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DOMGlobalSettings.h
  89. 29 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DOMHelperUtilities.h
  90. 87 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DOMHelperUtilities.m
  91. 7 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Document+Mutable.h
  92. 111 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Document.h
  93. 111 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Document.m
  94. 19 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentCSS.h
  95. 17 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentFragment.h
  96. 21 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentFragment.m
  97. 17 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentStyle.h
  98. 39 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentType.h
  99. 44 0
      Pods/SVGKit/Source/DOM classes/Core DOM/DocumentType.m
  100. 99 0
      Pods/SVGKit/Source/DOM classes/Core DOM/Element.h

+ 2 - 0
Podfile

@@ -16,6 +16,8 @@ target 'deltachat-ios' do
   pod 'SCSiriWaveformView'
   pod 'SDWebImage', '~> 5.9.1'
   pod 'SDWebImageWebPCoder'
+  pod 'SDWebImageSVGKitPlugin'
+  pod 'SVGKit', :git => 'https://github.com/SVGKit/SVGKit.git', :branch => '3.x'
   target 'deltachat-iosTests' do
     inherit! :search_paths
     # Pods for testing

+ 27 - 2
Podfile.lock

@@ -1,4 +1,7 @@
 PODS:
+  - CocoaLumberjack (3.7.2):
+    - CocoaLumberjack/Core (= 3.7.2)
+  - CocoaLumberjack/Core (3.7.2)
   - DBDebugToolkit (0.6.0)
   - InputBarAccessoryView (4.3.1):
     - InputBarAccessoryView/Core (= 4.3.1)
@@ -17,9 +20,14 @@ PODS:
   - SDWebImage (5.9.5):
     - SDWebImage/Core (= 5.9.5)
   - SDWebImage/Core (5.9.5)
+  - SDWebImageSVGKitPlugin (1.2.0):
+    - SDWebImage/Core (~> 5.6)
+    - SVGKit (>= 2.1)
   - SDWebImageWebPCoder (0.6.1):
     - libwebp (~> 1.0)
     - SDWebImage/Core (~> 5.7)
+  - SVGKit (3.1.0):
+    - CocoaLumberjack (~> 3.0)
   - SwiftFormat/CLI (0.40.9)
   - SwiftLint (0.43.1)
   - SwiftyBeaver (1.9.4)
@@ -30,36 +38,53 @@ DEPENDENCIES:
   - ReachabilitySwift
   - SCSiriWaveformView
   - SDWebImage (~> 5.9.1)
+  - SDWebImageSVGKitPlugin
   - SDWebImageWebPCoder
+  - SVGKit (from `https://github.com/SVGKit/SVGKit.git`, branch `3.x`)
   - SwiftFormat/CLI
   - SwiftLint
   - SwiftyBeaver
 
 SPEC REPOS:
   trunk:
+    - CocoaLumberjack
     - DBDebugToolkit
     - InputBarAccessoryView
     - libwebp
     - ReachabilitySwift
     - SCSiriWaveformView
     - SDWebImage
+    - SDWebImageSVGKitPlugin
     - SDWebImageWebPCoder
     - SwiftFormat
     - SwiftLint
     - SwiftyBeaver
 
+EXTERNAL SOURCES:
+  SVGKit:
+    :branch: 3.x
+    :git: https://github.com/SVGKit/SVGKit.git
+
+CHECKOUT OPTIONS:
+  SVGKit:
+    :commit: e9bc636845fb4d6d5d8e25a9bd08e75c5cf92de9
+    :git: https://github.com/SVGKit/SVGKit.git
+
 SPEC CHECKSUMS:
+  CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
   DBDebugToolkit: 03eb3483da4daf03b853b83ade5a8da9c8b7aff4
   InputBarAccessoryView: 58a348be7ea2736c7eec60e5c315511c2dbb39fd
   libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
   ReachabilitySwift: 4032e2f59586e11e3b0ebe15b167abdd587a388b
   SCSiriWaveformView: 15b9dd6f94c7536e2032b34a35c6ff74d38c7411
   SDWebImage: 0b2ba0d56479bf6a45ecddbfd5558bea93150d25
+  SDWebImageSVGKitPlugin: 06a811c05b9e839982baeb5251d15fe7a79abb82
   SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
+  SVGKit: 3c8468aab0026048532a3b27a0c81cdd939f0649
   SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
   SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52
   SwiftyBeaver: 576177b2c5c94b3aedd5993914e91271a0524e88
 
-PODFILE CHECKSUM: bda1eaa52c3523837e45c30f0792783dc02e1de9
+PODFILE CHECKSUM: 632fce258414c6c6231891ec0ba5fb604075674b
 
-COCOAPODS: 1.10.1
+COCOAPODS: 1.11.2

+ 14 - 0
Pods/CocoaLumberjack/LICENSE

@@ -0,0 +1,14 @@
+BSD 3-Clause License
+
+Copyright (c) 2010-2021, Deusty, LLC
+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.
+
+3. Neither the name of Deusty nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of Deusty, LLC.
+
+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.

+ 286 - 0
Pods/CocoaLumberjack/README.md

@@ -0,0 +1,286 @@
+<p align="center" >
+  <img src="https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/LumberjackLogo.png" title="Lumberjack logo" float=left>
+</p>
+
+CocoaLumberjack
+===============
+![Unit Tests](https://github.com/CocoaLumberjack/CocoaLumberjack/workflows/Unit%20Tests/badge.svg)
+[![Pod Version](http://img.shields.io/cocoapods/v/CocoaLumberjack.svg?style=flat)](http://cocoadocs.org/docsets/CocoaLumberjack/)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+[![Pod Platform](http://img.shields.io/cocoapods/p/CocoaLumberjack.svg?style=flat)](http://cocoadocs.org/docsets/CocoaLumberjack/)
+[![Pod License](http://img.shields.io/cocoapods/l/CocoaLumberjack.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause)
+[![codecov](https://codecov.io/gh/CocoaLumberjack/CocoaLumberjack/branch/master/graph/badge.svg)](https://codecov.io/gh/CocoaLumberjack/CocoaLumberjack)
+[![codebeat badge](https://codebeat.co/badges/840b714a-c8f3-4936-ada4-363473cd4e6b)](https://codebeat.co/projects/github-com-cocoalumberjack-cocoalumberjack-master)
+
+
+**CocoaLumberjack** is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS.
+
+## How to get started
+
+First, install CocoaLumberjack via [CocoaPods](http://cocoapods.org), [Carthage](https://github.com/Carthage/Carthage), [Swift Package Manager](https://swift.org/package-manager/) or manually.
+Then use `DDOSLogger` for iOS 10 and later, or `DDTTYLogger` and `DDASLLogger` for earlier versions to begin logging messages.
+
+### CocoaPods
+
+```ruby
+platform :ios, '9.0'
+
+target 'SampleTarget' do
+  use_frameworks!
+  pod 'CocoaLumberjack/Swift'
+end
+```
+Note: `Swift` is a subspec which will include all the Obj-C code plus the Swift one, so this is sufficient.
+For more details about how to use Swift with Lumberjack, see [this conversation](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/405).
+
+For Objective-C use the following:
+```ruby
+platform :ios, '9.0'
+
+target 'SampleTarget' do
+    pod 'CocoaLumberjack'
+end
+```
+
+### Carthage
+
+Carthage is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods.
+
+To install with Carthage, follow the instruction on [Carthage](https://github.com/Carthage/Carthage)
+
+Cartfile
+```
+github "CocoaLumberjack/CocoaLumberjack"
+```
+
+
+### Swift Package Manager
+
+As of CocoaLumberjack 3.6.0, you can use the Swift Package Manager as integration method.
+If you want to use the Swift Package Manager as integration method, either use Xcode to add the package dependency or add the following dependency to your Package.swift:
+
+```swift
+.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", from: "3.7.0"),
+```
+
+Note that you may need to add both products, `CocoaLumberjack` and `CocoaLumberjackSwift` to your target since SPM sometimes fails to detect that `CocoaLumerjackSwift` depends on `CocoaLumberjack`.
+
+### Install manually
+
+If you want to install CocoaLumberjack manually, read the [manual installation](https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/Documentation/GettingStarted.md#manual-installation) guide for more information.
+
+### Swift Usage
+
+Usually, you can simply `import CocoaLumberjackSwift`. If you installed CocoaLumberjack using CocoaPods, you need to use `import CocoaLumberjack` instead.
+
+```swift
+DDLog.add(DDOSLogger.sharedInstance) // Uses os_log
+
+let fileLogger: DDFileLogger = DDFileLogger() // File Logger
+fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
+fileLogger.logFileManager.maximumNumberOfLogFiles = 7
+DDLog.add(fileLogger)
+
+...
+
+DDLogVerbose("Verbose")
+DDLogDebug("Debug")
+DDLogInfo("Info")
+DDLogWarn("Warn")
+DDLogError("Error")
+```
+
+### Obj-C usage
+
+If you're using Lumberjack as a framework, you can `@import CocoaLumberjack;`.
+Otherwise, `#import <CocoaLumberjack/CocoaLumberjack.h>`
+
+```objc
+[DDLog addLogger:[DDOSLogger sharedInstance]]; // Uses os_log
+
+DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // File Logger
+fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
+fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
+[DDLog addLogger:fileLogger];
+
+...
+
+DDLogVerbose(@"Verbose");
+DDLogDebug(@"Debug");
+DDLogInfo(@"Info");
+DDLogWarn(@"Warn");
+DDLogError(@"Error");
+```
+### Objective-C ARC Semantic Issue
+
+When integrating Lumberjack into an existing Objective-C it is possible to run into `Multiple methods named 'tag' found with mismatched result, parameter type or attributes` build error.
+
+Add `#define DD_LEGACY_MESSAGE_TAG 0` before importing CocoaLumberjack or add `#define DD_LEGACY_MESSAGE_TAG 0` or add `-DDD_LEGACY_MESSAGE_TAG=0` to *Other C Flags*/*OTHER_CFLAGS* in your Xcode project.
+
+## [swift-log](https://github.com/apple/swift-log) backend
+
+CocoaLumberjack also ships with a backend implementation for [swift-log](https://github.com/apple/swift-log).
+Simply add CocoaLumberjack as dependency to your SPM target (see above) and also add the `CocoaLumberjackSwiftLogBackend` product as dependency to your target.
+
+You can then use `DDLogHandler` as backend for swift-log, which will forward all messages to CocoaLumberjack's `DDLog`. You will still configure the loggers and log formatters you want via `DDLog`, but writing log messages will be done using `Logger` from swift-log.
+
+In your own log formatters, you can make use of the `swiftLogInfo` property on `DDLogMessage` to retrieve the details of a message that is logged via swift-log.
+
+
+### More information
+
+- read the [Getting started](https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/Documentation/GettingStarted.md) guide, check out the [FAQ](https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/Documentation/FAQ.md) section or the other [docs](Documentation/)
+- if you find issues or want to suggest improvements, create an issue or a pull request
+- for all kinds of questions involving CocoaLumberjack, use the [Google group](http://groups.google.com/group/cocoalumberjack) or StackOverflow (use [#lumberjack](http://stackoverflow.com/questions/tagged/lumberjack)).
+
+
+## CocoaLumberjack 3
+
+### Migrating to 3.x
+
+* To be determined
+
+## Features
+
+### Lumberjack is Fast & Simple, yet Powerful & Flexible.
+
+It is similar in concept to other popular logging frameworks such as log4j, yet is designed specifically for Objective-C, and takes advantage of features such as multi-threading, grand central dispatch (if available), lockless atomic operations, and the dynamic nature of the Objective-C runtime.
+
+### Lumberjack is Fast
+
+In most cases it is an order of magnitude faster than NSLog.
+
+### Lumberjack is Simple
+
+It takes as little as a single line of code to configure lumberjack when your application launches. Then simply replace your NSLog statements with DDLog statements and that's about it. (And the DDLog macros have the exact same format and syntax as NSLog, so it's super easy.)
+
+### Lumberjack is Powerful:
+
+One log statement can be sent to multiple loggers, meaning you can log to a file and the console simultaneously. Want more? Create your own loggers (it's easy) and send your log statements over the network. Or to a database or distributed file system. The sky is the limit.
+
+### Lumberjack is Flexible:
+
+Configure your logging however you want. Change log levels per file (perfect for debugging). Change log levels per logger (verbose console, but concise log file). Change log levels per xcode configuration (verbose debug, but concise release). Have your log statements compiled out of the release build. Customize the number of log levels for your application. Add your own fine-grained logging. Dynamically change log levels during runtime. Choose how & when you want your log files to be rolled. Upload your log files to a central server. Compress archived log files to save disk space...
+
+## This framework is for you if:
+
+-   You're looking for a way to track down that impossible-to-reproduce bug that keeps popping up in the field.
+-   You're frustrated with the super short console log on the iPhone.
+-   You're looking to take your application to the next level in terms of support and stability.
+-   You're looking for an enterprise level logging solution for your application (Mac or iPhone).
+
+## Documentation
+
+- **[Get started using Lumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/GettingStarted.md)**<br/>
+- [Different log levels for Debug and Release builds](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/XcodeTricks.md)<br/>
+- [Different log levels for each logger](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/PerLoggerLogLevels.md)<br/>
+- [Use colors in the Xcode debugging console](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/XcodeColors.md)<br/>
+- [Write your own custom formatters](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomFormatters.md)<br/>
+- [FAQ](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/FAQ.md)<br/>
+- [Analysis of performance with benchmarks](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/Performance.md)<br/>
+- [Common issues you may encounter and their solutions](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/ProblemSolution.md)<br/>
+- [AppCode support](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/AppCode-support.md)
+- **[Full Lumberjack documentation](Documentation/)**<br/>
+
+## Requirements
+The current version of Lumberjack requires:
+- Xcode 12 or later
+- Swift 5.3 or later
+- iOS 9 or later
+- macOS 10.10 or later
+- watchOS 3 or later
+- tvOS 9 or later
+
+### Backwards compatibility
+- for Xcode 11 and Swift up to 5.2, use the 3.6.2 version
+- for Xcode 10 and Swift 4.2, use the 3.5.2 version
+- for iOS 8, use the 3.6.1 version
+- for iOS 6, iOS 7, OS X 10.8, OS X 10.9 and Xcode 9, use the 3.4.2 version
+- for iOS 5 and OS X 10.7, use the 3.3 version
+- for Xcode 8 and Swift 3, use the 3.2 version
+- for Xcode 7.3 and Swift 2.3, use the 2.4.0 version
+- for Xcode 7.3 and Swift 2.2, use the 2.3.0 version
+- for Xcode 7.2 and 7.1, use the 2.2.0 version
+- for Xcode 7.0 or earlier, use the 2.1.0 version
+- for Xcode 6 or earlier, use the 2.0.x version
+- for OS X < 10.7 support, use the 1.6.0 version
+
+## Communication
+
+- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/lumberjack). (Tag 'lumberjack')
+- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/lumberjack).
+- If you **found a bug**, open an issue.
+- If you **have a feature request**, open an issue.
+- If you **want to contribute**, submit a pull request.
+
+## Data Collection Practices
+
+Per [App privacy details on the App Store](https://developer.apple.com/app-store/app-privacy-details/), Apple is requesting app developers to provide info about their data collection, us SDK maintainers must provide them with the same data.
+
+### Data collection by the framework
+
+**By default, CocoaLumberjack does NOT collect any data on its own.** 
+
+[See our Data Collection Practices list.](https://cocoalumberjack.github.io/DataCollection/index.html)
+
+### Indirect data collection through the framework
+
+CocoaLumberjack is a logging framework which makes it easy to send those logs to different platforms.
+
+This is why collecting data might happen quite easily, if app developers include any sensitive data into their log messages.
+
+**Important note: app developers are fully responsible for any sensitive data collected through our logging system!**
+
+In consequence, you must comply to the Apple's privacy details policy (mentioned above) and document the ways in which user data is being collected.
+Since the number of scenarios where data might be indirectly collected through CocoaLumberjack is quite large, it's up to you, as app developers, to properly review your app's code and identify those cases.
+What we can do to help is raise awareness about potential data collection through our framework.
+
+Private data includes but isn't limited to:
+
+- user info (name, email, address, ...)
+- location info
+- contacts
+- identifiers (user id, device id, ...)
+- app usage data
+- performance data
+- health and fitness info
+- financial info
+- sensitive info
+- user content
+- history (browsing, search, ...)
+- purchases
+- diagnostics
+- ...
+
+_Example_: `DDLogInfo("User: \(myUser)")` will add the `myUser` info to the logs, so if those are forwarded to a 3rd party or sent via email, that may qualify as data collection.
+
+## Author
+
+- [Robbie Hanson](https://github.com/robbiehanson)
+- Love the project? Wanna buy me a coffee? (or a beer :D) [![donation](http://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UZRA26JPJB3DA)
+
+## Collaborators
+- [Ernesto Rivera](https://github.com/rivera-ernesto)
+- [Dmitry Vorobyov](https://github.com/dvor)
+- [Bogdan Poplauschi](https://github.com/bpoplauschi)
+- [C.W. Betts](https://github.com/MaddTheSane)
+- [Koichi Yokota (sushichop)](https://github.com/sushichop)
+- [Nick Brook](https://github.com/nrbrook)
+- [Florian Friedrich](https://github.com/ffried)
+- [Stephan Diederich](https://github.com/diederich)
+- [Kent Sutherland](https://github.com/ksuther)
+- [Dmitry Lobanov](https://github.com/lolgear)
+- [Hakon Hanesand](https://github.com/hhanesand)
+
+## License
+- CocoaLumberjack is available under the BSD 3 license. See the [LICENSE file](https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/LICENSE).
+
+## Extensions
+- [LogIO-CocoaLumberjack](https://github.com/s4nchez/LogIO-CocoaLumberjack) A log.io logger for CocoaLumberjack
+- [XCDLumberjackNSLogger](https://github.com/0xced/XCDLumberjackNSLogger) CocoaLumberjack logger which sends logs to NSLogger
+
+## Architecture
+
+<p align="center" >
+    <img src="https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/Documentation/CocoaLumberjackClassDiagram.png" title="CocoaLumberjack class diagram">
+</p>

+ 57 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m

@@ -0,0 +1,57 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if TARGET_OS_OSX
+
+#import <CocoaLumberjack/CLIColor.h>
+
+@interface CLIColor () {
+    CGFloat _red, _green, _blue, _alpha;
+}
+
+@end
+
+
+@implementation CLIColor
+
++ (instancetype)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+    CLIColor *color = [CLIColor new];
+    color->_red     = red;
+    color->_green   = green;
+    color->_blue    = blue;
+    color->_alpha   = alpha;
+    return color;
+}
+
+- (void)getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha {
+    if (red) {
+        *red    = _red;
+    }
+    if (green) {
+        *green  = _green;
+    }
+    if (blue) {
+        *blue   = _blue;
+    }
+    if (alpha) {
+        *alpha  = _alpha;
+    }
+}
+
+@end
+
+#endif

+ 205 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m

@@ -0,0 +1,205 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if !TARGET_OS_WATCH
+
+#include <asl.h>
+#include <notify.h>
+#include <notify_keys.h>
+#include <sys/time.h>
+
+#import <CocoaLumberjack/DDASLLogCapture.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+static BOOL _cancel = YES;
+static DDLogLevel _captureLevel = DDLogLevelVerbose;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDASLLogCapture
+#pragma clang diagnostic pop
+
++ (void)start {
+    // Ignore subsequent calls
+    if (!_cancel) {
+        return;
+    }
+    
+    _cancel = NO;
+    
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+        [self captureAslLogs];
+    });
+}
+
++ (void)stop {
+    _cancel = YES;
+}
+
++ (DDLogLevel)captureLevel {
+    return _captureLevel;
+}
+
++ (void)setCaptureLevel:(DDLogLevel)level {
+    _captureLevel = level;
+}
+
+#pragma mark - Private methods
+
++ (void)configureAslQuery:(aslmsg)query {
+    const char param[] = "7";  // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter
+    
+    asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC);
+
+    // Don't retrieve logs from our own DDASLLogger
+    asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL);
+    
+#if !TARGET_OS_IPHONE || (defined(TARGET_SIMULATOR) && TARGET_SIMULATOR)
+    int processId = [[NSProcessInfo processInfo] processIdentifier];
+    char pid[16];
+    snprintf(pid, sizeof(pid), "%d", processId);
+    asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC);
+#endif
+}
+
++ (void)aslMessageReceived:(aslmsg)msg {
+    const char* messageCString = asl_get( msg, ASL_KEY_MSG );
+    if ( messageCString == NULL )
+        return;
+
+    DDLogFlag flag;
+    BOOL async;
+
+    const char* levelCString = asl_get(msg, ASL_KEY_LEVEL);
+    switch (levelCString? atoi(levelCString) : 0) {
+        // By default all NSLog's with a ASL_LEVEL_WARNING level
+        case ASL_LEVEL_EMERG    :
+        case ASL_LEVEL_ALERT    :
+        case ASL_LEVEL_CRIT     : flag = DDLogFlagError;    async = NO;  break;
+        case ASL_LEVEL_ERR      : flag = DDLogFlagWarning;  async = YES; break;
+        case ASL_LEVEL_WARNING  : flag = DDLogFlagInfo;     async = YES; break;
+        case ASL_LEVEL_NOTICE   : flag = DDLogFlagDebug;    async = YES; break;
+        case ASL_LEVEL_INFO     :
+        case ASL_LEVEL_DEBUG    :
+        default                 : flag = DDLogFlagVerbose;  async = YES;  break;
+    }
+
+    if (!(_captureLevel & flag)) {
+        return;
+    }
+
+    //  NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding];
+    NSString *message = @(messageCString);
+
+    const char* secondsCString = asl_get( msg, ASL_KEY_TIME );
+    const char* nanoCString = asl_get( msg, ASL_KEY_TIME_NSEC );
+    NSTimeInterval seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970;
+    double nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0;
+    NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9);
+
+    NSDate *timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
+
+    DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message
+                                                               level:_captureLevel
+                                                                flag:flag
+                                                             context:0
+                                                                file:@"DDASLLogCapture"
+                                                            function:nil
+                                                                line:0
+                                                                 tag:nil
+                                                             options:0
+                                                           timestamp:timeStamp];
+    
+    [DDLog log:async message:logMessage];
+}
+
++ (void)captureAslLogs {
+    @autoreleasepool
+    {
+        /*
+           We use ASL_KEY_MSG_ID to see each message once, but there's no
+           obvious way to get the "next" ID. To bootstrap the process, we'll
+           search by timestamp until we've seen a message.
+         */
+
+        struct timeval timeval = {
+            .tv_sec = 0
+        };
+        gettimeofday(&timeval, NULL);
+        unsigned long long startTime = (unsigned long long)timeval.tv_sec;
+        __block unsigned long long lastSeenID = 0;
+
+        /*
+           syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message)
+           through the notify API when it saves messages to the ASL database.
+           There is some coalescing - currently it is sent at most twice per
+           second - but there is no documented guarantee about this. In any
+           case, there may be multiple messages per notification.
+
+           Notify notifications don't carry any payload, so we need to search
+           for the messages.
+         */
+        int notifyToken = 0;  // Can be used to unregister with notify_cancel().
+        notify_register_dispatch(kNotifyASLDBUpdate, &notifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
+        {
+            // At least one message has been posted; build a search query.
+            @autoreleasepool
+            {
+                aslmsg query = asl_new(ASL_TYPE_QUERY);
+                char stringValue[64];
+
+                if (lastSeenID > 0) {
+                    snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID);
+                    asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC);
+                } else {
+                    snprintf(stringValue, sizeof stringValue, "%llu", startTime);
+                    asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC);
+                }
+
+                [self configureAslQuery:query];
+
+                // Iterate over new messages.
+                aslmsg msg;
+                aslresponse response = asl_search(NULL, query);
+                
+                while ((msg = asl_next(response)))
+                {
+                    [self aslMessageReceived:msg];
+
+                    // Keep track of which messages we've seen.
+                    lastSeenID = (unsigned long long)atoll(asl_get(msg, ASL_KEY_MSG_ID));
+                }
+                asl_release(response);
+                asl_free(query);
+
+                if (_cancel) {
+                    notify_cancel(token);
+                    return;
+                }
+
+            }
+        });
+    }
+}
+
+@end
+
+#endif

+ 133 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m

@@ -0,0 +1,133 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if !TARGET_OS_WATCH
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <asl.h>
+
+#import <CocoaLumberjack/DDASLLogger.h>
+
+const char* const kDDASLKeyDDLog = "DDLog";
+const char* const kDDASLDDLogValue = "1";
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+static DDASLLogger *sharedInstance;
+#pragma clang diagnostic pop
+
+@interface DDASLLogger () {
+    aslclient _client;
+}
+
+@end
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDASLLogger
+#pragma clang diagnostic pop
+
++ (instancetype)sharedInstance {
+    static dispatch_once_t DDASLLoggerOnceToken;
+
+    dispatch_once(&DDASLLoggerOnceToken, ^{
+        sharedInstance = [[[self class] alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+- (instancetype)init {
+    if (sharedInstance != nil) {
+        return nil;
+    }
+
+    if ((self = [super init])) {
+        // A default asl client is provided for the main thread,
+        // but background threads need to create their own client.
+
+        _client = asl_open(NULL, "com.apple.console", 0);
+    }
+
+    return self;
+}
+
+- (DDLoggerName)loggerName {
+    return DDLoggerNameASL;
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Skip captured log messages
+    if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
+        return;
+    }
+
+    NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
+
+    if (message) {
+        const char *msg = [message UTF8String];
+
+        size_t aslLogLevel;
+        switch (logMessage->_flag) {
+            // Note: By default ASL will filter anything above level 5 (Notice).
+            // So our mappings shouldn't go above that level.
+            case DDLogFlagError     : aslLogLevel = ASL_LEVEL_CRIT;     break;
+            case DDLogFlagWarning   : aslLogLevel = ASL_LEVEL_ERR;      break;
+            case DDLogFlagInfo      : aslLogLevel = ASL_LEVEL_WARNING;  break; // Regular NSLog's level
+            case DDLogFlagDebug     :
+            case DDLogFlagVerbose   :
+            default                 : aslLogLevel = ASL_LEVEL_NOTICE;   break;
+        }
+
+        static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" };
+
+        // NSLog uses the current euid to set the ASL_KEY_READ_UID.
+        uid_t const readUID = geteuid();
+
+        char readUIDString[16];
+#ifndef NS_BLOCK_ASSERTIONS
+        size_t l = (size_t)snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
+#else
+        snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
+#endif
+
+        NSAssert(l < sizeof(readUIDString),
+                 @"Formatted euid is too long.");
+        NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])),
+                 @"Unhandled ASL log level.");
+
+        aslmsg m = asl_new(ASL_TYPE_MSG);
+        if (m != NULL) {
+            if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 &&
+                asl_set(m, ASL_KEY_MSG, msg) == 0 &&
+                asl_set(m, ASL_KEY_READ_UID, readUIDString) == 0 &&
+                asl_set(m, kDDASLKeyDDLog, kDDASLDDLogValue) == 0) {
+                asl_send(_client, m);
+            }
+            asl_free(m);
+        }
+        //TODO handle asl_* failures non-silently?
+    }
+}
+
+@end
+
+#endif

+ 683 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m

@@ -0,0 +1,683 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <CocoaLumberjack/DDAbstractDatabaseLogger.h>
+
+@interface DDAbstractDatabaseLogger ()
+
+- (void)destroySaveTimer;
+- (void)updateAndResumeSaveTimer;
+- (void)createSuspendedSaveTimer;
+- (void)destroyDeleteTimer;
+- (void)updateDeleteTimer;
+- (void)createAndStartDeleteTimer;
+
+@end
+
+#pragma mark -
+
+@implementation DDAbstractDatabaseLogger
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _saveThreshold = 500;
+        _saveInterval = 60;           // 60 seconds
+        _maxAge = (60 * 60 * 24 * 7); //  7 days
+        _deleteInterval = (60 * 5);   //  5 minutes
+    }
+
+    return self;
+}
+
+- (void)dealloc {
+    [self destroySaveTimer];
+    [self destroyDeleteTimer];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Override Me
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)db_log:(__unused DDLogMessage *)logMessage {
+    // Override me and add your implementation.
+    //
+    // Return YES if an item was added to the buffer.
+    // Return NO if the logMessage was ignored.
+
+    return NO;
+}
+
+- (void)db_save {
+    // Override me and add your implementation.
+}
+
+- (void)db_delete {
+    // Override me and add your implementation.
+}
+
+- (void)db_saveAndDelete {
+    // Override me and add your implementation.
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Private API
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)performSaveAndSuspendSaveTimer {
+    if (_unsavedCount > 0) {
+        if (_deleteOnEverySave) {
+            [self db_saveAndDelete];
+        } else {
+            [self db_save];
+        }
+    }
+
+    _unsavedCount = 0;
+    _unsavedTime = 0;
+
+    if (_saveTimer != NULL && _saveTimerSuspended == 0) {
+        dispatch_suspend(_saveTimer);
+        _saveTimerSuspended = 1;
+    }
+}
+
+- (void)performDelete {
+    if (_maxAge > 0.0) {
+        [self db_delete];
+
+        _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Timers
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)destroySaveTimer {
+    if (_saveTimer != NULL) {
+        dispatch_source_cancel(_saveTimer);
+
+        // Must activate a timer before releasing it (or it will crash)
+        if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
+            if (_saveTimerSuspended < 0) {
+                dispatch_activate(_saveTimer);
+            } else if (_saveTimerSuspended > 0) {
+                dispatch_resume(_saveTimer);
+            }
+        } else {
+            if (_saveTimerSuspended != 0) {
+                dispatch_resume(_saveTimer);
+            }
+        }
+
+        #if !OS_OBJECT_USE_OBJC
+        dispatch_release(_saveTimer);
+        #endif
+        _saveTimer = NULL;
+        _saveTimerSuspended = 0;
+    }
+}
+
+- (void)updateAndResumeSaveTimer {
+    if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0)) {
+        uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
+        dispatch_time_t startTime = dispatch_time(_unsavedTime, (int64_t)interval);
+
+        dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC);
+
+        if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
+            if (_saveTimerSuspended < 0) {
+                dispatch_activate(_saveTimer);
+                _saveTimerSuspended = 0;
+            } else if (_saveTimerSuspended > 0) {
+                dispatch_resume(_saveTimer);
+                _saveTimerSuspended = 0;
+            }
+        } else {
+            if (_saveTimerSuspended != 0) {
+                dispatch_resume(_saveTimer);
+                _saveTimerSuspended = 0;
+            }
+        }
+    }
+}
+
+- (void)createSuspendedSaveTimer {
+    if ((_saveTimer == NULL) && (_saveInterval > 0.0)) {
+        _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
+
+        dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
+                                                            [self performSaveAndSuspendSaveTimer];
+                                                        } });
+
+        _saveTimerSuspended = -1;
+    }
+}
+
+- (void)destroyDeleteTimer {
+    if (_deleteTimer != NULL) {
+        dispatch_source_cancel(_deleteTimer);
+        #if !OS_OBJECT_USE_OBJC
+        dispatch_release(_deleteTimer);
+        #endif
+        _deleteTimer = NULL;
+    }
+}
+
+- (void)updateDeleteTimer {
+    if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
+        int64_t interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
+        dispatch_time_t startTime;
+
+        if (_lastDeleteTime > 0) {
+            startTime = dispatch_time(_lastDeleteTime, interval);
+        } else {
+            startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
+        }
+
+        dispatch_source_set_timer(_deleteTimer, startTime, (uint64_t)interval, 1ull * NSEC_PER_SEC);
+    }
+}
+
+- (void)createAndStartDeleteTimer {
+    if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
+        _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
+
+        if (_deleteTimer != NULL) {
+            dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
+                                                                  [self performDelete];
+                                                              } });
+
+            [self updateDeleteTimer];
+
+            // We are sure that -updateDeleteTimer did call dispatch_source_set_timer()
+            // since it has the same guards on _deleteInterval and _maxAge
+            if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *))
+                dispatch_activate(_deleteTimer);
+            else
+                dispatch_resume(_deleteTimer);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSUInteger)saveThreshold {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block NSUInteger result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_saveThreshold;
+        });
+    });
+
+    return result;
+}
+
+- (void)setSaveThreshold:(NSUInteger)threshold {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            if (self->_saveThreshold != threshold) {
+                self->_saveThreshold = threshold;
+
+                // Since the saveThreshold has changed,
+                // we check to see if the current unsavedCount has surpassed the new threshold.
+                //
+                // If it has, we immediately save the log.
+
+                if ((self->_unsavedCount >= self->_saveThreshold) && (self->_saveThreshold > 0)) {
+                    [self performSaveAndSuspendSaveTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)saveInterval {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block NSTimeInterval result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_saveInterval;
+        });
+    });
+
+    return result;
+}
+
+- (void)setSaveInterval:(NSTimeInterval)interval {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* saveInterval != interval */ islessgreater(self->_saveInterval, interval)) {
+                self->_saveInterval = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the saveInterval was previously enabled and it just got disabled,
+                //    then we need to stop the saveTimer. (And we might as well release it.)
+                //
+                // 2. If the saveInterval was previously disabled and it just got enabled,
+                //    then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
+                //
+                // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
+                //
+                // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
+                //    (Plus we might need to do an immediate save.)
+
+                if (self->_saveInterval > 0.0) {
+                    if (self->_saveTimer == NULL) {
+                        // Handles #2
+                        //
+                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self createSuspendedSaveTimer];
+                        [self updateAndResumeSaveTimer];
+                    } else {
+                        // Handles #3
+                        // Handles #4
+                        //
+                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self updateAndResumeSaveTimer];
+                    }
+                } else if (self->_saveTimer) {
+                    // Handles #1
+
+                    [self destroySaveTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)maxAge {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block NSTimeInterval result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_maxAge;
+        });
+    });
+
+    return result;
+}
+
+- (void)setMaxAge:(NSTimeInterval)interval {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) {
+                NSTimeInterval oldMaxAge = self->_maxAge;
+                NSTimeInterval newMaxAge = interval;
+
+                self->_maxAge = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the maxAge was previously enabled and it just got disabled,
+                //    then we need to stop the deleteTimer. (And we might as well release it.)
+                //
+                // 2. If the maxAge was previously disabled and it just got enabled,
+                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
+                //
+                // 3. If the maxAge was increased,
+                //    then we don't need to do anything.
+                //
+                // 4. If the maxAge was decreased,
+                //    then we should do an immediate delete.
+
+                BOOL shouldDeleteNow = NO;
+
+                if (oldMaxAge > 0.0) {
+                    if (newMaxAge <= 0.0) {
+                        // Handles #1
+
+                        [self destroyDeleteTimer];
+                    } else if (oldMaxAge > newMaxAge) {
+                        // Handles #4
+                        shouldDeleteNow = YES;
+                    }
+                } else if (newMaxAge > 0.0) {
+                    // Handles #2
+                    shouldDeleteNow = YES;
+                }
+
+                if (shouldDeleteNow) {
+                    [self performDelete];
+
+                    if (self->_deleteTimer) {
+                        [self updateDeleteTimer];
+                    } else {
+                        [self createAndStartDeleteTimer];
+                    }
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)deleteInterval {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block NSTimeInterval result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_deleteInterval;
+        });
+    });
+
+    return result;
+}
+
+- (void)setDeleteInterval:(NSTimeInterval)interval {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* deleteInterval != interval */ islessgreater(self->_deleteInterval, interval)) {
+                self->_deleteInterval = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the deleteInterval was previously enabled and it just got disabled,
+                //    then we need to stop the deleteTimer. (And we might as well release it.)
+                //
+                // 2. If the deleteInterval was previously disabled and it just got enabled,
+                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
+                //
+                // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
+                //
+                // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
+                //    (Plus we might need to do an immediate delete.)
+
+                if (self->_deleteInterval > 0.0) {
+                    if (self->_deleteTimer == NULL) {
+                        // Handles #2
+                        //
+                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
+                        // if a delete is needed the timer will fire immediately.
+
+                        [self createAndStartDeleteTimer];
+                    } else {
+                        // Handles #3
+                        // Handles #4
+                        //
+                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self updateDeleteTimer];
+                    }
+                } else if (self->_deleteTimer) {
+                    // Handles #1
+
+                    [self destroyDeleteTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (BOOL)deleteOnEverySave {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block BOOL result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_deleteOnEverySave;
+        });
+    });
+
+    return result;
+}
+
+- (void)setDeleteOnEverySave:(BOOL)flag {
+    dispatch_block_t block = ^{
+        self->_deleteOnEverySave = flag;
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Public API
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)savePendingLogEntries {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self performSaveAndSuspendSaveTimer];
+        }
+    };
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_async(self.loggerQueue, block);
+    }
+}
+
+- (void)deleteOldLogEntries {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self performDelete];
+        }
+    };
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_async(self.loggerQueue, block);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark DDLogger
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)didAddLogger {
+    // If you override me be sure to invoke [super didAddLogger];
+
+    [self createSuspendedSaveTimer];
+
+    [self createAndStartDeleteTimer];
+}
+
+- (void)willRemoveLogger {
+    // If you override me be sure to invoke [super willRemoveLogger];
+
+    [self performSaveAndSuspendSaveTimer];
+
+    [self destroySaveTimer];
+    [self destroyDeleteTimer];
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    if ([self db_log:logMessage]) {
+        BOOL firstUnsavedEntry = (++_unsavedCount == 1);
+
+        if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
+            [self performSaveAndSuspendSaveTimer];
+        } else if (firstUnsavedEntry) {
+            _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
+            [self updateAndResumeSaveTimer];
+        }
+    }
+}
+
+- (void)flush {
+    // This method is invoked by DDLog's flushLog method.
+    //
+    // It is called automatically when the application quits,
+    // or if the developer invokes DDLog's flushLog method prior to crashing or something.
+
+    [self performSaveAndSuspendSaveTimer];
+}
+
+@end

+ 31 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h

@@ -0,0 +1,31 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDFileLogger.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DDFileLogger (Internal)
+
+- (void)logData:(NSData *)data;
+
+// Will assert if used outside logger's queue.
+- (void)lt_logData:(NSData *)data;
+
+- (nullable NSData *)lt_dataForMessage:(DDLogMessage *)message;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 1800 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m

@@ -0,0 +1,1800 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <sys/xattr.h>
+
+#import "DDFileLogger+Internal.h"
+
+// We probably shouldn't be using DDLog() statements within the DDLog implementation.
+// But we still want to leave our log statements for any future debugging,
+// and to allow other developers to trace the implementation (which is a great learning tool).
+//
+// So we use primitive logging macros around NSLog.
+// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
+
+#ifndef DD_NSLOG_LEVEL
+    #define DD_NSLOG_LEVEL 2
+#endif
+
+#define NSLogError(frmt, ...)    do{ if(DD_NSLOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogWarn(frmt, ...)     do{ if(DD_NSLOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogInfo(frmt, ...)     do{ if(DD_NSLOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogDebug(frmt, ...)    do{ if(DD_NSLOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogVerbose(frmt, ...)  do{ if(DD_NSLOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0)
+
+
+#if TARGET_OS_IPHONE
+BOOL doesAppRunInBackground(void);
+#endif
+
+unsigned long long const kDDDefaultLogMaxFileSize      = 1024 * 1024;      // 1 MB
+NSTimeInterval     const kDDDefaultLogRollingFrequency = 60 * 60 * 24;     // 24 Hours
+NSUInteger         const kDDDefaultLogMaxNumLogFiles   = 5;                // 5 Files
+unsigned long long const kDDDefaultLogFilesDiskQuota   = 20 * 1024 * 1024; // 20 MB
+
+NSTimeInterval     const kDDRollingLeeway              = 1.0;              // 1s
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLogFileManagerDefault () {
+    NSDateFormatter *_fileDateFormatter;
+    NSUInteger _maximumNumberOfLogFiles;
+    unsigned long long _logFilesDiskQuota;
+    NSString *_logsDirectory;
+#if TARGET_OS_IPHONE
+    NSFileProtectionType _defaultFileProtectionLevel;
+#endif
+}
+
+@end
+
+@implementation DDLogFileManagerDefault
+
+@synthesize maximumNumberOfLogFiles = _maximumNumberOfLogFiles;
+@synthesize logFilesDiskQuota = _logFilesDiskQuota;
+
+- (instancetype)init {
+    return [self initWithLogsDirectory:nil];
+}
+
+- (instancetype)initWithLogsDirectory:(nullable NSString *)aLogsDirectory {
+    if ((self = [super init])) {
+        _maximumNumberOfLogFiles = kDDDefaultLogMaxNumLogFiles;
+        _logFilesDiskQuota = kDDDefaultLogFilesDiskQuota;
+
+        _fileDateFormatter = [[NSDateFormatter alloc] init];
+        [_fileDateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+        [_fileDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+        [_fileDateFormatter setDateFormat: @"yyyy'-'MM'-'dd'--'HH'-'mm'-'ss'-'SSS'"];
+
+        if (aLogsDirectory.length > 0) {
+            _logsDirectory = [aLogsDirectory copy];
+        } else {
+            _logsDirectory = [[self defaultLogsDirectory] copy];
+        }
+
+        NSLogVerbose(@"DDFileLogManagerDefault: logsDirectory:\n%@", [self logsDirectory]);
+        NSLogVerbose(@"DDFileLogManagerDefault: sortedLogFileNames:\n%@", [self sortedLogFileNames]);
+    }
+
+    return self;
+}
+
+#if TARGET_OS_IPHONE
+- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory
+           defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel {
+
+    if ((self = [self initWithLogsDirectory:logsDirectory])) {
+        if ([fileProtectionLevel isEqualToString:NSFileProtectionNone] ||
+            [fileProtectionLevel isEqualToString:NSFileProtectionComplete] ||
+            [fileProtectionLevel isEqualToString:NSFileProtectionCompleteUnlessOpen] ||
+            [fileProtectionLevel isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]) {
+            _defaultFileProtectionLevel = fileProtectionLevel;
+        }
+    }
+
+    return self;
+}
+
+#endif
+
+- (void)deleteOldFilesForConfigurationChange {
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        @autoreleasepool {
+            // See method header for queue reasoning.
+            [self deleteOldLogFiles];
+        }
+    });
+}
+
+- (void)setLogFilesDiskQuota:(unsigned long long)logFilesDiskQuota {
+    if (_logFilesDiskQuota != logFilesDiskQuota) {
+        _logFilesDiskQuota = logFilesDiskQuota;
+        NSLogInfo(@"DDFileLogManagerDefault: Responding to configuration change: logFilesDiskQuota");
+        [self deleteOldFilesForConfigurationChange];
+    }
+}
+
+- (void)setMaximumNumberOfLogFiles:(NSUInteger)maximumNumberOfLogFiles {
+    if (_maximumNumberOfLogFiles != maximumNumberOfLogFiles) {
+        _maximumNumberOfLogFiles = maximumNumberOfLogFiles;
+        NSLogInfo(@"DDFileLogManagerDefault: Responding to configuration change: maximumNumberOfLogFiles");
+        [self deleteOldFilesForConfigurationChange];
+    }
+}
+
+#if TARGET_OS_IPHONE
+- (NSFileProtectionType)logFileProtection {
+    if (_defaultFileProtectionLevel.length > 0) {
+        return _defaultFileProtectionLevel;
+    } else if (doesAppRunInBackground()) {
+        return NSFileProtectionCompleteUntilFirstUserAuthentication;
+    } else {
+        return NSFileProtectionCompleteUnlessOpen;
+    }
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark File Deleting
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Deletes archived log files that exceed the maximumNumberOfLogFiles or logFilesDiskQuota configuration values.
+ * Method may take a while to execute since we're performing IO. It's not critical that this is synchronized with
+ * log output, since the files we're deleting are all archived and not in use, therefore this method is called on a
+ * background queue.
+ **/
+- (void)deleteOldLogFiles {
+    NSLogVerbose(@"DDLogFileManagerDefault: deleteOldLogFiles");
+
+    NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
+    NSUInteger firstIndexToDelete = NSNotFound;
+
+    const unsigned long long diskQuota = self.logFilesDiskQuota;
+    const NSUInteger maxNumLogFiles = self.maximumNumberOfLogFiles;
+
+    if (diskQuota) {
+        unsigned long long used = 0;
+
+        for (NSUInteger i = 0; i < sortedLogFileInfos.count; i++) {
+            DDLogFileInfo *info = sortedLogFileInfos[i];
+            used += info.fileSize;
+
+            if (used > diskQuota) {
+                firstIndexToDelete = i;
+                break;
+            }
+        }
+    }
+
+    if (maxNumLogFiles) {
+        if (firstIndexToDelete == NSNotFound) {
+            firstIndexToDelete = maxNumLogFiles;
+        } else {
+            firstIndexToDelete = MIN(firstIndexToDelete, maxNumLogFiles);
+        }
+    }
+
+    if (firstIndexToDelete == 0) {
+        // Do we consider the first file?
+        // We are only supposed to be deleting archived files.
+        // In most cases, the first file is likely the log file that is currently being written to.
+        // So in most cases, we do not want to consider this file for deletion.
+
+        if (sortedLogFileInfos.count > 0) {
+            DDLogFileInfo *logFileInfo = sortedLogFileInfos[0];
+
+            if (!logFileInfo.isArchived) {
+                // Don't delete active file.
+                ++firstIndexToDelete;
+            }
+        }
+    }
+
+    if (firstIndexToDelete != NSNotFound) {
+        // removing all log files starting with firstIndexToDelete
+
+        for (NSUInteger i = firstIndexToDelete; i < sortedLogFileInfos.count; i++) {
+            DDLogFileInfo *logFileInfo = sortedLogFileInfos[i];
+
+            NSError *error = nil;
+            BOOL success = [[NSFileManager defaultManager] removeItemAtPath:logFileInfo.filePath error:&error];
+            if (success) {
+                NSLogInfo(@"DDLogFileManagerDefault: Deleting file: %@", logFileInfo.fileName);
+            } else {
+                NSLogError(@"DDLogFileManagerDefault: Error deleting file %@", error);
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Log Files
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns the path to the default logs directory.
+ * If the logs directory doesn't exist, this method automatically creates it.
+ **/
+- (NSString *)defaultLogsDirectory {
+
+#if TARGET_OS_IPHONE
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+    NSString *baseDir = paths.firstObject;
+    NSString *logsDirectory = [baseDir stringByAppendingPathComponent:@"Logs"];
+#else
+    NSString *appName = [[NSProcessInfo processInfo] processName];
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+    NSString *basePath = ([paths count] > 0) ? paths[0] : NSTemporaryDirectory();
+    NSString *logsDirectory = [[basePath stringByAppendingPathComponent:@"Logs"] stringByAppendingPathComponent:appName];
+#endif
+
+    return logsDirectory;
+}
+
+- (NSString *)logsDirectory {
+    // We could do this check once, during initialization, and not bother again.
+    // But this way the code continues to work if the directory gets deleted while the code is running.
+
+    NSAssert(_logsDirectory.length > 0, @"Directory must be set.");
+
+    NSError *err = nil;
+    BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:_logsDirectory
+                                             withIntermediateDirectories:YES
+                                                              attributes:nil
+                                                                   error:&err];
+    if (success == NO) {
+        NSLogError(@"DDFileLogManagerDefault: Error creating logsDirectory: %@", err);
+    }
+
+    return _logsDirectory;
+}
+
+- (BOOL)isLogFile:(NSString *)fileName {
+    NSString *appName = [self applicationName];
+
+    // We need to add a space to the name as otherwise we could match applications that have the name prefix.
+    BOOL hasProperPrefix = [fileName hasPrefix:[appName stringByAppendingString:@" "]];
+    BOOL hasProperSuffix = [fileName hasSuffix:@".log"];
+
+    return (hasProperPrefix && hasProperSuffix);
+}
+
+// if you change formatter, then change sortedLogFileInfos method also accordingly
+- (NSDateFormatter *)logFileDateFormatter {
+    return _fileDateFormatter;
+}
+
+- (NSArray *)unsortedLogFilePaths {
+    NSString *logsDirectory = [self logsDirectory];
+    NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logsDirectory error:nil];
+
+    NSMutableArray *unsortedLogFilePaths = [NSMutableArray arrayWithCapacity:[fileNames count]];
+
+    for (NSString *fileName in fileNames) {
+        // Filter out any files that aren't log files. (Just for extra safety)
+
+#if TARGET_IPHONE_SIMULATOR
+        // This is only used on the iPhone simulator for backward compatibility reason.
+        //
+        // In case of iPhone simulator there can be 'archived' extension. isLogFile:
+        // method knows nothing about it. Thus removing it for this method.
+        NSString *theFileName = [fileName stringByReplacingOccurrencesOfString:@".archived"
+                                                                    withString:@""];
+
+        if ([self isLogFile:theFileName])
+#else
+
+        if ([self isLogFile:fileName])
+#endif
+        {
+            NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName];
+
+            [unsortedLogFilePaths addObject:filePath];
+        }
+    }
+
+    return unsortedLogFilePaths;
+}
+
+- (NSArray *)unsortedLogFileNames {
+    NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths];
+
+    NSMutableArray *unsortedLogFileNames = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]];
+
+    for (NSString *filePath in unsortedLogFilePaths) {
+        [unsortedLogFileNames addObject:[filePath lastPathComponent]];
+    }
+
+    return unsortedLogFileNames;
+}
+
+- (NSArray *)unsortedLogFileInfos {
+    NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths];
+
+    NSMutableArray *unsortedLogFileInfos = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]];
+
+    for (NSString *filePath in unsortedLogFilePaths) {
+        DDLogFileInfo *logFileInfo = [[DDLogFileInfo alloc] initWithFilePath:filePath];
+
+        [unsortedLogFileInfos addObject:logFileInfo];
+    }
+
+    return unsortedLogFileInfos;
+}
+
+- (NSArray *)sortedLogFilePaths {
+    NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
+
+    NSMutableArray *sortedLogFilePaths = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]];
+
+    for (DDLogFileInfo *logFileInfo in sortedLogFileInfos) {
+        [sortedLogFilePaths addObject:[logFileInfo filePath]];
+    }
+
+    return sortedLogFilePaths;
+}
+
+- (NSArray *)sortedLogFileNames {
+    NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
+
+    NSMutableArray *sortedLogFileNames = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]];
+
+    for (DDLogFileInfo *logFileInfo in sortedLogFileInfos) {
+        [sortedLogFileNames addObject:[logFileInfo fileName]];
+    }
+
+    return sortedLogFileNames;
+}
+
+- (NSArray *)sortedLogFileInfos {
+    return [[self unsortedLogFileInfos] sortedArrayUsingComparator:^NSComparisonResult(DDLogFileInfo *obj1,
+                                                                                       DDLogFileInfo *obj2) {
+        NSDate *date1 = [NSDate new];
+        NSDate *date2 = [NSDate new];
+
+        NSArray<NSString *> *arrayComponent = [[obj1 fileName] componentsSeparatedByString:@" "];
+        if (arrayComponent.count > 0) {
+            NSString *stringDate = arrayComponent.lastObject;
+            stringDate = [stringDate stringByReplacingOccurrencesOfString:@".log" withString:@""];
+#if TARGET_IPHONE_SIMULATOR
+            // This is only used on the iPhone simulator for backward compatibility reason.
+            stringDate = [stringDate stringByReplacingOccurrencesOfString:@".archived" withString:@""];
+#endif
+            date1 = [[self logFileDateFormatter] dateFromString:stringDate] ?: [obj1 creationDate];
+        }
+
+        arrayComponent = [[obj2 fileName] componentsSeparatedByString:@" "];
+        if (arrayComponent.count > 0) {
+            NSString *stringDate = arrayComponent.lastObject;
+            stringDate = [stringDate stringByReplacingOccurrencesOfString:@".log" withString:@""];
+#if TARGET_IPHONE_SIMULATOR
+            // This is only used on the iPhone simulator for backward compatibility reason.
+            stringDate = [stringDate stringByReplacingOccurrencesOfString:@".archived" withString:@""];
+#endif
+            date2 = [[self logFileDateFormatter] dateFromString:stringDate] ?: [obj2 creationDate];
+        }
+
+        return [date2 compare:date1 ?: [NSDate new]];
+    }];
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Creation
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//if you change newLogFileName , then  change isLogFile method also accordingly
+- (NSString *)newLogFileName {
+    NSString *appName = [self applicationName];
+
+    NSDateFormatter *dateFormatter = [self logFileDateFormatter];
+    NSString *formattedDate = [dateFormatter stringFromDate:[NSDate date]];
+
+    return [NSString stringWithFormat:@"%@ %@.log", appName, formattedDate];
+}
+
+- (nullable NSString *)logFileHeader {
+    return nil;
+}
+
+- (NSData *)logFileHeaderData {
+    NSString *fileHeaderStr = [self logFileHeader];
+
+    if (fileHeaderStr.length == 0) {
+        return nil;
+    }
+
+    if (![fileHeaderStr hasSuffix:@"\n"]) {
+        fileHeaderStr = [fileHeaderStr stringByAppendingString:@"\n"];
+    }
+
+    return [fileHeaderStr dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+- (NSString *)createNewLogFileWithError:(NSError *__autoreleasing  _Nullable *)error {
+    static NSUInteger MAX_ALLOWED_ERROR = 5;
+
+    NSString *fileName = [self newLogFileName];
+    NSString *logsDirectory = [self logsDirectory];
+    NSData *fileHeader = [self logFileHeaderData];
+    if (fileHeader == nil) {
+        fileHeader = [NSData new];
+    }
+
+    NSUInteger attempt = 1;
+    NSUInteger criticalErrors = 0;
+    NSError *lastCriticalError;
+
+    do {
+        if (criticalErrors >= MAX_ALLOWED_ERROR) {
+            NSLogError(@"DDLogFileManagerDefault: Bailing file creation, encountered %ld errors.",
+                        (unsigned long)criticalErrors);
+            *error = lastCriticalError;
+            return nil;
+        }
+
+        NSString *actualFileName = fileName;
+        if (attempt > 1) {
+            NSString *extension = [actualFileName pathExtension];
+
+            actualFileName = [actualFileName stringByDeletingPathExtension];
+            actualFileName = [actualFileName stringByAppendingFormat:@" %lu", (unsigned long)attempt];
+
+            if (extension.length) {
+                actualFileName = [actualFileName stringByAppendingPathExtension:extension];
+            }
+        }
+
+        NSString *filePath = [logsDirectory stringByAppendingPathComponent:actualFileName];
+
+        NSError *currentError = nil;
+        BOOL success = [fileHeader writeToFile:filePath options:NSDataWritingAtomic error:&currentError];
+
+#if TARGET_OS_IPHONE
+        if (success) {
+            // When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen.
+            //
+            // But in case if app is able to launch from background we need to have an ability to open log file any time we
+            // want (even if device is locked). Thats why that attribute have to be changed to
+            // NSFileProtectionCompleteUntilFirstUserAuthentication.
+            NSDictionary *attributes = @{NSFileProtectionKey: [self logFileProtection]};
+            success = [[NSFileManager defaultManager] setAttributes:attributes
+                                                       ofItemAtPath:filePath
+                                                              error:&currentError];
+        }
+#endif
+
+        if (success) {
+            NSLogVerbose(@"DDLogFileManagerDefault: Created new log file: %@", actualFileName);
+            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                // Since we just created a new log file, we may need to delete some old log files
+                [self deleteOldLogFiles];
+            });
+            return filePath;
+        } else if (currentError.code == NSFileWriteFileExistsError) {
+            attempt++;
+            continue;
+        } else {
+            NSLogError(@"DDLogFileManagerDefault: Critical error while creating log file: %@", currentError);
+            criticalErrors++;
+            lastCriticalError = currentError;
+            continue;
+        }
+
+        return filePath;
+    } while (YES);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Utility
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSString *)applicationName {
+    static NSString *_appName;
+    static dispatch_once_t onceToken;
+
+    dispatch_once(&onceToken, ^{
+        _appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
+
+        if (_appName.length == 0) {
+            _appName = [[NSProcessInfo processInfo] processName];
+        }
+
+        if (_appName.length == 0) {
+            _appName = @"";
+        }
+    });
+
+    return _appName;
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLogFileFormatterDefault () {
+    NSDateFormatter *_dateFormatter;
+}
+
+@end
+
+@implementation DDLogFileFormatterDefault
+
+- (instancetype)init {
+    return [self initWithDateFormatter:nil];
+}
+
+- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)aDateFormatter {
+    if ((self = [super init])) {
+        if (aDateFormatter) {
+            _dateFormatter = aDateFormatter;
+        } else {
+            _dateFormatter = [[NSDateFormatter alloc] init];
+            [_dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; // 10.4+ style
+            [_dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+            [_dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+            [_dateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];
+        }
+    }
+
+    return self;
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    NSString *dateAndTime = [_dateFormatter stringFromDate:logMessage->_timestamp];
+
+    return [NSString stringWithFormat:@"%@  %@", dateAndTime, logMessage->_message];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDFileLogger () {
+    id <DDLogFileManager> _logFileManager;
+
+    DDLogFileInfo *_currentLogFileInfo;
+    NSFileHandle *_currentLogFileHandle;
+
+    dispatch_source_t _currentLogFileVnode;
+
+    NSTimeInterval _rollingFrequency;
+    dispatch_source_t _rollingTimer;
+
+    unsigned long long _maximumFileSize;
+
+    dispatch_queue_t _completionQueue;
+}
+
+@end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wincomplete-implementation"
+@implementation DDFileLogger
+#pragma clang diagnostic pop
+
+- (instancetype)init {
+    DDLogFileManagerDefault *defaultLogFileManager = [[DDLogFileManagerDefault alloc] init];
+    return [self initWithLogFileManager:defaultLogFileManager completionQueue:nil];
+}
+
+- (instancetype)initWithLogFileManager:(id<DDLogFileManager>)logFileManager {
+    return [self initWithLogFileManager:logFileManager completionQueue:nil];
+}
+
+- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)aLogFileManager
+                       completionQueue:(nullable dispatch_queue_t)dispatchQueue {
+    if ((self = [super init])) {
+        _completionQueue = dispatchQueue ?: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+
+        _maximumFileSize = kDDDefaultLogMaxFileSize;
+        _rollingFrequency = kDDDefaultLogRollingFrequency;
+        _automaticallyAppendNewlineForCustomFormatters = YES;
+
+        _logFileManager = aLogFileManager;
+        _logFormatter = [DDLogFileFormatterDefault new];
+    }
+
+    return self;
+}
+
+- (void)lt_cleanup {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+        __autoreleasing NSError *error;
+        BOOL synchronized = [_currentLogFileHandle synchronizeAndReturnError:&error];
+        if (!synchronized) {
+            NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error);
+        }
+        BOOL closed = [_currentLogFileHandle closeAndReturnError:&error];
+        if (!closed) {
+            NSLogError(@"DDFileLogger: Failed to close file: %@", error);
+        }
+    } else {
+        [_currentLogFileHandle synchronizeFile];
+        [_currentLogFileHandle closeFile];
+    }
+        
+    if (_currentLogFileVnode) {
+        dispatch_source_cancel(_currentLogFileVnode);
+        _currentLogFileVnode = NULL;
+    }
+
+    if (_rollingTimer) {
+        dispatch_source_cancel(_rollingTimer);
+        _rollingTimer = NULL;
+    }
+}
+
+- (void)dealloc {
+    if (self.isOnInternalLoggerQueue) {
+        [self lt_cleanup];
+    } else {
+        dispatch_sync(self.loggerQueue, ^{
+            [self lt_cleanup];
+        });
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Properties
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (unsigned long long)maximumFileSize {
+    __block unsigned long long result;
+
+    dispatch_block_t block = ^{
+        result = self->_maximumFileSize;
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the maximumFileSize variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, block);
+    });
+
+    return result;
+}
+
+- (void)setMaximumFileSize:(unsigned long long)newMaximumFileSize {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            self->_maximumFileSize = newMaximumFileSize;
+            [self lt_maybeRollLogFileDueToSize];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the maximumFileSize variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_async(globalLoggingQueue, ^{
+        dispatch_async(self.loggerQueue, block);
+    });
+}
+
+- (NSTimeInterval)rollingFrequency {
+    __block NSTimeInterval result;
+
+    dispatch_block_t block = ^{
+        result = self->_rollingFrequency;
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation should access the rollingFrequency variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, block);
+    });
+
+    return result;
+}
+
+- (void)setRollingFrequency:(NSTimeInterval)newRollingFrequency {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            self->_rollingFrequency = newRollingFrequency;
+            [self lt_maybeRollLogFileDueToAge];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation should access the rollingFrequency variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_async(globalLoggingQueue, ^{
+        dispatch_async(self.loggerQueue, block);
+    });
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark File Rolling
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)lt_scheduleTimerToRollLogFileDueToAge {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    if (_rollingTimer) {
+        dispatch_source_cancel(_rollingTimer);
+        _rollingTimer = NULL;
+    }
+
+    if (_currentLogFileInfo == nil || _rollingFrequency <= 0.0) {
+        return;
+    }
+
+    NSDate *logFileCreationDate = [_currentLogFileInfo creationDate];
+    NSTimeInterval frequency = MIN(_rollingFrequency, DBL_MAX - [logFileCreationDate timeIntervalSinceReferenceDate]);
+    NSDate *logFileRollingDate = [logFileCreationDate dateByAddingTimeInterval:frequency];
+
+    NSLogVerbose(@"DDFileLogger: scheduleTimerToRollLogFileDueToAge");
+    NSLogVerbose(@"DDFileLogger: logFileCreationDate    : %@", logFileCreationDate);
+    NSLogVerbose(@"DDFileLogger: actual rollingFrequency: %f", frequency);
+    NSLogVerbose(@"DDFileLogger: logFileRollingDate     : %@", logFileRollingDate);
+
+    _rollingTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _loggerQueue);
+
+    __weak __auto_type weakSelf = self;
+    dispatch_source_set_event_handler(_rollingTimer, ^{ @autoreleasepool {
+        [weakSelf lt_maybeRollLogFileDueToAge];
+    } });
+
+    #if !OS_OBJECT_USE_OBJC
+    dispatch_source_t theRollingTimer = _rollingTimer;
+    dispatch_source_set_cancel_handler(_rollingTimer, ^{
+        dispatch_release(theRollingTimer);
+    });
+    #endif
+
+    static NSTimeInterval const kDDMaxTimerDelay = LLONG_MAX / NSEC_PER_SEC;
+    int64_t delay = (int64_t)(MIN([logFileRollingDate timeIntervalSinceNow], kDDMaxTimerDelay) * (NSTimeInterval) NSEC_PER_SEC);
+    dispatch_time_t fireTime = dispatch_time(DISPATCH_TIME_NOW, delay);
+
+    dispatch_source_set_timer(_rollingTimer, fireTime, DISPATCH_TIME_FOREVER, (uint64_t)kDDRollingLeeway * NSEC_PER_SEC);
+
+    if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *))
+        dispatch_activate(_rollingTimer);
+    else
+        dispatch_resume(_rollingTimer);
+}
+
+- (void)rollLogFile {
+    [self rollLogFileWithCompletionBlock:nil];
+}
+
+- (void)rollLogFileWithCompletionBlock:(nullable void (^)(void))completionBlock {
+    // This method is public.
+    // We need to execute the rolling on our logging thread/queue.
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self lt_rollLogFileNow];
+
+            if (completionBlock) {
+                dispatch_async(self->_completionQueue, ^{
+                    completionBlock();
+                });
+            }
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)lt_rollLogFileNow {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+    NSLogVerbose(@"DDFileLogger: rollLogFileNow");
+
+    if (_currentLogFileHandle == nil) {
+        return;
+    }
+    
+    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+        __autoreleasing NSError *error;
+        BOOL synchronized = [_currentLogFileHandle synchronizeAndReturnError:&error];
+        if (!synchronized) {
+            NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error);
+        }
+        BOOL closed = [_currentLogFileHandle closeAndReturnError:&error];
+        if (!closed) {
+            NSLogError(@"DDFileLogger: Failed to close file: %@", error);
+        }
+    } else {
+        [_currentLogFileHandle synchronizeFile];
+        [_currentLogFileHandle closeFile];
+    }
+    _currentLogFileHandle = nil;
+
+    _currentLogFileInfo.isArchived = YES;
+
+    const BOOL logFileManagerRespondsToNewArchiveSelector = [_logFileManager respondsToSelector:@selector(didArchiveLogFile:wasRolled:)];
+    const BOOL logFileManagerRespondsToSelector = (logFileManagerRespondsToNewArchiveSelector
+                                                   || [_logFileManager respondsToSelector:@selector(didRollAndArchiveLogFile:)]);
+    NSString *archivedFilePath = (logFileManagerRespondsToSelector) ? [_currentLogFileInfo.filePath copy] : nil;
+    _currentLogFileInfo = nil;
+
+    if (logFileManagerRespondsToSelector) {
+        dispatch_block_t block;
+        if (logFileManagerRespondsToNewArchiveSelector) {
+            block = ^{
+                [self->_logFileManager didArchiveLogFile:archivedFilePath wasRolled:YES];
+            };
+        } else {
+            block = ^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+                [self->_logFileManager didRollAndArchiveLogFile:archivedFilePath];
+#pragma clang diagnostic pop
+            };
+        }
+        dispatch_async(_completionQueue, block);
+    }
+
+    if (_currentLogFileVnode) {
+        dispatch_source_cancel(_currentLogFileVnode);
+        _currentLogFileVnode = nil;
+    }
+
+    if (_rollingTimer) {
+        dispatch_source_cancel(_rollingTimer);
+        _rollingTimer = nil;
+    }
+}
+
+- (void)lt_maybeRollLogFileDueToAge {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    if (_rollingFrequency > 0.0 && (_currentLogFileInfo.age + kDDRollingLeeway) >= _rollingFrequency) {
+        NSLogVerbose(@"DDFileLogger: Rolling log file due to age...");
+        [self lt_rollLogFileNow];
+    } else {
+        [self lt_scheduleTimerToRollLogFileDueToAge];
+    }
+}
+
+- (void)lt_maybeRollLogFileDueToSize {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    // This method is called from logMessage.
+    // Keep it FAST.
+
+    // Note: Use direct access to maximumFileSize variable.
+    // We specifically wrote our own getter/setter method to allow us to do this (for performance reasons).
+
+    if (_maximumFileSize > 0) {
+        unsigned long long fileSize;
+        if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+            __autoreleasing NSError *error;
+            BOOL succeed = [_currentLogFileHandle getOffset:&fileSize error:&error];
+            if (!succeed) {
+                NSLogError(@"DDFileLogger: Failed to get offset: %@", error);
+                return;
+            }
+        } else {
+            fileSize = [_currentLogFileHandle offsetInFile];
+        }
+
+        if (fileSize >= _maximumFileSize) {
+            NSLogVerbose(@"DDFileLogger: Rolling log file due to size (%qu)...", fileSize);
+
+            [self lt_rollLogFileNow];
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark File Logging
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)lt_shouldLogFileBeArchived:(DDLogFileInfo *)mostRecentLogFileInfo {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    if ([self shouldArchiveRecentLogFileInfo:mostRecentLogFileInfo]) {
+        return YES;
+    } else if (_maximumFileSize > 0 && mostRecentLogFileInfo.fileSize >= _maximumFileSize) {
+        return YES;
+    } else if (_rollingFrequency > 0.0 && mostRecentLogFileInfo.age >= _rollingFrequency) {
+        return YES;
+    }
+
+#if TARGET_OS_IPHONE
+    // When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen.
+    //
+    // But in case if app is able to launch from background we need to have an ability to open log file any time we
+    // want (even if device is locked). Thats why that attribute have to be changed to
+    // NSFileProtectionCompleteUntilFirstUserAuthentication.
+    //
+    // If previous log was created when app wasn't running in background, but now it is - we archive it and create
+    // a new one.
+    //
+    // If user has overwritten to NSFileProtectionNone there is no need to create a new one.
+    if (doesAppRunInBackground()) {
+        NSFileProtectionType key = mostRecentLogFileInfo.fileAttributes[NSFileProtectionKey];
+        BOOL isUntilFirstAuth = [key isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication];
+        BOOL isNone = [key isEqualToString:NSFileProtectionNone];
+
+        if (key != nil && !isUntilFirstAuth && !isNone) {
+            return YES;
+        }
+    }
+#endif
+
+    return NO;
+}
+
+/**
+ * Returns the log file that should be used.
+ * If there is an existing log file that is suitable, within the
+ * constraints of maximumFileSize and rollingFrequency, then it is returned.
+ *
+ * Otherwise a new file is created and returned.
+ **/
+- (DDLogFileInfo *)currentLogFileInfo {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+    // Do not access this method on any Lumberjack queue, will deadlock.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    __block DDLogFileInfo *info = nil;
+    dispatch_block_t block = ^{
+        info = [self lt_currentLogFileInfo];
+    };
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self->_loggerQueue, block);
+    });
+
+    return info;
+}
+
+- (DDLogFileInfo *)lt_currentLogFileInfo {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    // Get the current log file info ivar (might be nil).
+    DDLogFileInfo *newCurrentLogFile = _currentLogFileInfo;
+
+    // Check if we're resuming and if so, get the first of the sorted log file infos.
+    BOOL isResuming = newCurrentLogFile == nil;
+    if (isResuming) {
+        NSArray *sortedLogFileInfos = [_logFileManager sortedLogFileInfos];
+        newCurrentLogFile = sortedLogFileInfos.firstObject;
+    }
+
+    // Check if the file we've found is still valid. Otherwise create a new one.
+    if (newCurrentLogFile != nil && [self lt_shouldUseLogFile:newCurrentLogFile isResuming:isResuming]) {
+        if (isResuming) {
+            NSLogVerbose(@"DDFileLogger: Resuming logging with file %@", newCurrentLogFile.fileName);
+        }
+        _currentLogFileInfo = newCurrentLogFile;
+    } else {
+        NSString *currentLogFilePath;
+        if ([_logFileManager respondsToSelector:@selector(createNewLogFileWithError:)]) {
+            __autoreleasing NSError *error;
+            currentLogFilePath = [_logFileManager createNewLogFileWithError:&error];
+            if (!currentLogFilePath) {
+                NSLogError(@"DDFileLogger: Failed to create new log file: %@", error);
+            }
+        } else {
+            #pragma clang diagnostic push
+            #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            NSAssert([_logFileManager respondsToSelector:@selector(createNewLogFile)],
+                     @"Invalid log file manager! Responds neither to `-createNewLogFileWithError:` nor `-createNewLogFile`!");
+            currentLogFilePath = [_logFileManager createNewLogFile];
+            #pragma clang diagnostic pop
+        }
+        // Use static factory method here, since it checks for nil (and is unavailable to Swift).
+        _currentLogFileInfo = [DDLogFileInfo logFileWithPath:currentLogFilePath];
+    }
+
+    return _currentLogFileInfo;
+}
+
+- (BOOL)lt_shouldUseLogFile:(nonnull DDLogFileInfo *)logFileInfo isResuming:(BOOL)isResuming {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+    NSParameterAssert(logFileInfo);
+
+    // Check if the log file is archived. We must not use archived log files.
+    if (logFileInfo.isArchived) {
+        return NO;
+    }
+
+    // If we're resuming, we need to check if the log file is allowed for reuse or needs to be archived.
+    if (isResuming && (_doNotReuseLogFiles || [self lt_shouldLogFileBeArchived:logFileInfo])) {
+        logFileInfo.isArchived = YES;
+
+        const BOOL logFileManagerRespondsToNewArchiveSelector = [_logFileManager respondsToSelector:@selector(didArchiveLogFile:wasRolled:)];
+        if (logFileManagerRespondsToNewArchiveSelector || [_logFileManager respondsToSelector:@selector(didArchiveLogFile:)]) {
+            NSString *archivedFilePath = [logFileInfo.filePath copy];
+            dispatch_block_t block;
+            if (logFileManagerRespondsToNewArchiveSelector) {
+                block = ^{
+                    [self->_logFileManager didArchiveLogFile:archivedFilePath wasRolled:NO];
+                };
+            } else {
+                block = ^{
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+                    [self->_logFileManager didArchiveLogFile:archivedFilePath];
+    #pragma clang diagnostic pop
+                };
+            }
+            dispatch_async(_completionQueue, block);
+        }
+
+        return NO;
+    }
+
+    // All checks have passed. It's valid.
+    return YES;
+}
+
+- (void)lt_monitorCurrentLogFileForExternalChanges {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+    NSAssert(_currentLogFileHandle, @"Can not monitor without handle.");
+
+    _currentLogFileVnode = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
+                                                  (uintptr_t)[_currentLogFileHandle fileDescriptor],
+                                                  DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
+                                                  _loggerQueue);
+
+    __weak __auto_type weakSelf = self;
+    dispatch_source_set_event_handler(_currentLogFileVnode, ^{ @autoreleasepool {
+        NSLogInfo(@"DDFileLogger: Current logfile was moved. Rolling it and creating a new one");
+        [weakSelf lt_rollLogFileNow];
+    } });
+
+#if !OS_OBJECT_USE_OBJC
+    dispatch_source_t vnode = _currentLogFileVnode;
+    dispatch_source_set_cancel_handler(_currentLogFileVnode, ^{
+        dispatch_release(vnode);
+    });
+#endif
+
+    if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
+        dispatch_activate(_currentLogFileVnode);
+    } else {
+        dispatch_resume(_currentLogFileVnode);
+    }
+}
+
+- (NSFileHandle *)lt_currentLogFileHandle {
+    NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
+
+    if (!_currentLogFileHandle) {
+        NSString *logFilePath = [[self lt_currentLogFileInfo] filePath];
+        _currentLogFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
+        if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+            __autoreleasing NSError *error;
+            BOOL succeed = [_currentLogFileHandle seekToEndReturningOffset:nil error:&error];
+            if (!succeed) {
+                NSLogError(@"DDFileLogger: Failed to seek to end of file: %@", error);
+            }
+        } else {
+            [_currentLogFileHandle seekToEndOfFile];
+        }
+
+        if (_currentLogFileHandle) {
+            [self lt_scheduleTimerToRollLogFileDueToAge];
+            [self lt_monitorCurrentLogFileForExternalChanges];
+        }
+    }
+
+    return _currentLogFileHandle;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark DDLogger Protocol
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int exception_count = 0;
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
+    NSData *data = [self lt_dataForMessage:logMessage];
+
+    if (data.length == 0) {
+        return;
+    }
+
+    [self lt_logData:data];
+}
+
+- (void)willLogMessage:(DDLogFileInfo *)logFileInfo {}
+
+- (void)didLogMessage:(DDLogFileInfo *)logFileInfo {
+    [self lt_maybeRollLogFileDueToSize];
+}
+
+- (BOOL)shouldArchiveRecentLogFileInfo:(__unused DDLogFileInfo *)recentLogFileInfo {
+    return NO;
+}
+
+- (void)willRemoveLogger {
+    [self lt_rollLogFileNow];
+}
+
+- (void)flush {
+    // This method is public.
+    // We need to execute the rolling on our logging thread/queue.
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self lt_flush];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_sync(globalLoggingQueue, ^{
+            dispatch_sync(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)lt_flush {
+    NSAssert([self isOnInternalLoggerQueue], @"flush should only be executed on internal queue.");
+    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+        __autoreleasing NSError *error;
+        BOOL succeed = [_currentLogFileHandle synchronizeAndReturnError:&error];
+        if (!succeed) {
+            NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error);
+        }
+    } else {
+        [_currentLogFileHandle synchronizeFile];
+    }
+}
+
+- (DDLoggerName)loggerName {
+    return DDLoggerNameFile;
+}
+
+@end
+
+@implementation DDFileLogger (Internal)
+
+- (void)logData:(NSData *)data {
+    // This method is public.
+    // We need to execute the rolling on our logging thread/queue.
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self lt_logData:data];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_sync(globalLoggingQueue, ^{
+            dispatch_sync(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)lt_deprecationCatchAll {}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
+    if (aSelector == @selector(willLogMessage) || aSelector == @selector(didLogMessage)) {
+        // Ignore calls to deprecated methods.
+        return [self methodSignatureForSelector:@selector(lt_deprecationCatchAll)];
+    }
+
+    return [super methodSignatureForSelector:aSelector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)anInvocation {
+    if (anInvocation.selector != @selector(lt_deprecationCatchAll)) {
+        [super forwardInvocation:anInvocation];
+    }
+}
+
+- (void)lt_logData:(NSData *)data {
+    static BOOL implementsDeprecatedWillLog = NO;
+    static BOOL implementsDeprecatedDidLog = NO;
+
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        implementsDeprecatedWillLog = [self respondsToSelector:@selector(willLogMessage)];
+        implementsDeprecatedDidLog = [self respondsToSelector:@selector(didLogMessage)];
+    });
+
+    NSAssert([self isOnInternalLoggerQueue], @"logMessage should only be executed on internal queue.");
+
+    if (data.length == 0) {
+        return;
+    }
+
+    @try {
+        if (implementsDeprecatedWillLog) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            [self willLogMessage];
+#pragma clang diagnostic pop
+        } else {
+            [self willLogMessage:_currentLogFileInfo];
+        }
+
+        NSFileHandle *handle = [self lt_currentLogFileHandle];
+        if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+            __autoreleasing NSError *error;
+            BOOL sought = [handle seekToEndReturningOffset:nil error:&error];
+            if (!sought) {
+                NSLogError(@"DDFileLogger: Failed to seek to end of file: %@", error);
+            }
+            BOOL written =  [handle writeData:data error:&error];
+            if (!written) {
+                NSLogError(@"DDFileLogger: Failed to write data: %@", error);
+            }
+        } else {
+            [handle seekToEndOfFile];
+            [handle writeData:data];
+        }
+
+        if (implementsDeprecatedDidLog) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            [self didLogMessage];
+#pragma clang diagnostic pop
+        } else {
+            [self didLogMessage:_currentLogFileInfo];
+        }
+
+    } @catch (NSException *exception) {
+        exception_count++;
+
+        if (exception_count <= 10) {
+            NSLogError(@"DDFileLogger.logMessage: %@", exception);
+
+            if (exception_count == 10) {
+                NSLogError(@"DDFileLogger.logMessage: Too many exceptions -- will not log any more of them.");
+            }
+        }
+    }
+}
+
+- (NSData *)lt_dataForMessage:(DDLogMessage *)logMessage {
+    NSAssert([self isOnInternalLoggerQueue], @"logMessage should only be executed on internal queue.");
+
+    NSString *message = logMessage->_message;
+    BOOL isFormatted = NO;
+
+    if (_logFormatter != nil) {
+        message = [_logFormatter formatLogMessage:logMessage];
+        isFormatted = message != logMessage->_message;
+    }
+
+    if (message.length == 0) {
+        return nil;
+    }
+
+    BOOL shouldFormat = !isFormatted || _automaticallyAppendNewlineForCustomFormatters;
+    if (shouldFormat && ![message hasSuffix:@"\n"]) {
+        message = [message stringByAppendingString:@"\n"];
+    }
+
+    return [message dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static NSString * const kDDXAttrArchivedName = @"lumberjack.log.archived";
+
+@interface DDLogFileInfo () {
+    __strong NSString *_filePath;
+    __strong NSString *_fileName;
+
+    __strong NSDictionary *_fileAttributes;
+
+    __strong NSDate *_creationDate;
+    __strong NSDate *_modificationDate;
+
+    unsigned long long _fileSize;
+}
+
+#if TARGET_IPHONE_SIMULATOR
+
+// Old implementation of extended attributes on the simulator.
+
+- (BOOL)_hasExtensionAttributeWithName:(NSString *)attrName;
+- (void)_removeExtensionAttributeWithName:(NSString *)attrName;
+
+#endif
+
+@end
+
+
+@implementation DDLogFileInfo
+
+@synthesize filePath;
+
+@dynamic fileName;
+@dynamic fileAttributes;
+@dynamic creationDate;
+@dynamic modificationDate;
+@dynamic fileSize;
+@dynamic age;
+
+@dynamic isArchived;
+
+#pragma mark Lifecycle
+
++ (instancetype)logFileWithPath:(NSString *)aFilePath {
+    if (!aFilePath) return nil;
+    return [[self alloc] initWithFilePath:aFilePath];
+}
+
+- (instancetype)initWithFilePath:(NSString *)aFilePath {
+    NSParameterAssert(aFilePath);
+    if ((self = [super init])) {
+        filePath = [aFilePath copy];
+    }
+
+    return self;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Standard Info
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSDictionary *)fileAttributes {
+    if (_fileAttributes == nil && filePath != nil) {
+        NSError *error = nil;
+        _fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
+
+        if (error) {
+            NSLogError(@"DDLogFileInfo: Failed to read file attributes: %@", error);
+        }
+    }
+
+    return _fileAttributes ?: @{};
+}
+
+- (NSString *)fileName {
+    if (_fileName == nil) {
+        _fileName = [filePath lastPathComponent];
+    }
+
+    return _fileName;
+}
+
+- (NSDate *)modificationDate {
+    if (_modificationDate == nil) {
+        _modificationDate = self.fileAttributes[NSFileModificationDate];
+    }
+
+    return _modificationDate;
+}
+
+- (NSDate *)creationDate {
+    if (_creationDate == nil) {
+        _creationDate = self.fileAttributes[NSFileCreationDate];
+    }
+
+    return _creationDate;
+}
+
+- (unsigned long long)fileSize {
+    if (_fileSize == 0) {
+        _fileSize = [self.fileAttributes[NSFileSize] unsignedLongLongValue];
+    }
+
+    return _fileSize;
+}
+
+- (NSTimeInterval)age {
+    return -[[self creationDate] timeIntervalSinceNow];
+}
+
+- (NSString *)description {
+    return [@{ @"filePath": self.filePath ? : @"",
+               @"fileName": self.fileName ? : @"",
+               @"fileAttributes": self.fileAttributes ? : @"",
+               @"creationDate": self.creationDate ? : @"",
+               @"modificationDate": self.modificationDate ? : @"",
+               @"fileSize": @(self.fileSize),
+               @"age": @(self.age),
+               @"isArchived": @(self.isArchived) } description];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Archiving
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)isArchived {
+    return [self hasExtendedAttributeWithName:kDDXAttrArchivedName];
+}
+
+- (void)setIsArchived:(BOOL)flag {
+    if (flag) {
+        [self addExtendedAttributeWithName:kDDXAttrArchivedName];
+    } else {
+        [self removeExtendedAttributeWithName:kDDXAttrArchivedName];
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Changes
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)reset {
+    _fileName = nil;
+    _fileAttributes = nil;
+    _creationDate = nil;
+    _modificationDate = nil;
+}
+
+- (void)renameFile:(NSString *)newFileName {
+    // This method is only used on the iPhone simulator, where normal extended attributes are broken.
+    // See full explanation in the header file.
+
+    if (![newFileName isEqualToString:[self fileName]]) {
+        NSFileManager* fileManager = [NSFileManager defaultManager];
+        NSString *fileDir = [filePath stringByDeletingLastPathComponent];
+        NSString *newFilePath = [fileDir stringByAppendingPathComponent:newFileName];
+
+        // We only want to assert when we're not using the simulator, as we're "archiving" a log file with this method in the sim
+        // (in which case the file might not exist anymore and neither does it parent folder).
+#if defined(DEBUG) && (!defined(TARGET_IPHONE_SIMULATOR) || !TARGET_IPHONE_SIMULATOR)
+        BOOL directory = NO;
+        [fileManager fileExistsAtPath:fileDir isDirectory:&directory];
+        NSAssert(directory, @"Containing directory must exist.");
+#endif
+
+        NSError *error = nil;
+
+        BOOL success = [fileManager removeItemAtPath:newFilePath error:&error];
+        if (!success && error.code != NSFileNoSuchFileError) {
+            NSLogError(@"DDLogFileInfo: Error deleting archive (%@): %@", self.fileName, error);
+        }
+
+        success = [fileManager moveItemAtPath:filePath toPath:newFilePath error:&error];
+
+        // When a log file is deleted, moved or renamed on the simulator, we attempt to rename it as a
+        // result of "archiving" it, but since the file doesn't exist anymore, needless error logs are printed
+        // We therefore ignore this error, and assert that the directory we are copying into exists (which
+        // is the only other case where this error code can come up).
+#if TARGET_IPHONE_SIMULATOR
+        if (!success && error.code != NSFileNoSuchFileError)
+#else
+        if (!success)
+#endif
+        {
+            NSLogError(@"DDLogFileInfo: Error renaming file (%@): %@", self.fileName, error);
+        }
+
+        filePath = newFilePath;
+        [self reset];
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Attribute Management
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if TARGET_IPHONE_SIMULATOR
+
+// Old implementation of extended attributes on the simulator.
+
+// Extended attributes were not working properly on the simulator
+// due to misuse of setxattr() function.
+// Now that this is fixed in the new implementation, we want to keep
+// backward compatibility with previous simulator installations.
+
+static NSString * const kDDExtensionSeparator = @".";
+
+static NSString *_xattrToExtensionName(NSString *attrName) {
+    static NSDictionary<NSString *, NSString *>* _xattrToExtensionNameMap;
+    static dispatch_once_t _token;
+    dispatch_once(&_token, ^{
+        _xattrToExtensionNameMap = @{ kDDXAttrArchivedName: @"archived" };
+    });
+    return [_xattrToExtensionNameMap objectForKey:attrName];
+}
+
+- (BOOL)_hasExtensionAttributeWithName:(NSString *)attrName {
+    // This method is only used on the iPhone simulator for backward compatibility reason.
+
+    // Split the file name into components. File name may have various format, but generally
+    // structure is same:
+    //
+    // <name part>.<extension part> and <name part>.archived.<extension part>
+    // or
+    // <name part> and <name part>.archived
+    //
+    // So we want to search for the attrName in the components (ignoring the first array index).
+
+    NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator];
+
+    // Watch out for file names without an extension
+
+    for (NSUInteger i = 1; i < components.count; i++) {
+        NSString *attr = components[i];
+
+        if ([attrName isEqualToString:attr]) {
+            return YES;
+        }
+    }
+
+    return NO;
+}
+
+- (void)_removeExtensionAttributeWithName:(NSString *)attrName {
+    // This method is only used on the iPhone simulator for backward compatibility reason.
+
+    if ([attrName length] == 0) {
+        return;
+    }
+
+    // Example:
+    // attrName = "archived"
+    //
+    // "mylog.archived.txt" -> "mylog.txt"
+    // "mylog.archived"     -> "mylog"
+
+    NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator];
+
+    NSUInteger count = [components count];
+
+    NSUInteger estimatedNewLength = [[self fileName] length];
+    NSMutableString *newFileName = [NSMutableString stringWithCapacity:estimatedNewLength];
+
+    if (count > 0) {
+        [newFileName appendString:components.firstObject];
+    }
+
+    BOOL found = NO;
+
+    NSUInteger i;
+
+    for (i = 1; i < count; i++) {
+        NSString *attr = components[i];
+
+        if ([attrName isEqualToString:attr]) {
+            found = YES;
+        } else {
+            [newFileName appendString:kDDExtensionSeparator];
+            [newFileName appendString:attr];
+        }
+    }
+
+    if (found) {
+        [self renameFile:newFileName];
+    }
+}
+
+#endif /* if TARGET_IPHONE_SIMULATOR */
+
+- (BOOL)hasExtendedAttributeWithName:(NSString *)attrName {
+    const char *path = [filePath fileSystemRepresentation];
+    const char *name = [attrName UTF8String];
+    BOOL hasExtendedAttribute = NO;
+    char buffer[1];
+
+    ssize_t result = getxattr(path, name, buffer, 1, 0, 0);
+
+    // Fast path
+    if (result > 0 && buffer[0] == '\1') {
+        hasExtendedAttribute = YES;
+    }
+    // Maintain backward compatibility, but fix it for future checks
+    else if (result >= 0) {
+        hasExtendedAttribute = YES;
+
+        [self addExtendedAttributeWithName:attrName];
+    }
+#if TARGET_IPHONE_SIMULATOR
+    else if ([self _hasExtensionAttributeWithName:_xattrToExtensionName(attrName)]) {
+        hasExtendedAttribute = YES;
+
+        [self addExtendedAttributeWithName:attrName];
+    }
+#endif
+
+    return hasExtendedAttribute;
+}
+
+- (void)addExtendedAttributeWithName:(NSString *)attrName {
+    const char *path = [filePath fileSystemRepresentation];
+    const char *name = [attrName UTF8String];
+
+    int result = setxattr(path, name, "\1", 1, 0, 0);
+
+    if (result < 0) {
+        if (errno != ENOENT) {
+            NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %s",
+                       attrName,
+                       filePath,
+                       strerror(errno));
+        } else {
+            NSLogDebug(@"DDLogFileInfo: File does not exist in setxattr(%@, %@): error = %s",
+                       attrName,
+                       filePath,
+                       strerror(errno));
+        }
+    }
+#if TARGET_IPHONE_SIMULATOR
+    else {
+        [self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)];
+    }
+#endif
+}
+
+- (void)removeExtendedAttributeWithName:(NSString *)attrName {
+    const char *path = [filePath fileSystemRepresentation];
+    const char *name = [attrName UTF8String];
+
+    int result = removexattr(path, name, 0);
+
+    if (result < 0 && errno != ENOATTR) {
+        NSLogError(@"DDLogFileInfo: removexattr(%@, %@): error = %s",
+                   attrName,
+                   self.fileName,
+                   strerror(errno));
+    }
+
+#if TARGET_IPHONE_SIMULATOR
+    [self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)];
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Comparisons
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)isEqual:(id)object {
+    if ([object isKindOfClass:[self class]]) {
+        DDLogFileInfo *another = (DDLogFileInfo *)object;
+
+        return [filePath isEqualToString:[another filePath]];
+    }
+
+    return NO;
+}
+
+- (NSUInteger)hash {
+    return [filePath hash];
+}
+
+- (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another {
+    __auto_type us = [self creationDate];
+    __auto_type them = [another creationDate];
+    return [them compare:us];
+}
+
+- (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another {
+    __auto_type us = [self modificationDate];
+    __auto_type them = [another modificationDate];
+    return [them compare:us];
+}
+
+@end
+
+#if TARGET_OS_IPHONE
+/**
+ * When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen.
+ *
+ * But in case if app is able to launch from background we need to have an ability to open log file any time we
+ * want (even if device is locked). Thats why that attribute have to be changed to
+ * NSFileProtectionCompleteUntilFirstUserAuthentication.
+ */
+BOOL doesAppRunInBackground() {
+    BOOL answer = NO;
+
+    NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
+
+    for (NSString *mode in backgroundModes) {
+        if (mode.length > 0) {
+            answer = YES;
+            break;
+        }
+    }
+
+    return answer;
+}
+
+#endif

+ 1308 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m

@@ -0,0 +1,1308 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <pthread.h>
+#import <objc/runtime.h>
+#import <sys/qos.h>
+
+#if TARGET_OS_IOS
+    #import <UIKit/UIDevice.h>
+    #import <UIKit/UIApplication.h>
+#elif !defined(DD_CLI) && __has_include(<AppKit/NSApplication.h>)
+    #import <AppKit/NSApplication.h>
+#endif
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+// We probably shouldn't be using DDLog() statements within the DDLog implementation.
+// But we still want to leave our log statements for any future debugging,
+// and to allow other developers to trace the implementation (which is a great learning tool).
+//
+// So we use a primitive logging macro around NSLog.
+// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
+
+#ifndef DD_DEBUG
+    #define DD_DEBUG 0
+#endif
+
+#define NSLogDebug(frmt, ...) do{ if(DD_DEBUG) NSLog((frmt), ##__VA_ARGS__); } while(0)
+
+// The "global logging queue" refers to [DDLog loggingQueue].
+// It is the queue that all log statements go through.
+//
+// The logging queue sets a flag via dispatch_queue_set_specific using this key.
+// We can check for this key via dispatch_get_specific() to see if we're on the "global logging queue".
+
+static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueIdentityKey;
+
+@interface DDLoggerNode : NSObject
+{
+    // Direct accessors to be used only for performance
+    @public
+    id <DDLogger> _logger;
+    DDLogLevel _level;
+    dispatch_queue_t _loggerQueue;
+}
+
+@property (nonatomic, readonly) id <DDLogger> logger;
+@property (nonatomic, readonly) DDLogLevel level;
+@property (nonatomic, readonly) dispatch_queue_t loggerQueue;
+
++ (instancetype)nodeWithLogger:(id <DDLogger>)logger
+                   loggerQueue:(dispatch_queue_t)loggerQueue
+                         level:(DDLogLevel)level;
+
+@end
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLog ()
+
+// An array used to manage all the individual loggers.
+// The array is only modified on the loggingQueue/loggingThread.
+@property (nonatomic, strong) NSMutableArray *_loggers;
+
+@end
+
+@implementation DDLog
+
+// All logging statements are added to the same queue to ensure FIFO operation.
+static dispatch_queue_t _loggingQueue;
+
+// Individual loggers are executed concurrently per log statement.
+// Each logger has it's own associated queue, and a dispatch group is used for synchronization.
+static dispatch_group_t _loggingGroup;
+
+// Minor optimization for uniprocessor machines
+static NSUInteger _numProcessors;
+
+/**
+ *  Returns the singleton `DDLog`.
+ *  The instance is used by `DDLog` class methods.
+ *
+ *  @return The singleton `DDLog`.
+ */
++ (instancetype)sharedInstance {
+    static id sharedInstance = nil;
+
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        sharedInstance = [[self alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+/**
+ * The runtime sends initialize to each class in a program exactly one time just before the class,
+ * or any class that inherits from it, is sent its first message from within the program. (Thus the
+ * method may never be invoked if the class is not used.) The runtime sends the initialize message to
+ * classes in a thread-safe manner. Superclasses receive this message before their subclasses.
+ *
+ * This method may also be called directly, hence the safety mechanism.
+ **/
++ (void)initialize {
+    static dispatch_once_t DDLogOnceToken;
+
+    dispatch_once(&DDLogOnceToken, ^{
+        NSLogDebug(@"DDLog: Using grand central dispatch");
+
+        _loggingQueue = dispatch_queue_create("cocoa.lumberjack", NULL);
+        _loggingGroup = dispatch_group_create();
+
+        void *nonNullValue = GlobalLoggingQueueIdentityKey; // Whatever, just not null
+        dispatch_queue_set_specific(_loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL);
+
+        // Figure out how many processors are available.
+        // This may be used later for an optimization on uniprocessor machines.
+
+        _numProcessors = MAX([NSProcessInfo processInfo].processorCount, (NSUInteger) 1);
+
+        NSLogDebug(@"DDLog: numProcessors = %@", @(_numProcessors));
+    });
+}
+
+/**
+ *  The `DDLog` initializer.
+ *  Static variables are set only once.
+ *
+ *  @return An initialized `DDLog` instance.
+ */
+- (instancetype)init {
+    self = [super init];
+
+    if (self) {
+        self._loggers = [[NSMutableArray alloc] initWithCapacity:4];
+
+#if TARGET_OS_IOS
+        NSString *notificationName = UIApplicationWillTerminateNotification;
+#else
+        NSString *notificationName = nil;
+
+        // On Command Line Tool apps AppKit may not be available
+#if !defined(DD_CLI) && __has_include(<AppKit/NSApplication.h>)
+        if (NSApp) {
+            notificationName = NSApplicationWillTerminateNotification;
+        }
+#endif
+
+        if (!notificationName) {
+            // If there is no NSApp -> we are running Command Line Tool app.
+            // In this case terminate notification wouldn't be fired, so we use workaround.
+            __weak __auto_type weakSelf = self;
+            atexit_b (^{
+                [weakSelf applicationWillTerminate:nil];
+            });
+        }
+
+#endif /* if TARGET_OS_IOS */
+
+        if (notificationName) {
+            [[NSNotificationCenter defaultCenter] addObserver:self
+                                                     selector:@selector(applicationWillTerminate:)
+                                                         name:notificationName
+                                                       object:nil];
+        }
+    }
+
+    return self;
+}
+
+/**
+ * Provides access to the logging queue.
+ **/
++ (dispatch_queue_t)loggingQueue {
+    return _loggingQueue;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Notifications
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)applicationWillTerminate:(NSNotification * __attribute__((unused)))notification {
+    [self flushLog];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Logger Management
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
++ (void)addLogger:(id <DDLogger>)logger {
+    [self.sharedInstance addLogger:logger];
+}
+
+- (void)addLogger:(id <DDLogger>)logger {
+    [self addLogger:logger withLevel:DDLogLevelAll]; // DDLogLevelAll has all bits set
+}
+
++ (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level {
+    [self.sharedInstance addLogger:logger withLevel:level];
+}
+
+- (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level {
+    if (!logger) {
+        return;
+    }
+
+    dispatch_async(_loggingQueue, ^{ @autoreleasepool {
+        [self lt_addLogger:logger level:level];
+    } });
+}
+
++ (void)removeLogger:(id <DDLogger>)logger {
+    [self.sharedInstance removeLogger:logger];
+}
+
+- (void)removeLogger:(id <DDLogger>)logger {
+    if (!logger) {
+        return;
+    }
+
+    dispatch_async(_loggingQueue, ^{ @autoreleasepool {
+        [self lt_removeLogger:logger];
+    } });
+}
+
++ (void)removeAllLoggers {
+    [self.sharedInstance removeAllLoggers];
+}
+
+- (void)removeAllLoggers {
+    dispatch_async(_loggingQueue, ^{ @autoreleasepool {
+        [self lt_removeAllLoggers];
+    } });
+}
+
++ (NSArray<id<DDLogger>> *)allLoggers {
+    return [self.sharedInstance allLoggers];
+}
+
+- (NSArray<id<DDLogger>> *)allLoggers {
+    __block NSArray *theLoggers;
+
+    dispatch_sync(_loggingQueue, ^{ @autoreleasepool {
+        theLoggers = [self lt_allLoggers];
+    } });
+
+    return theLoggers;
+}
+
++ (NSArray<DDLoggerInformation *> *)allLoggersWithLevel {
+    return [self.sharedInstance allLoggersWithLevel];
+}
+
+- (NSArray<DDLoggerInformation *> *)allLoggersWithLevel {
+    __block NSArray *theLoggersWithLevel;
+
+    dispatch_sync(_loggingQueue, ^{ @autoreleasepool {
+        theLoggersWithLevel = [self lt_allLoggersWithLevel];
+    } });
+
+    return theLoggersWithLevel;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark - Master Logging
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)queueLogMessage:(DDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag {
+    // We have a tricky situation here...
+    //
+    // In the common case, when the queueSize is below the maximumQueueSize,
+    // we want to simply enqueue the logMessage. And we want to do this as fast as possible,
+    // which means we don't want to block and we don't want to use any locks.
+    //
+    // However, if the queueSize gets too big, we want to block.
+    // But we have very strict requirements as to when we block, and how long we block.
+    //
+    // The following example should help illustrate our requirements:
+    //
+    // Imagine that the maximum queue size is configured to be 5,
+    // and that there are already 5 log messages queued.
+    // Let us call these 5 queued log messages A, B, C, D, and E. (A is next to be executed)
+    //
+    // Now if our thread issues a log statement (let us call the log message F),
+    // it should block before the message is added to the queue.
+    // Furthermore, it should be unblocked immediately after A has been unqueued.
+    //
+    // The requirements are strict in this manner so that we block only as long as necessary,
+    // and so that blocked threads are unblocked in the order in which they were blocked.
+    //
+    // Returning to our previous example, let us assume that log messages A through E are still queued.
+    // Our aforementioned thread is blocked attempting to queue log message F.
+    // Now assume we have another separate thread that attempts to issue log message G.
+    // It should block until log messages A and B have been unqueued.
+
+    dispatch_block_t logBlock = ^{
+        // We're now sure we won't overflow the queue.
+        // It is time to queue our log message.
+        @autoreleasepool {
+            [self lt_log:logMessage];
+        }
+    };
+
+    if (asyncFlag) {
+        dispatch_async(_loggingQueue, logBlock);
+    } else if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) {
+        // We've logged an error message while on the logging queue...
+        logBlock();
+    } else {
+        dispatch_sync(_loggingQueue, logBlock);
+    }
+}
+
++ (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag
+     format:(NSString *)format, ... {
+    va_list args;
+
+    if (format) {
+        va_start(args, format);
+
+        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
+
+        va_end(args);
+
+        va_start(args, format);
+
+        [self log:asynchronous
+          message:message
+            level:level
+             flag:flag
+          context:context
+             file:file
+         function:function
+             line:line
+              tag:tag];
+
+        va_end(args);
+    }
+}
+
+- (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag
+     format:(NSString *)format, ... {
+    va_list args;
+
+    if (format) {
+        va_start(args, format);
+
+        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
+
+        va_end(args);
+
+        va_start(args, format);
+
+        [self log:asynchronous
+          message:message
+            level:level
+             flag:flag
+          context:context
+             file:file
+         function:function
+             line:line
+              tag:tag];
+
+        va_end(args);
+    }
+}
+
++ (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag
+     format:(NSString *)format
+       args:(va_list)args {
+    [self.sharedInstance log:asynchronous level:level flag:flag context:context file:file function:function line:line tag:tag format:format args:args];
+}
+
+- (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag
+     format:(NSString *)format
+       args:(va_list)args {
+    if (format) {
+        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
+        [self log:asynchronous
+          message:message
+            level:level
+             flag:flag
+          context:context
+             file:file
+         function:function
+             line:line
+              tag:tag];
+    }
+}
+
++ (void)log:(BOOL)asynchronous
+    message:(NSString *)message
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag {
+    [self.sharedInstance log:asynchronous message:message level:level flag:flag context:context file:file function:function line:line tag:tag];
+}
+
+- (void)log:(BOOL)asynchronous
+    message:(NSString *)message
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(const char *)function
+       line:(NSUInteger)line
+        tag:(id)tag {
+    DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message
+                                                               level:level
+                                                                flag:flag
+                                                             context:context
+                                                                file:[NSString stringWithFormat:@"%s", file]
+                                                            function:[NSString stringWithFormat:@"%s", function]
+                                                                line:line
+                                                                 tag:tag
+                                                             options:(DDLogMessageOptions)0
+                                                           timestamp:nil];
+
+    [self queueLogMessage:logMessage asynchronously:asynchronous];
+}
+
++ (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
+    [self.sharedInstance log:asynchronous message:logMessage];
+}
+
+- (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
+    [self queueLogMessage:logMessage asynchronously:asynchronous];
+}
+
++ (void)flushLog {
+    [self.sharedInstance flushLog];
+}
+
+- (void)flushLog {
+    NSAssert(!dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method shouldn't be run on the logging thread/queue that make flush fast enough");
+    
+    dispatch_sync(_loggingQueue, ^{ @autoreleasepool {
+        [self lt_flush];
+    } });
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Registered Dynamic Logging
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
++ (BOOL)isRegisteredClass:(Class)class {
+    SEL getterSel = @selector(ddLogLevel);
+    SEL setterSel = @selector(ddSetLogLevel:);
+
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+
+    // Issue #6 (GoogleCode) - Crashes on iOS 4.2.1 and iPhone 4
+    //
+    // Crash caused by class_getClassMethod(2).
+    //
+    //     "It's a bug with UIAccessibilitySafeCategory__NSObject so it didn't pop up until
+    //      users had VoiceOver enabled [...]. I was able to work around it by searching the
+    //      result of class_copyMethodList() instead of calling class_getClassMethod()"
+
+    BOOL result = NO;
+
+    unsigned int methodCount, i;
+    Method *methodList = class_copyMethodList(object_getClass(class), &methodCount);
+
+    if (methodList != NULL) {
+        BOOL getterFound = NO;
+        BOOL setterFound = NO;
+
+        for (i = 0; i < methodCount; ++i) {
+            SEL currentSel = method_getName(methodList[i]);
+
+            if (currentSel == getterSel) {
+                getterFound = YES;
+            } else if (currentSel == setterSel) {
+                setterFound = YES;
+            }
+
+            if (getterFound && setterFound) {
+                result = YES;
+                break;
+            }
+        }
+
+        free(methodList);
+    }
+
+    return result;
+
+#else /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */
+
+    // Issue #24 (GitHub) - Crashing in in ARC+Simulator
+    //
+    // The method +[DDLog isRegisteredClass] will crash a project when using it with ARC + Simulator.
+    // For running in the Simulator, it needs to execute the non-iOS code.
+
+    Method getter = class_getClassMethod(class, getterSel);
+    Method setter = class_getClassMethod(class, setterSel);
+
+    if ((getter != NULL) && (setter != NULL)) {
+        return YES;
+    }
+
+    return NO;
+
+#endif /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */
+}
+
++ (NSArray *)registeredClasses {
+
+    // We're going to get the list of all registered classes.
+    // The Objective-C runtime library automatically registers all the classes defined in your source code.
+    //
+    // To do this we use the following method (documented in the Objective-C Runtime Reference):
+    //
+    // int objc_getClassList(Class *buffer, int bufferLen)
+    //
+    // We can pass (NULL, 0) to obtain the total number of
+    // registered class definitions without actually retrieving any class definitions.
+    // This allows us to allocate the minimum amount of memory needed for the application.
+
+    NSUInteger numClasses = 0;
+    Class *classes = NULL;
+
+    while (numClasses == 0) {
+
+        numClasses = (NSUInteger)MAX(objc_getClassList(NULL, 0), 0);
+
+        // numClasses now tells us how many classes we have (but it might change)
+        // So we can allocate our buffer, and get pointers to all the class definitions.
+
+        NSUInteger bufferSize = numClasses;
+
+        classes = numClasses ? (Class *)calloc(bufferSize, sizeof(Class)) : NULL;
+        if (classes == NULL) {
+            return @[]; //no memory or classes?
+        }
+
+        numClasses = (NSUInteger)MAX(objc_getClassList(classes, (int)bufferSize),0);
+
+        if (numClasses > bufferSize || numClasses == 0) {
+            //apparently more classes added between calls (or a problem); try again
+            free(classes);
+            classes = NULL;
+            numClasses = 0;
+        }
+    }
+
+    // We can now loop through the classes, and test each one to see if it is a DDLogging class.
+
+    NSMutableArray *result = [NSMutableArray arrayWithCapacity:numClasses];
+
+    for (NSUInteger i = 0; i < numClasses; i++) {
+        Class class = classes[i];
+
+        if ([self isRegisteredClass:class]) {
+            [result addObject:class];
+        }
+    }
+
+    free(classes);
+
+    return result;
+}
+
++ (NSArray *)registeredClassNames {
+    NSArray *registeredClasses = [self registeredClasses];
+    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[registeredClasses count]];
+
+    for (Class class in registeredClasses) {
+        [result addObject:NSStringFromClass(class)];
+    }
+    return result;
+}
+
++ (DDLogLevel)levelForClass:(Class)aClass {
+    if ([self isRegisteredClass:aClass]) {
+        return [aClass ddLogLevel];
+    }
+    return (DDLogLevel)-1;
+}
+
++ (DDLogLevel)levelForClassWithName:(NSString *)aClassName {
+    Class aClass = NSClassFromString(aClassName);
+
+    return [self levelForClass:aClass];
+}
+
++ (void)setLevel:(DDLogLevel)level forClass:(Class)aClass {
+    if ([self isRegisteredClass:aClass]) {
+        [aClass ddSetLogLevel:level];
+    }
+}
+
++ (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName {
+    Class aClass = NSClassFromString(aClassName);
+    [self setLevel:level forClass:aClass];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Logging Thread
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)lt_addLogger:(id <DDLogger>)logger level:(DDLogLevel)level {
+    // Add to loggers array.
+    // Need to create loggerQueue if loggerNode doesn't provide one.
+
+    for (DDLoggerNode *node in self._loggers) {
+        if (node->_logger == logger
+            && node->_level == level) {
+            // Exactly same logger already added, exit
+            return;
+        }
+    }
+
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    dispatch_queue_t loggerQueue = NULL;
+    if ([logger respondsToSelector:@selector(loggerQueue)]) {
+        // Logger may be providing its own queue
+        loggerQueue = logger.loggerQueue;
+    }
+
+    if (loggerQueue == nil) {
+        // Automatically create queue for the logger.
+        // Use the logger name as the queue name if possible.
+        const char *loggerQueueName = NULL;
+
+        if ([logger respondsToSelector:@selector(loggerName)]) {
+            loggerQueueName = logger.loggerName.UTF8String;
+        }
+
+        loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
+    }
+
+    DDLoggerNode *loggerNode = [DDLoggerNode nodeWithLogger:logger loggerQueue:loggerQueue level:level];
+    [self._loggers addObject:loggerNode];
+
+    if ([logger respondsToSelector:@selector(didAddLoggerInQueue:)]) {
+        dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool {
+            [logger didAddLoggerInQueue:loggerNode->_loggerQueue];
+        } });
+    } else if ([logger respondsToSelector:@selector(didAddLogger)]) {
+        dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool {
+            [logger didAddLogger];
+        } });
+    }
+}
+
+- (void)lt_removeLogger:(id <DDLogger>)logger {
+    // Find associated loggerNode in list of added loggers
+
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    DDLoggerNode *loggerNode = nil;
+
+    for (DDLoggerNode *node in self._loggers) {
+        if (node->_logger == logger) {
+            loggerNode = node;
+            break;
+        }
+    }
+
+    if (loggerNode == nil) {
+        NSLogDebug(@"DDLog: Request to remove logger which wasn't added");
+        return;
+    }
+
+    // Notify logger
+    if ([logger respondsToSelector:@selector(willRemoveLogger)]) {
+        dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool {
+            [logger willRemoveLogger];
+        } });
+    }
+
+    // Remove from loggers array
+    [self._loggers removeObject:loggerNode];
+}
+
+- (void)lt_removeAllLoggers {
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    // Notify all loggers
+    for (DDLoggerNode *loggerNode in self._loggers) {
+        if ([loggerNode->_logger respondsToSelector:@selector(willRemoveLogger)]) {
+            dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool {
+                [loggerNode->_logger willRemoveLogger];
+            } });
+        }
+    }
+
+    // Remove all loggers from array
+
+    [self._loggers removeAllObjects];
+}
+
+- (NSArray *)lt_allLoggers {
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    NSMutableArray *theLoggers = [NSMutableArray new];
+
+    for (DDLoggerNode *loggerNode in self._loggers) {
+        [theLoggers addObject:loggerNode->_logger];
+    }
+
+    return [theLoggers copy];
+}
+
+- (NSArray *)lt_allLoggersWithLevel {
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    NSMutableArray *theLoggersWithLevel = [NSMutableArray new];
+
+    for (DDLoggerNode *loggerNode in self._loggers) {
+        [theLoggersWithLevel addObject:[DDLoggerInformation informationWithLogger:loggerNode->_logger
+                                                                         andLevel:loggerNode->_level]];
+    }
+
+    return [theLoggersWithLevel copy];
+}
+
+- (void)lt_log:(DDLogMessage *)logMessage {
+    // Execute the given log message on each of our loggers.
+
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    if (_numProcessors > 1) {
+        // Execute each logger concurrently, each within its own queue.
+        // All blocks are added to same group.
+        // After each block has been queued, wait on group.
+        //
+        // The waiting ensures that a slow logger doesn't end up with a large queue of pending log messages.
+        // This would defeat the purpose of the efforts we made earlier to restrict the max queue size.
+
+        for (DDLoggerNode *loggerNode in self._loggers) {
+            // skip the loggers that shouldn't write this message based on the log level
+
+            if (!(logMessage->_flag & loggerNode->_level)) {
+                continue;
+            }
+
+            dispatch_group_async(_loggingGroup, loggerNode->_loggerQueue, ^{ @autoreleasepool {
+                [loggerNode->_logger logMessage:logMessage];
+            } });
+        }
+
+        dispatch_group_wait(_loggingGroup, DISPATCH_TIME_FOREVER);
+    } else {
+        // Execute each logger serially, each within its own queue.
+
+        for (DDLoggerNode *loggerNode in self._loggers) {
+            // skip the loggers that shouldn't write this message based on the log level
+
+            if (!(logMessage->_flag & loggerNode->_level)) {
+                continue;
+            }
+
+#if DD_DEBUG
+            // we must assure that we aren not on loggerNode->_loggerQueue.
+            if (loggerNode->_loggerQueue == NULL) {
+              // tell that we can't dispatch logger node on queue that is NULL.
+              NSLogDebug(@"DDLog: current node has loggerQueue == NULL");
+            }
+            else {
+              dispatch_async(loggerNode->_loggerQueue, ^{
+                if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) {
+                  // tell that we somehow on logging queue?
+                  NSLogDebug(@"DDLog: current node has loggerQueue == globalLoggingQueue");
+                }
+              });
+            }
+#endif
+            // next, we must check that node is OK.
+            dispatch_sync(loggerNode->_loggerQueue, ^{ @autoreleasepool {
+                [loggerNode->_logger logMessage:logMessage];
+            } });
+        }
+    }
+}
+
+- (void)lt_flush {
+    // All log statements issued before the flush method was invoked have now been executed.
+    //
+    // Now we need to propagate the flush request to any loggers that implement the flush method.
+    // This is designed for loggers that buffer IO.
+
+    NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
+             @"This method should only be run on the logging thread/queue");
+
+    for (DDLoggerNode *loggerNode in self._loggers) {
+        if ([loggerNode->_logger respondsToSelector:@selector(flush)]) {
+            dispatch_group_async(_loggingGroup, loggerNode->_loggerQueue, ^{ @autoreleasepool {
+                [loggerNode->_logger flush];
+            } });
+        }
+    }
+
+    dispatch_group_wait(_loggingGroup, DISPATCH_TIME_FOREVER);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Utilities
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy) {
+    if (filePath == NULL) {
+        return nil;
+    }
+
+    char *lastSlash = NULL;
+    char *lastDot = NULL;
+
+    char *p = (char *)filePath;
+
+    while (*p != '\0') {
+        if (*p == '/') {
+            lastSlash = p;
+        } else if (*p == '.') {
+            lastDot = p;
+        }
+
+        p++;
+    }
+
+    char *subStr;
+    NSUInteger subLen;
+
+    if (lastSlash) {
+        if (lastDot) {
+            // lastSlash -> lastDot
+            subStr = lastSlash + 1;
+            subLen = (NSUInteger)(lastDot - subStr);
+        } else {
+            // lastSlash -> endOfString
+            subStr = lastSlash + 1;
+            subLen = (NSUInteger)(p - subStr);
+        }
+    } else {
+        if (lastDot) {
+            // startOfString -> lastDot
+            subStr = (char *)filePath;
+            subLen = (NSUInteger)(lastDot - subStr);
+        } else {
+            // startOfString -> endOfString
+            subStr = (char *)filePath;
+            subLen = (NSUInteger)(p - subStr);
+        }
+    }
+
+    if (copy) {
+        return [[NSString alloc] initWithBytes:subStr
+                                        length:subLen
+                                      encoding:NSUTF8StringEncoding];
+    } else {
+        // We can take advantage of the fact that __FILE__ is a string literal.
+        // Specifically, we don't need to waste time copying the string.
+        // We can just tell NSString to point to a range within the string literal.
+
+        return [[NSString alloc] initWithBytesNoCopy:subStr
+                                              length:subLen
+                                            encoding:NSUTF8StringEncoding
+                                        freeWhenDone:NO];
+    }
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation DDLoggerNode
+
+- (instancetype)initWithLogger:(id <DDLogger>)logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level {
+    if ((self = [super init])) {
+        _logger = logger;
+
+        if (loggerQueue) {
+            _loggerQueue = loggerQueue;
+            #if !OS_OBJECT_USE_OBJC
+            dispatch_retain(loggerQueue);
+            #endif
+        }
+
+        _level = level;
+    }
+    return self;
+}
+
++ (instancetype)nodeWithLogger:(id <DDLogger>)logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level {
+    return [[self alloc] initWithLogger:logger loggerQueue:loggerQueue level:level];
+}
+
+- (void)dealloc {
+    #if !OS_OBJECT_USE_OBJC
+    if (_loggerQueue) {
+        dispatch_release(_loggerQueue);
+    }
+    #endif
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation DDLogMessage
+
+- (instancetype)init {
+    self = [super init];
+    return self;
+}
+
+- (instancetype)initWithMessage:(NSString *)message
+                          level:(DDLogLevel)level
+                           flag:(DDLogFlag)flag
+                        context:(NSInteger)context
+                           file:(NSString *)file
+                       function:(NSString *)function
+                           line:(NSUInteger)line
+                            tag:(id)tag
+                        options:(DDLogMessageOptions)options
+                      timestamp:(NSDate *)timestamp {
+    if ((self = [super init])) {
+        BOOL copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
+        _message      = copyMessage ? [message copy] : message;
+        _level        = level;
+        _flag         = flag;
+        _context      = context;
+
+        BOOL copyFile = (options & DDLogMessageCopyFile) != 0;
+        _file = copyFile ? [file copy] : file;
+
+        BOOL copyFunction = (options & DDLogMessageCopyFunction) != 0;
+        _function = copyFunction ? [function copy] : function;
+
+        _line         = line;
+        _representedObject = tag;
+#if DD_LEGACY_MESSAGE_TAG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        _tag = tag;
+#pragma clang diagnostic pop
+#endif
+        _options      = options;
+        _timestamp    = timestamp ?: [NSDate new];
+
+        __uint64_t tid;
+        if (pthread_threadid_np(NULL, &tid) == 0) {
+            _threadID = [[NSString alloc] initWithFormat:@"%llu", tid];
+        } else {
+            _threadID = @"missing threadId";
+        }
+        _threadName   = NSThread.currentThread.name;
+
+        // Get the file name without extension
+        _fileName = [_file lastPathComponent];
+        NSUInteger dotLocation = [_fileName rangeOfString:@"." options:NSBackwardsSearch].location;
+        if (dotLocation != NSNotFound)
+        {
+            _fileName = [_fileName substringToIndex:dotLocation];
+        }
+
+        // Try to get the current queue's label
+        _queueLabel = [[NSString alloc] initWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
+        _qos = (NSUInteger) qos_class_self();
+    }
+    return self;
+}
+
+- (BOOL)isEqual:(id)other {
+    if (other == self) {
+        return YES;
+    } else if (![super isEqual:other] || ![other isKindOfClass:[self class]]) {
+        return NO;
+    } else {
+        __auto_type otherMsg = (DDLogMessage *)other;
+        return [otherMsg->_message isEqualToString:_message]
+        && otherMsg->_level == _level
+        && otherMsg->_flag == _flag
+        && otherMsg->_context == _context
+        && [otherMsg->_file isEqualToString:_file]
+        && [otherMsg->_fileName isEqualToString:_fileName]
+        && [otherMsg->_function isEqualToString:_function]
+        && otherMsg->_line == _line
+        && (([otherMsg->_representedObject respondsToSelector:@selector(isEqual:)] && [otherMsg->_representedObject isEqual:_representedObject]) || otherMsg->_representedObject == _representedObject)
+        && otherMsg->_options == _options
+        && [otherMsg->_timestamp isEqualToDate:_timestamp]
+        && [otherMsg->_threadID isEqualToString:_threadID] // If the thread ID is the same, the name will likely be the same as well.
+        && [otherMsg->_queueLabel isEqualToString:_queueLabel]
+        && otherMsg->_qos == _qos;
+    }
+}
+
+- (NSUInteger)hash {
+    return [super hash]
+    ^ _message.hash
+    ^ _level
+    ^ _flag
+    ^ _context
+    ^ _file.hash
+    ^ _fileName.hash
+    ^ _function.hash
+    ^ _line
+    ^ ([_representedObject respondsToSelector:@selector(hash)] ? [_representedObject hash] : 0)
+    ^ _options
+    ^ _timestamp.hash
+    ^ _threadID.hash
+    ^ _queueLabel.hash
+    ^ _qos;
+}
+
+- (id)copyWithZone:(NSZone * __attribute__((unused)))zone {
+    DDLogMessage *newMessage = [DDLogMessage new];
+
+    newMessage->_message = _message;
+    newMessage->_level = _level;
+    newMessage->_flag = _flag;
+    newMessage->_context = _context;
+    newMessage->_file = _file;
+    newMessage->_fileName = _fileName;
+    newMessage->_function = _function;
+    newMessage->_line = _line;
+    newMessage->_representedObject = _representedObject;
+#if DD_LEGACY_MESSAGE_TAG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    newMessage->_tag = _tag;
+#pragma clang diagnostic pop
+#endif
+    newMessage->_options = _options;
+    newMessage->_timestamp = _timestamp;
+    newMessage->_threadID = _threadID;
+    newMessage->_threadName = _threadName;
+    newMessage->_queueLabel = _queueLabel;
+    newMessage->_qos = _qos;
+
+    return newMessage;
+}
+
+// ensure compatibility even when built with DD_LEGACY_MESSAGE_TAG to 0.
+- (id)tag {
+    return _representedObject;
+}
+
+@end
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation DDAbstractLogger
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        const char *loggerQueueName = NULL;
+
+        if ([self respondsToSelector:@selector(loggerName)]) {
+            loggerQueueName = self.loggerName.UTF8String;
+        }
+
+        _loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
+
+        // We're going to use dispatch_queue_set_specific() to "mark" our loggerQueue.
+        // Later we can use dispatch_get_specific() to determine if we're executing on our loggerQueue.
+        // The documentation states:
+        //
+        // > Keys are only compared as pointers and are never dereferenced.
+        // > Thus, you can use a pointer to a static variable for a specific subsystem or
+        // > any other value that allows you to identify the value uniquely.
+        // > Specifying a pointer to a string constant is not recommended.
+        //
+        // So we're going to use the very convenient key of "self",
+        // which also works when multiple logger classes extend this class, as each will have a different "self" key.
+        //
+        // This is used primarily for thread-safety assertions (via the isOnInternalLoggerQueue method below).
+
+        void *key = (__bridge void *)self;
+        void *nonNullValue = (__bridge void *)self;
+
+        dispatch_queue_set_specific(_loggerQueue, key, nonNullValue, NULL);
+    }
+
+    return self;
+}
+
+- (void)dealloc {
+    #if !OS_OBJECT_USE_OBJC
+
+    if (_loggerQueue) {
+        dispatch_release(_loggerQueue);
+    }
+
+    #endif
+}
+
+- (void)logMessage:(DDLogMessage * __attribute__((unused)))logMessage {
+    // Override me
+}
+
+- (id <DDLogFormatter>)logFormatter {
+    // This method must be thread safe and intuitive.
+    // Therefore if somebody executes the following code:
+    //
+    // [logger setLogFormatter:myFormatter];
+    // formatter = [logger logFormatter];
+    //
+    // They would expect formatter to equal myFormatter.
+    // This functionality must be ensured by the getter and setter method.
+    //
+    // The thread safety must not come at a cost to the performance of the logMessage method.
+    // This method is likely called sporadically, while the logMessage method is called repeatedly.
+    // This means, the implementation of this method:
+    // - Must NOT require the logMessage method to acquire a lock.
+    // - Must NOT require the logMessage method to access an atomic property (also a lock of sorts).
+    //
+    // Thread safety is ensured by executing access to the formatter variable on the loggerQueue.
+    // This is the same queue that the logMessage method operates on.
+    //
+    // Note: The last time I benchmarked the performance of direct access vs atomic property access,
+    // direct access was over twice as fast on the desktop and over 6 times as fast on the iPhone.
+    //
+    // Furthermore, consider the following code:
+    //
+    // DDLogVerbose(@"log msg 1");
+    // DDLogVerbose(@"log msg 2");
+    // [logger setFormatter:myFormatter];
+    // DDLogVerbose(@"log msg 3");
+    //
+    // Our intuitive requirement means that the new formatter will only apply to the 3rd log message.
+    // This must remain true even when using asynchronous logging.
+    // We must keep in mind the various queue's that are in play here:
+    //
+    // loggerQueue : Our own private internal queue that the logMessage method runs on.
+    //               Operations are added to this queue from the global loggingQueue.
+    //
+    // globalLoggingQueue : The queue that all log messages go through before they arrive in our loggerQueue.
+    //
+    // All log statements go through the serial globalLoggingQueue before they arrive at our loggerQueue.
+    // Thus this method also goes through the serial globalLoggingQueue to ensure intuitive operation.
+
+    // IMPORTANT NOTE:
+    //
+    // Methods within the DDLogger implementation MUST access the formatter ivar directly.
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block id <DDLogFormatter> result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self->_loggerQueue, ^{
+            result = self->_logFormatter;
+        });
+    });
+
+    return result;
+}
+
+- (void)setLogFormatter:(id <DDLogFormatter>)logFormatter {
+    // The design of this method is documented extensively in the logFormatter message (above in code).
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            if (self->_logFormatter != logFormatter) {
+                if ([self->_logFormatter respondsToSelector:@selector(willRemoveFromLogger:)]) {
+                    [self->_logFormatter willRemoveFromLogger:self];
+                }
+
+                self->_logFormatter = logFormatter;
+
+                if ([self->_logFormatter respondsToSelector:@selector(didAddToLogger:inQueue:)]) {
+                    [self->_logFormatter didAddToLogger:self inQueue:self->_loggerQueue];
+                } else if ([self->_logFormatter respondsToSelector:@selector(didAddToLogger:)]) {
+                    [self->_logFormatter didAddToLogger:self];
+                }
+            }
+        }
+    };
+
+    dispatch_async(DDLog.loggingQueue, ^{
+        dispatch_async(self->_loggerQueue, block);
+    });
+}
+
+- (dispatch_queue_t)loggerQueue {
+    return _loggerQueue;
+}
+
+- (NSString *)loggerName {
+    return NSStringFromClass([self class]);
+}
+
+- (BOOL)isOnGlobalLoggingQueue {
+    return (dispatch_get_specific(GlobalLoggingQueueIdentityKey) != NULL);
+}
+
+- (BOOL)isOnInternalLoggerQueue {
+    void *key = (__bridge void *)self;
+
+    return (dispatch_get_specific(key) != NULL);
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLoggerInformation()
+{
+    // Direct accessors to be used only for performance
+    @public
+    id <DDLogger> _logger;
+    DDLogLevel _level;
+}
+
+@end
+
+@implementation DDLoggerInformation
+
+- (instancetype)initWithLogger:(id <DDLogger>)logger andLevel:(DDLogLevel)level {
+    if ((self = [super init])) {
+        _logger = logger;
+        _level = level;
+    }
+    return self;
+}
+
++ (instancetype)informationWithLogger:(id <DDLogger>)logger andLevel:(DDLogLevel)level {
+    return [[self alloc] initWithLogger:logger andLevel:level];
+}
+
+@end

+ 21 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m

@@ -0,0 +1,21 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDLoggerNames.h>
+
+DDLoggerName const DDLoggerNameASL    = @"cocoa.lumberjack.aslLogger";
+DDLoggerName const DDLoggerNameTTY    = @"cocoa.lumberjack.ttyLogger";
+DDLoggerName const DDLoggerNameOS     = @"cocoa.lumberjack.osLogger";
+DDLoggerName const DDLoggerNameFile   = @"cocoa.lumberjack.fileLogger";

+ 118 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m

@@ -0,0 +1,118 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <os/log.h>
+
+#import <CocoaLumberjack/DDOSLogger.h>
+
+@interface DDOSLogger () {
+    NSString *_subsystem;
+    NSString *_category;
+}
+
+@property (copy, nonatomic, readonly, nullable) NSString *subsystem;
+@property (copy, nonatomic, readonly, nullable) NSString *category;
+@property (strong, nonatomic, readwrite, nonnull) os_log_t logger;
+
+@end
+
+@implementation DDOSLogger
+
+@synthesize subsystem = _subsystem;
+@synthesize category = _category;
+
+#pragma mark - Initialization
+
+/**
+ * Assertion
+ * Swift: (String, String)?
+ */
+- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category {
+    NSAssert((subsystem == nil) == (category == nil), @"Either both subsystem and category or neither should be nil.");
+    if (self = [super init]) {
+        _subsystem = [subsystem copy];
+        _category = [category copy];
+    }
+    return self;
+}
+
+static DDOSLogger *sharedInstance;
+
+- (instancetype)init {
+    return [self initWithSubsystem:nil category:nil];
+}
+
++ (instancetype)sharedInstance {
+    static dispatch_once_t DDOSLoggerOnceToken;
+
+    dispatch_once(&DDOSLoggerOnceToken, ^{
+        sharedInstance = [[[self class] alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+#pragma mark - os_log
+
+- (os_log_t)getLogger {
+    if (self.subsystem == nil || self.category == nil) {
+        return OS_LOG_DEFAULT;
+    }
+    return os_log_create(self.subsystem.UTF8String, self.category.UTF8String);
+}
+
+- (os_log_t)logger {
+    if (_logger == nil)  {
+        _logger = [self getLogger];
+    }
+    return _logger;
+}
+
+#pragma mark - DDLogger
+
+- (DDLoggerName)loggerName {
+    return DDLoggerNameOS;
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Skip captured log messages
+    if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
+        return;
+    }
+
+    if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) {
+        NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
+        if (message != nil) {
+            const char *msg = [message UTF8String];
+            __auto_type logger = [self logger];
+            switch (logMessage->_flag) {
+                case DDLogFlagError  :
+                    os_log_error(logger, "%{public}s", msg);
+                    break;
+                case DDLogFlagWarning:
+                case DDLogFlagInfo   :
+                    os_log_info(logger, "%{public}s", msg);
+                    break;
+                case DDLogFlagDebug  :
+                case DDLogFlagVerbose:
+                default              :
+                    os_log_debug(logger, "%{public}s", msg);
+                    break;
+            }
+        }
+    }
+}
+
+@end

+ 1492 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m

@@ -0,0 +1,1492 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <sys/uio.h>
+
+#import <CocoaLumberjack/DDTTYLogger.h>
+
+// We probably shouldn't be using DDLog() statements within the DDLog implementation.
+// But we still want to leave our log statements for any future debugging,
+// and to allow other developers to trace the implementation (which is a great learning tool).
+//
+// So we use primitive logging macros around NSLog.
+// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
+
+#ifndef DD_NSLOG_LEVEL
+    #define DD_NSLOG_LEVEL 2
+#endif
+
+#define NSLogError(frmt, ...)    do{ if(DD_NSLOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogWarn(frmt, ...)     do{ if(DD_NSLOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogInfo(frmt, ...)     do{ if(DD_NSLOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogDebug(frmt, ...)    do{ if(DD_NSLOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0)
+#define NSLogVerbose(frmt, ...)  do{ if(DD_NSLOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0)
+
+// Xcode does NOT natively support colors in the Xcode debugging console.
+// You'll need to install the XcodeColors plugin to see colors in the Xcode console.
+// https://github.com/robbiehanson/XcodeColors
+//
+// The following is documentation from the XcodeColors project:
+//
+//
+// How to apply color formatting to your log statements:
+//
+// To set the foreground color:
+// Insert the ESCAPE_SEQ into your string, followed by "fg124,12,255;" where r=124, g=12, b=255.
+//
+// To set the background color:
+// Insert the ESCAPE_SEQ into your string, followed by "bg12,24,36;" where r=12, g=24, b=36.
+//
+// To reset the foreground color (to default value):
+// Insert the ESCAPE_SEQ into your string, followed by "fg;"
+//
+// To reset the background color (to default value):
+// Insert the ESCAPE_SEQ into your string, followed by "bg;"
+//
+// To reset the foreground and background color (to default values) in one operation:
+// Insert the ESCAPE_SEQ into your string, followed by ";"
+
+#define XCODE_COLORS_ESCAPE_SEQ "\033["
+
+#define XCODE_COLORS_RESET_FG   XCODE_COLORS_ESCAPE_SEQ "fg;" // Clear any foreground color
+#define XCODE_COLORS_RESET_BG   XCODE_COLORS_ESCAPE_SEQ "bg;" // Clear any background color
+#define XCODE_COLORS_RESET      XCODE_COLORS_ESCAPE_SEQ ";"  // Clear any foreground or background color
+
+// If running in a shell, not all RGB colors will be supported.
+// In this case we automatically map to the closest available color.
+// In order to provide this mapping, we have a hard-coded set of the standard RGB values available in the shell.
+// However, not every shell is the same, and Apple likes to think different even when it comes to shell colors.
+//
+// Map to standard Terminal.app colors (1), or
+// map to standard xterm colors (0).
+
+#define MAP_TO_TERMINAL_APP_COLORS 1
+
+typedef struct {
+  uint8_t r;
+  uint8_t g;
+  uint8_t b;
+} DDRGBColor;
+
+@interface DDTTYLoggerColorProfile : NSObject {
+    @public
+    DDLogFlag mask;
+    NSInteger context;
+
+    uint8_t fg_r;
+    uint8_t fg_g;
+    uint8_t fg_b;
+
+    uint8_t bg_r;
+    uint8_t bg_g;
+    uint8_t bg_b;
+
+    NSUInteger fgCodeIndex;
+    NSString *fgCodeRaw;
+
+    NSUInteger bgCodeIndex;
+    NSString *bgCodeRaw;
+
+    char fgCode[24];
+    size_t fgCodeLen;
+
+    char bgCode[24];
+    size_t bgCodeLen;
+
+    char resetCode[8];
+    size_t resetCodeLen;
+}
+
+- (nullable instancetype)initWithForegroundColor:(nullable DDColor *)fgColor backgroundColor:(nullable DDColor *)bgColor flag:(DDLogFlag)mask context:(NSInteger)ctxt;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDTTYLogger () {
+    NSString *_appName;
+    char *_app;
+    size_t _appLen;
+    
+    NSString *_processID;
+    char *_pid;
+    size_t _pidLen;
+    
+    BOOL _colorsEnabled;
+    NSMutableArray *_colorProfilesArray;
+    NSMutableDictionary *_colorProfilesDict;
+}
+
+@end
+
+
+@implementation DDTTYLogger
+
+static BOOL isaColorTTY;
+static BOOL isaColor256TTY;
+static BOOL isaXcodeColorTTY;
+
+static NSArray *codes_fg = nil;
+static NSArray *codes_bg = nil;
+static NSArray *colors   = nil;
+
+static DDTTYLogger *sharedInstance;
+
+/**
+ * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode.
+ *
+ * This method is used when the application is running from within a shell that only supports 16 color mode.
+ * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
+ **/
++ (void)initialize_colors_16 {
+    if (codes_fg || codes_bg || colors) {
+        return;
+    }
+
+    NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:16];
+
+    // In a standard shell only 16 colors are supported.
+    //
+    // More information about ansi escape codes can be found online.
+    // http://en.wikipedia.org/wiki/ANSI_escape_code
+    codes_fg = @[
+        @"30m",  // normal - black
+        @"31m",  // normal - red
+        @"32m",  // normal - green
+        @"33m",  // normal - yellow
+        @"34m",  // normal - blue
+        @"35m",  // normal - magenta
+        @"36m",  // normal - cyan
+        @"37m",  // normal - gray
+        @"1;30m",  // bright - darkgray
+        @"1;31m",  // bright - red
+        @"1;32m",  // bright - green
+        @"1;33m",  // bright - yellow
+        @"1;34m",  // bright - blue
+        @"1;35m",  // bright - magenta
+        @"1;36m",  // bright - cyan
+        @"1;37m",  // bright - white
+    ];
+
+    codes_bg = @[
+        @"40m",  // normal - black
+        @"41m",  // normal - red
+        @"42m",  // normal - green
+        @"43m",  // normal - yellow
+        @"44m",  // normal - blue
+        @"45m",  // normal - magenta
+        @"46m",  // normal - cyan
+        @"47m",  // normal - gray
+        @"1;40m",  // bright - darkgray
+        @"1;41m",  // bright - red
+        @"1;42m",  // bright - green
+        @"1;43m",  // bright - yellow
+        @"1;44m",  // bright - blue
+        @"1;45m",  // bright - magenta
+        @"1;46m",  // bright - cyan
+        @"1;47m",  // bright - white
+    ];
+
+
+#if MAP_TO_TERMINAL_APP_COLORS
+
+    // Standard Terminal.app colors:
+    //
+    // These are the default colors used by Apple's Terminal.app.
+    DDRGBColor rgbColors[] = {
+        {  0,   0,   0}, // normal - black
+        {194,  54,  33}, // normal - red
+        { 37, 188,  36}, // normal - green
+        {173, 173,  39}, // normal - yellow
+        { 73,  46, 225}, // normal - blue
+        {211,  56, 211}, // normal - magenta
+        { 51, 187, 200}, // normal - cyan
+        {203, 204, 205}, // normal - gray
+        {129, 131, 131}, // bright - darkgray
+        {252,  57,  31}, // bright - red
+        { 49, 231,  34}, // bright - green
+        {234, 236,  35}, // bright - yellow
+        { 88,  51, 255}, // bright - blue
+        {249,  53, 248}, // bright - magenta
+        { 20, 240, 240}, // bright - cyan
+        {233, 235, 235}, // bright - white
+    };
+
+#else /* if MAP_TO_TERMINAL_APP_COLORS */
+
+    // Standard xterm colors:
+    //
+    // These are the default colors used by most xterm shells.
+
+    DDRGBColor rgbColors[] = {
+        {  0,   0,   0}, // normal - black
+        {205,   0,   0}, // normal - red
+        {  0, 205,   0}, // normal - green
+        {205, 205,   0}, // normal - yellow
+        {  0,   0, 238}, // normal - blue
+        {205,   0, 205}, // normal - magenta
+        {  0, 205, 205}, // normal - cyan
+        {229, 229, 229}, // normal - gray
+        {127, 127, 127}, // bright - darkgray
+        {255,   0,   0}, // bright - red
+        {  0, 255,   0}, // bright - green
+        {255, 255,   0}, // bright - yellow
+        { 92,  92, 255}, // bright - blue
+        {255,   0, 255}, // bright - magenta
+        {  0, 255, 255}, // bright - cyan
+        {255, 255, 255}, // bright - white
+    };
+#endif /* if MAP_TO_TERMINAL_APP_COLORS */
+
+    for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
+        [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
+    }
+    colors   = [m_colors   copy];
+
+    NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
+    NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
+}
+
+/**
+ * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode.
+ *
+ * This method is used when the application is running from within a shell that supports 256 color mode.
+ * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
+ **/
++ (void)initialize_colors_256 {
+    if (codes_fg || codes_bg || colors) {
+        return;
+    }
+
+    NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256 - 16)];
+    NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256 - 16)];
+    NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:(256 - 16)];
+
+    #if MAP_TO_TERMINAL_APP_COLORS
+
+    // Standard Terminal.app colors:
+    //
+    // These are the colors the Terminal.app uses in xterm-256color mode.
+    // In this mode, the terminal supports 256 different colors, specified by 256 color codes.
+    //
+    // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
+    // These are actually configurable, and thus we ignore them for the purposes of mapping,
+    // as we can't rely on them being constant. They are largely duplicated anyway.
+    //
+    // The next 216 color codes are designed to run the spectrum, with several shades of every color.
+    // While the color codes are standardized, the actual RGB values for each color code is not.
+    // Apple's Terminal.app uses different RGB values from that of a standard xterm.
+    // Apple's choices in colors are designed to be a little nicer on the eyes.
+    //
+    // The last 24 color codes represent a grayscale.
+    //
+    // Unfortunately, unlike the standard xterm color chart,
+    // Apple's RGB values cannot be calculated using a simple formula (at least not that I know of).
+    // Also, I don't know of any ways to programmatically query the shell for the RGB values.
+    // So this big giant color chart had to be made by hand.
+    //
+    // More information about ansi escape codes can be found online.
+    // http://en.wikipedia.org/wiki/ANSI_escape_code
+
+    // Colors
+    DDRGBColor rgbColors[] = {
+        { 47,  49,  49},
+        { 60,  42, 144},
+        { 66,  44, 183},
+        { 73,  46, 222},
+        { 81,  50, 253},
+        { 88,  51, 255},
+
+        { 42, 128,  37},
+        { 42, 127, 128},
+        { 44, 126, 169},
+        { 56, 125, 209},
+        { 59, 124, 245},
+        { 66, 123, 255},
+
+        { 51, 163,  41},
+        { 39, 162, 121},
+        { 42, 161, 162},
+        { 53, 160, 202},
+        { 45, 159, 240},
+        { 58, 158, 255},
+
+        { 31, 196,  37},
+        { 48, 196, 115},
+        { 39, 195, 155},
+        { 49, 195, 195},
+        { 32, 194, 235},
+        { 53, 193, 255},
+
+        { 50, 229,  35},
+        { 40, 229, 109},
+        { 27, 229, 149},
+        { 49, 228, 189},
+        { 33, 228, 228},
+        { 53, 227, 255},
+
+        { 27, 254,  30},
+        { 30, 254, 103},
+        { 45, 254, 143},
+        { 38, 253, 182},
+        { 38, 253, 222},
+        { 42, 253, 252},
+
+        {140,  48,  40},
+        {136,  51, 136},
+        {135,  52, 177},
+        {134,  52, 217},
+        {135,  56, 248},
+        {134,  53, 255},
+
+        {125, 125,  38},
+        {124, 125, 125},
+        {122, 124, 166},
+        {123, 124, 207},
+        {123, 122, 247},
+        {124, 121, 255},
+
+        {119, 160,  35},
+        {117, 160, 120},
+        {117, 160, 160},
+        {115, 159, 201},
+        {116, 158, 240},
+        {117, 157, 255},
+
+        {113, 195,  39},
+        {110, 194, 114},
+        {111, 194, 154},
+        {108, 194, 194},
+        {109, 193, 234},
+        {108, 192, 255},
+
+        {105, 228,  30},
+        {103, 228, 109},
+        {105, 228, 148},
+        {100, 227, 188},
+        { 99, 227, 227},
+        { 99, 226, 253},
+
+        { 92, 253,  34},
+        { 96, 253, 103},
+        { 97, 253, 142},
+        { 88, 253, 182},
+        { 93, 253, 221},
+        { 88, 254, 251},
+
+        {177,  53,  34},
+        {174,  54, 131},
+        {172,  55, 172},
+        {171,  57, 213},
+        {170,  55, 249},
+        {170,  57, 255},
+
+        {165, 123,  37},
+        {163, 123, 123},
+        {162, 123, 164},
+        {161, 122, 205},
+        {161, 121, 241},
+        {161, 121, 255},
+
+        {158, 159,  33},
+        {157, 158, 118},
+        {157, 158, 159},
+        {155, 157, 199},
+        {155, 157, 239},
+        {154, 156, 255},
+
+        {152, 193,  40},
+        {151, 193, 113},
+        {150, 193, 153},
+        {150, 192, 193},
+        {148, 192, 232},
+        {149, 191, 253},
+
+        {146, 227,  28},
+        {144, 227, 108},
+        {144, 227, 147},
+        {144, 227, 187},
+        {142, 226, 227},
+        {142, 225, 252},
+
+        {138, 253,  36},
+        {137, 253, 102},
+        {136, 253, 141},
+        {138, 254, 181},
+        {135, 255, 220},
+        {133, 255, 250},
+
+        {214,  57,  30},
+        {211,  59, 126},
+        {209,  57, 168},
+        {208,  55, 208},
+        {207,  58, 247},
+        {206,  61, 255},
+
+        {204, 121,  32},
+        {202, 121, 121},
+        {201, 121, 161},
+        {200, 120, 202},
+        {200, 120, 241},
+        {198, 119, 255},
+
+        {198, 157,  37},
+        {196, 157, 116},
+        {195, 156, 157},
+        {195, 156, 197},
+        {194, 155, 236},
+        {193, 155, 255},
+
+        {191, 192,  36},
+        {190, 191, 112},
+        {189, 191, 152},
+        {189, 191, 191},
+        {188, 190, 230},
+        {187, 190, 253},
+
+        {185, 226,  28},
+        {184, 226, 106},
+        {183, 225, 146},
+        {183, 225, 186},
+        {182, 225, 225},
+        {181, 224, 252},
+
+        {178, 255,  35},
+        {178, 255, 101},
+        {177, 254, 141},
+        {176, 254, 180},
+        {176, 254, 220},
+        {175, 253, 249},
+
+        {247,  56,  30},
+        {245,  57, 122},
+        {243,  59, 163},
+        {244,  60, 204},
+        {242,  59, 241},
+        {240,  55, 255},
+
+        {241, 119,  36},
+        {240, 120, 118},
+        {238, 119, 158},
+        {237, 119, 199},
+        {237, 118, 238},
+        {236, 118, 255},
+
+        {235, 154,  36},
+        {235, 154, 114},
+        {234, 154, 154},
+        {232, 154, 194},
+        {232, 153, 234},
+        {232, 153, 255},
+
+        {230, 190,  30},
+        {229, 189, 110},
+        {228, 189, 150},
+        {227, 189, 190},
+        {227, 189, 229},
+        {226, 188, 255},
+
+        {224, 224,  35},
+        {223, 224, 105},
+        {222, 224, 144},
+        {222, 223, 184},
+        {222, 223, 224},
+        {220, 223, 253},
+
+        {217, 253,  28},
+        {217, 253,  99},
+        {216, 252, 139},
+        {216, 252, 179},
+        {215, 252, 218},
+        {215, 251, 250},
+
+        {255,  61,  30},
+        {255,  60, 118},
+        {255,  58, 159},
+        {255,  56, 199},
+        {255,  55, 238},
+        {255,  59, 255},
+
+        {255, 117,  29},
+        {255, 117, 115},
+        {255, 117, 155},
+        {255, 117, 195},
+        {255, 116, 235},
+        {254, 116, 255},
+
+        {255, 152,  27},
+        {255, 152, 111},
+        {254, 152, 152},
+        {255, 152, 192},
+        {254, 151, 231},
+        {253, 151, 253},
+
+        {255, 187,  33},
+        {253, 187, 107},
+        {252, 187, 148},
+        {253, 187, 187},
+        {254, 187, 227},
+        {252, 186, 252},
+
+        {252, 222,  34},
+        {251, 222, 103},
+        {251, 222, 143},
+        {250, 222, 182},
+        {251, 221, 222},
+        {252, 221, 252},
+
+        {251, 252,  15},
+        {251, 252,  97},
+        {249, 252, 137},
+        {247, 252, 177},
+        {247, 253, 217},
+        {254, 255, 255},
+
+        // Grayscale
+
+        { 52,  53,  53},
+        { 57,  58,  59},
+        { 66,  67,  67},
+        { 75,  76,  76},
+        { 83,  85,  85},
+        { 92,  93,  94},
+
+        {101, 102, 102},
+        {109, 111, 111},
+        {118, 119, 119},
+        {126, 127, 128},
+        {134, 136, 136},
+        {143, 144, 145},
+
+        {151, 152, 153},
+        {159, 161, 161},
+        {167, 169, 169},
+        {176, 177, 177},
+        {184, 185, 186},
+        {192, 193, 194},
+
+        {200, 201, 202},
+        {208, 209, 210},
+        {216, 218, 218},
+        {224, 226, 226},
+        {232, 234, 234},
+        {240, 242, 242},
+    };
+
+    for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
+        [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
+    }
+
+    // Color codes
+
+    int index = 16;
+
+    while (index < 256) {
+        [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+        [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+
+        index++;
+    }
+
+    #else /* if MAP_TO_TERMINAL_APP_COLORS */
+
+    // Standard xterm colors:
+    //
+    // These are the colors xterm shells use in xterm-256color mode.
+    // In this mode, the shell supports 256 different colors, specified by 256 color codes.
+    //
+    // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
+    // These are generally configurable, and thus we ignore them for the purposes of mapping,
+    // as we can't rely on them being constant. They are largely duplicated anyway.
+    //
+    // The next 216 color codes are designed to run the spectrum, with several shades of every color.
+    // The last 24 color codes represent a grayscale.
+    //
+    // While the color codes are standardized, the actual RGB values for each color code is not.
+    // However most standard xterms follow a well known color chart,
+    // which can easily be calculated using the simple formula below.
+    //
+    // More information about ansi escape codes can be found online.
+    // http://en.wikipedia.org/wiki/ANSI_escape_code
+
+    int index = 16;
+
+    int r; // red
+    int g; // green
+    int b; // blue
+
+    int ri; // r increment
+    int gi; // g increment
+    int bi; // b increment
+
+    // Calculate xterm colors (using standard algorithm)
+
+    int r = 0;
+    int g = 0;
+    int b = 0;
+
+    for (ri = 0; ri < 6; ri++) {
+        r = (ri == 0) ? 0 : 95 + (40 * (ri - 1));
+
+        for (gi = 0; gi < 6; gi++) {
+            g = (gi == 0) ? 0 : 95 + (40 * (gi - 1));
+
+            for (bi = 0; bi < 6; bi++) {
+                b = (bi == 0) ? 0 : 95 + (40 * (bi - 1));
+
+                [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+                [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+                [m_colors addObject:DDMakeColor(r, g, b)];
+
+                index++;
+            }
+        }
+    }
+
+    // Calculate xterm grayscale (using standard algorithm)
+
+    r = 8;
+    g = 8;
+    b = 8;
+
+    while (index < 256) {
+        [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
+        [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
+        [m_colors addObject:DDMakeColor(r, g, b)];
+
+        r += 10;
+        g += 10;
+        b += 10;
+
+        index++;
+    }
+
+    #endif /* if MAP_TO_TERMINAL_APP_COLORS */
+
+    codes_fg = [m_codes_fg copy];
+    codes_bg = [m_codes_bg copy];
+    colors   = [m_colors   copy];
+
+    NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
+    NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
+}
+
++ (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(DDColor *)color {
+    #if TARGET_OS_IPHONE
+
+    // iOS
+
+    BOOL done = NO;
+
+    if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
+        done = [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
+    }
+
+    if (!done) {
+        // The method getRed:green:blue:alpha: was only available starting iOS 5.
+        // So in iOS 4 and earlier, we have to jump through hoops.
+
+        CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
+
+        unsigned char pixel[4];
+        CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)(kCGBitmapAlphaInfoMask & kCGImageAlphaNoneSkipLast));
+
+        CGContextSetFillColorWithColor(context, [color CGColor]);
+        CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
+
+        if (rPtr) {
+            *rPtr = pixel[0] / 255.0f;
+        }
+
+        if (gPtr) {
+            *gPtr = pixel[1] / 255.0f;
+        }
+
+        if (bPtr) {
+            *bPtr = pixel[2] / 255.0f;
+        }
+
+        CGContextRelease(context);
+        CGColorSpaceRelease(rgbColorSpace);
+    }
+
+    #elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
+
+    // OS X without AppKit
+
+    [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
+
+    #else /* if TARGET_OS_IPHONE */
+
+    // OS X with AppKit
+
+    NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+
+    [safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
+    #endif /* if TARGET_OS_IPHONE */
+}
+
+/**
+ * Maps the given color to the closest available color supported by the shell.
+ * The shell may support 256 colors, or only 16.
+ *
+ * This method loops through the known supported color set, and calculates the closest color.
+ * The array index of that color, within the colors array, is then returned.
+ * This array index may also be used as the index within the codes_fg and codes_bg arrays.
+ **/
++ (NSUInteger)codeIndexForColor:(DDColor *)inColor {
+    CGFloat inR, inG, inB;
+
+    [self getRed:&inR green:&inG blue:&inB fromColor:inColor];
+
+    NSUInteger bestIndex = 0;
+    CGFloat lowestDistance = 100.0f;
+
+    NSUInteger i = 0;
+
+    for (DDColor *color in colors) {
+        // Calculate Euclidean distance (lower value means closer to given color)
+
+        CGFloat r, g, b;
+        [self getRed:&r green:&g blue:&b fromColor:color];
+
+    #if CGFLOAT_IS_DOUBLE
+        CGFloat distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0));
+    #else
+        CGFloat distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f));
+    #endif
+
+        NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f",
+                     (unsigned long)i, inR, inG, inB, r, g, b, distance);
+
+        if (distance < lowestDistance) {
+            bestIndex = i;
+            lowestDistance = distance;
+
+            NSLogVerbose(@"DDTTYLogger: New best index = %lu", (unsigned long)bestIndex);
+        }
+
+        i++;
+    }
+
+    return bestIndex;
+}
+
++ (instancetype)sharedInstance {
+    static dispatch_once_t DDTTYLoggerOnceToken;
+
+    dispatch_once(&DDTTYLoggerOnceToken, ^{
+        // Xcode does NOT natively support colors in the Xcode debugging console.
+        // You'll need to install the XcodeColors plugin to see colors in the Xcode console.
+        //
+        // PS - Please read the header file before diving into the source code.
+
+        char *xcode_colors = getenv("XcodeColors");
+        char *term = getenv("TERM");
+
+        if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) {
+            isaXcodeColorTTY = YES;
+        } else if (term) {
+            if (strcasestr(term, "color") != NULL) {
+                isaColorTTY = YES;
+                isaColor256TTY = (strcasestr(term, "256") != NULL);
+
+                if (isaColor256TTY) {
+                    [self initialize_colors_256];
+                } else {
+                    [self initialize_colors_16];
+                }
+            }
+        }
+
+        NSLogInfo(@"DDTTYLogger: isaColorTTY = %@", (isaColorTTY ? @"YES" : @"NO"));
+        NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO"));
+        NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO"));
+
+        sharedInstance = [[self alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+- (instancetype)init {
+    if (sharedInstance != nil) {
+        return nil;
+    }
+
+    if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) {
+        NSLogWarn(@"CocoaLumberjack: Warning: Usage of DDTTYLogger detected when DDOSLogger is available and can be used! Please consider migrating to DDOSLogger.");
+    }
+
+    if ((self = [super init])) {
+        // Initialize 'app' variable (char *)
+
+        _appName = [[NSProcessInfo processInfo] processName];
+
+        _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+        if (_appLen == 0) {
+            _appName = @"<UnnamedApp>";
+            _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+        }
+
+        _app = (char *)calloc(_appLen + 1, sizeof(char));
+
+        if (_app == NULL) {
+            return nil;
+        }
+
+        BOOL processedAppName = [_appName getCString:_app maxLength:(_appLen + 1) encoding:NSUTF8StringEncoding];
+
+        if (NO == processedAppName) {
+            free(_app);
+            return nil;
+        }
+
+        // Initialize 'pid' variable (char *)
+
+        _processID = [NSString stringWithFormat:@"%i", (int)getpid()];
+
+        _pidLen = [_processID lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+        _pid = (char *)calloc(_pidLen + 1, sizeof(char));
+
+        if (_pid == NULL) {
+            free(_app);
+            return nil;
+        }
+
+        BOOL processedID = [_processID getCString:_pid maxLength:(_pidLen + 1) encoding:NSUTF8StringEncoding];
+
+        if (NO == processedID) {
+            free(_app);
+            free(_pid);
+            return nil;
+        }
+
+        // Initialize color stuff
+
+        _colorsEnabled = NO;
+        _colorProfilesArray = [[NSMutableArray alloc] initWithCapacity:8];
+        _colorProfilesDict = [[NSMutableDictionary alloc] initWithCapacity:8];
+
+        _automaticallyAppendNewlineForCustomFormatters = YES;
+    }
+
+    return self;
+}
+
+- (DDLoggerName)loggerName {
+    return DDLoggerNameTTY;
+}
+
+- (void)loadDefaultColorProfiles {
+    [self setForegroundColor:DDMakeColor(214,  57,  30) backgroundColor:nil forFlag:DDLogFlagError];
+    [self setForegroundColor:DDMakeColor(204, 121,  32) backgroundColor:nil forFlag:DDLogFlagWarning];
+}
+
+- (BOOL)colorsEnabled {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    __block BOOL result;
+
+    dispatch_sync(globalLoggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_colorsEnabled;
+        });
+    });
+
+    return result;
+}
+
+- (void)setColorsEnabled:(BOOL)newColorsEnabled {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            self->_colorsEnabled = newColorsEnabled;
+
+            if ([self->_colorProfilesArray count] == 0) {
+                [self loadDefaultColorProfiles];
+            }
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+    NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
+
+    dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+
+    dispatch_async(globalLoggingQueue, ^{
+        dispatch_async(self.loggerQueue, block);
+    });
+}
+
+- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask {
+    [self setForegroundColor:txtColor backgroundColor:bgColor forFlag:mask context:LOG_CONTEXT_ALL];
+}
+
+- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            DDTTYLoggerColorProfile *newColorProfile =
+                [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
+                                                         backgroundColor:bgColor
+                                                                    flag:mask
+                                                                 context:ctxt];
+
+            NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
+
+            NSUInteger i = 0;
+
+            for (DDTTYLoggerColorProfile *colorProfile in self->_colorProfilesArray) {
+                if ((colorProfile->mask == mask) && (colorProfile->context == ctxt)) {
+                    break;
+                }
+
+                i++;
+            }
+
+            if (i < [self->_colorProfilesArray count]) {
+                self->_colorProfilesArray[i] = newColorProfile;
+            } else {
+                [self->_colorProfilesArray addObject:newColorProfile];
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id <NSCopying>)tag {
+    NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            DDTTYLoggerColorProfile *newColorProfile =
+                [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
+                                                         backgroundColor:bgColor
+                                                                    flag:(DDLogFlag)0
+                                                                 context:0];
+
+            NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
+
+            self->_colorProfilesDict[tag] = newColorProfile;
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)clearColorsForFlag:(DDLogFlag)mask {
+    [self clearColorsForFlag:mask context:0];
+}
+
+- (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            NSUInteger i = 0;
+
+            for (DDTTYLoggerColorProfile *colorProfile in self->_colorProfilesArray) {
+                if ((colorProfile->mask == mask) && (colorProfile->context == context)) {
+                    break;
+                }
+
+                i++;
+            }
+
+            if (i < [self->_colorProfilesArray count]) {
+                [self->_colorProfilesArray removeObjectAtIndex:i];
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)clearColorsForTag:(id <NSCopying>)tag {
+    NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self->_colorProfilesDict removeObjectForKey:tag];
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)clearColorsForAllFlags {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self->_colorProfilesArray removeAllObjects];
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)clearColorsForAllTags {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self->_colorProfilesDict removeAllObjects];
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)clearAllColors {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self->_colorProfilesArray removeAllObjects];
+            [self->_colorProfilesDict removeAllObjects];
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_async(globalLoggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    NSString *logMsg = logMessage->_message;
+    BOOL isFormatted = NO;
+
+    if (_logFormatter) {
+        logMsg = [_logFormatter formatLogMessage:logMessage];
+        isFormatted = logMsg != logMessage->_message;
+    }
+
+    if (logMsg) {
+        // Search for a color profile associated with the log message
+
+        DDTTYLoggerColorProfile *colorProfile = nil;
+
+        if (_colorsEnabled) {
+            if (logMessage->_representedObject) {
+                colorProfile = _colorProfilesDict[logMessage->_representedObject];
+            }
+
+            if (colorProfile == nil) {
+                for (DDTTYLoggerColorProfile *cp in _colorProfilesArray) {
+                    if (logMessage->_flag & cp->mask) {
+                        // Color profile set for this context?
+                        if (logMessage->_context == cp->context) {
+                            colorProfile = cp;
+
+                            // Stop searching
+                            break;
+                        }
+
+                        // Check if LOG_CONTEXT_ALL was specified as a default color for this flag
+                        if (cp->context == LOG_CONTEXT_ALL) {
+                            colorProfile = cp;
+
+                            // We don't break to keep searching for more specific color profiles for the context
+                        }
+                    }
+                }
+            }
+        }
+
+        // Convert log message to C string.
+        //
+        // We use the stack instead of the heap for speed if possible.
+        // But we're extra cautious to avoid a stack overflow.
+
+        NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+        const BOOL useStack = msgLen < (1024 * 4);
+
+        char *msg;
+        if (useStack) {
+            msg = (char *)alloca(msgLen + 1);
+        } else {
+            msg = (char *)calloc(msgLen + 1, sizeof(char));
+        }
+        if (msg == NULL) {
+            return;
+        }
+
+        BOOL logMsgEnc = [logMsg getCString:msg maxLength:(msgLen + 1) encoding:NSUTF8StringEncoding];
+        if (!logMsgEnc) {
+            if (!useStack) {
+                free(msg);
+            }
+            return;
+        }
+
+        // Write the log message to STDERR
+
+        if (isFormatted) {
+            // The log message has already been formatted.
+            const int iovec_len = (_automaticallyAppendNewlineForCustomFormatters) ? 5 : 4;
+            struct iovec v[iovec_len];
+
+            if (colorProfile) {
+                v[0].iov_base = colorProfile->fgCode;
+                v[0].iov_len = colorProfile->fgCodeLen;
+
+                v[1].iov_base = colorProfile->bgCode;
+                v[1].iov_len = colorProfile->bgCodeLen;
+
+                v[iovec_len - 1].iov_base = colorProfile->resetCode;
+                v[iovec_len - 1].iov_len = colorProfile->resetCodeLen;
+            } else {
+                v[0].iov_base = "";
+                v[0].iov_len = 0;
+
+                v[1].iov_base = "";
+                v[1].iov_len = 0;
+
+                v[iovec_len - 1].iov_base = "";
+                v[iovec_len - 1].iov_len = 0;
+            }
+
+            v[2].iov_base = msg;
+            v[2].iov_len = msgLen;
+
+            if (iovec_len == 5) {
+                v[3].iov_base = "\n";
+                v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
+            }
+
+            writev(STDERR_FILENO, v, iovec_len);
+        } else {
+            // The log message is unformatted, so apply standard NSLog style formatting.
+
+            int len;
+            char ts[24] = "";
+            size_t tsLen = 0;
+
+            // Calculate timestamp.
+            // The technique below is faster than using NSDateFormatter.
+            if (logMessage->_timestamp) {
+                NSTimeInterval epoch = [logMessage->_timestamp timeIntervalSince1970];
+                struct tm tm;
+                time_t time = (time_t)epoch;
+                (void)localtime_r(&time, &tm);
+                int milliseconds = (int)((epoch - floor(epoch)) * 1000.0);
+
+                len = snprintf(ts, 24, "%04d-%02d-%02d %02d:%02d:%02d:%03d", // yyyy-MM-dd HH:mm:ss:SSS
+                               tm.tm_year + 1900,
+                               tm.tm_mon + 1,
+                               tm.tm_mday,
+                               tm.tm_hour,
+                               tm.tm_min,
+                               tm.tm_sec, milliseconds);
+
+                tsLen = (NSUInteger)MAX(MIN(24 - 1, len), 0);
+            }
+
+            // Calculate thread ID
+            //
+            // How many characters do we need for the thread id?
+            // logMessage->machThreadID is of type mach_port_t, which is an unsigned int.
+            //
+            // 1 hex char = 4 bits
+            // 8 hex chars for 32 bit, plus ending '\0' = 9
+
+            char tid[9];
+            len = snprintf(tid, 9, "%s", [logMessage->_threadID cStringUsingEncoding:NSUTF8StringEncoding]);
+
+            size_t tidLen = (NSUInteger)MAX(MIN(9 - 1, len), 0);
+
+            // Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg
+
+            struct iovec v[13];
+
+            if (colorProfile) {
+                v[0].iov_base = colorProfile->fgCode;
+                v[0].iov_len = colorProfile->fgCodeLen;
+
+                v[1].iov_base = colorProfile->bgCode;
+                v[1].iov_len = colorProfile->bgCodeLen;
+
+                v[12].iov_base = colorProfile->resetCode;
+                v[12].iov_len = colorProfile->resetCodeLen;
+            } else {
+                v[0].iov_base = "";
+                v[0].iov_len = 0;
+
+                v[1].iov_base = "";
+                v[1].iov_len = 0;
+
+                v[12].iov_base = "";
+                v[12].iov_len = 0;
+            }
+
+            v[2].iov_base = ts;
+            v[2].iov_len = tsLen;
+
+            v[3].iov_base = " ";
+            v[3].iov_len = 1;
+
+            v[4].iov_base = _app;
+            v[4].iov_len = _appLen;
+
+            v[5].iov_base = "[";
+            v[5].iov_len = 1;
+
+            v[6].iov_base = _pid;
+            v[6].iov_len = _pidLen;
+
+            v[7].iov_base = ":";
+            v[7].iov_len = 1;
+
+            v[8].iov_base = tid;
+            v[8].iov_len = MIN((size_t)8, tidLen); // snprintf doesn't return what you might think
+
+            v[9].iov_base = "] ";
+            v[9].iov_len = 2;
+
+            v[10].iov_base = (char *)msg;
+            v[10].iov_len = msgLen;
+
+            v[11].iov_base = "\n";
+            v[11].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
+
+            writev(STDERR_FILENO, v, 13);
+        }
+
+        if (!useStack) {
+            free(msg);
+        }
+    }
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation DDTTYLoggerColorProfile
+
+- (instancetype)initWithForegroundColor:(DDColor *)fgColor backgroundColor:(DDColor *)bgColor flag:(DDLogFlag)aMask context:(NSInteger)ctxt {
+    if ((self = [super init])) {
+        mask = aMask;
+        context = ctxt;
+
+        CGFloat r, g, b;
+
+        if (fgColor) {
+            [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor];
+
+            fg_r = (uint8_t)(r * 255.0f);
+            fg_g = (uint8_t)(g * 255.0f);
+            fg_b = (uint8_t)(b * 255.0f);
+        }
+
+        if (bgColor) {
+            [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor];
+
+            bg_r = (uint8_t)(r * 255.0f);
+            bg_g = (uint8_t)(g * 255.0f);
+            bg_b = (uint8_t)(b * 255.0f);
+        }
+
+        if (fgColor && isaColorTTY) {
+            // Map foreground color to closest available shell color
+
+            fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor];
+            fgCodeRaw   = codes_fg[fgCodeIndex];
+
+            NSString *escapeSeq = @"\033[";
+
+            NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+            NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+            BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode)      maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
+            BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
+
+            if (!escapeSeqEnc || !fgCodeRawEsc) {
+                return nil;
+            }
+
+            fgCodeLen = len1 + len2;
+        } else if (fgColor && isaXcodeColorTTY) {
+            // Convert foreground color to color code sequence
+
+            const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
+
+            int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b);
+            fgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
+        } else {
+            // No foreground color or no color support
+
+            fgCode[0] = '\0';
+            fgCodeLen = 0;
+        }
+
+        if (bgColor && isaColorTTY) {
+            // Map background color to closest available shell color
+
+            bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor];
+            bgCodeRaw   = codes_bg[bgCodeIndex];
+
+            NSString *escapeSeq = @"\033[";
+
+            NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+            NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+            BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode)      maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
+            BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
+
+            if (!escapeSeqEnc || !bgCodeRawEsc) {
+                return nil;
+            }
+
+            bgCodeLen = len1 + len2;
+        } else if (bgColor && isaXcodeColorTTY) {
+            // Convert background color to color code sequence
+
+            const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
+
+            int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b);
+            bgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
+        } else {
+            // No background color or no color support
+
+            bgCode[0] = '\0';
+            bgCodeLen = 0;
+        }
+
+        if (isaColorTTY) {
+            resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, "\033[0m"), 0);
+        } else if (isaXcodeColorTTY) {
+            resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, XCODE_COLORS_RESET), 0);
+        } else {
+            resetCode[0] = '\0';
+            resetCodeLen = 0;
+        }
+    }
+
+    return self;
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:
+            @"<DDTTYLoggerColorProfile: %p mask:%i ctxt:%ld fg:%u,%u,%u bg:%u,%u,%u fgCode:%@ bgCode:%@>",
+            self, (int)mask, (long)context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw];
+}
+
+@end

+ 57 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m

@@ -0,0 +1,57 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h>
+
+@implementation DDContextAllowlistFilterLogFormatter (Deprecated)
+
+- (void)addToWhitelist:(NSInteger)loggingContext {
+    [self addToAllowlist:loggingContext];
+}
+
+- (void)removeFromWhitelist:(NSInteger)loggingContext {
+    [self removeFromAllowlist:loggingContext];
+}
+
+- (NSArray *)whitelist {
+    return [self allowlist];
+}
+
+- (BOOL)isOnWhitelist:(NSInteger)loggingContext {
+    return [self isOnAllowlist:loggingContext];
+}
+
+@end
+
+
+@implementation DDContextDenylistFilterLogFormatter (Deprecated)
+
+- (void)addToBlacklist:(NSInteger)loggingContext {
+    [self addToDenylist:loggingContext];
+}
+
+- (void)removeFromBlacklist:(NSInteger)loggingContext {
+    [self removeFromDenylist:loggingContext];
+}
+
+- (NSArray *)blacklist {
+    return [self denylist];
+}
+
+- (BOOL)isOnBlacklist:(NSInteger)loggingContext {
+    return [self isOnDenylist:loggingContext];
+}
+
+@end

+ 185 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m

@@ -0,0 +1,185 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <pthread/pthread.h>
+
+#import <CocoaLumberjack/DDContextFilterLogFormatter.h>
+
+@interface DDLoggingContextSet : NSObject
+
+@property (readonly, copy, nonnull) NSArray *currentSet;
+
+- (void)addToSet:(NSInteger)loggingContext;
+- (void)removeFromSet:(NSInteger)loggingContext;
+
+- (BOOL)isInSet:(NSInteger)loggingContext;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDContextAllowlistFilterLogFormatter () {
+    DDLoggingContextSet *_contextSet;
+}
+@end
+
+@implementation DDContextAllowlistFilterLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _contextSet = [[DDLoggingContextSet alloc] init];
+    }
+    return self;
+}
+
+- (void)addToAllowlist:(NSInteger)loggingContext {
+    [_contextSet addToSet:loggingContext];
+}
+
+- (void)removeFromAllowlist:(NSInteger)loggingContext {
+    [_contextSet removeFromSet:loggingContext];
+}
+
+- (NSArray *)allowlist {
+    return [_contextSet currentSet];
+}
+
+- (BOOL)isOnAllowlist:(NSInteger)loggingContext {
+    return [_contextSet isInSet:loggingContext];
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    if ([self isOnAllowlist:logMessage->_context]) {
+        return logMessage->_message;
+    } else {
+        return nil;
+    }
+}
+
+@end
+
+
+@interface DDContextDenylistFilterLogFormatter () {
+    DDLoggingContextSet *_contextSet;
+}
+@end
+
+@implementation DDContextDenylistFilterLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _contextSet = [[DDLoggingContextSet alloc] init];
+    }
+    return self;
+}
+
+- (void)addToDenylist:(NSInteger)loggingContext {
+    [_contextSet addToSet:loggingContext];
+}
+
+- (void)removeFromDenylist:(NSInteger)loggingContext {
+    [_contextSet removeFromSet:loggingContext];
+}
+
+- (NSArray *)denylist {
+    return [_contextSet currentSet];
+}
+
+- (BOOL)isOnDenylist:(NSInteger)loggingContext {
+    return [_contextSet isInSet:loggingContext];
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    if ([self isOnDenylist:logMessage->_context]) {
+        return nil;
+    } else {
+        return logMessage->_message;
+    }
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLoggingContextSet () {
+    pthread_mutex_t _mutex;
+    NSMutableSet *_set;
+}
+@end
+
+@implementation DDLoggingContextSet
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _set = [[NSMutableSet alloc] init];
+        pthread_mutex_init(&_mutex, NULL);
+    }
+
+    return self;
+}
+
+- (void)dealloc {
+    pthread_mutex_destroy(&_mutex);
+}
+
+- (void)addToSet:(NSInteger)loggingContext {
+    pthread_mutex_lock(&_mutex);
+    {
+        [_set addObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+- (void)removeFromSet:(NSInteger)loggingContext {
+    pthread_mutex_lock(&_mutex);
+    {
+        [_set removeObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+- (NSArray *)currentSet {
+    NSArray *result = nil;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = [_set allObjects];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+- (BOOL)isInSet:(NSInteger)loggingContext {
+    BOOL result = NO;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = [_set containsObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+@end

+ 269 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m

@@ -0,0 +1,269 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <pthread/pthread.h>
+#import <stdatomic.h>
+#import <sys/qos.h>
+
+#import <CocoaLumberjack/DDDispatchQueueLogFormatter.h>
+
+DDQualityOfServiceName const DDQualityOfServiceUserInteractive = @"UI";
+DDQualityOfServiceName const DDQualityOfServiceUserInitiated   = @"IN";
+DDQualityOfServiceName const DDQualityOfServiceDefault         = @"DF";
+DDQualityOfServiceName const DDQualityOfServiceUtility         = @"UT";
+DDQualityOfServiceName const DDQualityOfServiceBackground      = @"BG";
+DDQualityOfServiceName const DDQualityOfServiceUnspecified     = @"UN";
+
+static DDQualityOfServiceName _qos_name(NSUInteger qos) {
+    switch ((qos_class_t) qos) {
+        case QOS_CLASS_USER_INTERACTIVE: return DDQualityOfServiceUserInteractive;
+        case QOS_CLASS_USER_INITIATED:   return DDQualityOfServiceUserInitiated;
+        case QOS_CLASS_DEFAULT:          return DDQualityOfServiceDefault;
+        case QOS_CLASS_UTILITY:          return DDQualityOfServiceUtility;
+        case QOS_CLASS_BACKGROUND:       return DDQualityOfServiceBackground;
+        default:                         return DDQualityOfServiceUnspecified;
+    }
+}
+
+#pragma mark - DDDispatchQueueLogFormatter
+
+@interface DDDispatchQueueLogFormatter () {
+    NSDateFormatter *_dateFormatter;      // Use [self stringFromDate]
+
+    pthread_mutex_t _mutex;
+
+    NSUInteger _minQueueLength;           // _prefix == Only access via atomic property
+    NSUInteger _maxQueueLength;           // _prefix == Only access via atomic property
+    NSMutableDictionary *_replacements;   // _prefix == Only access from within spinlock
+}
+@end
+
+
+@implementation DDDispatchQueueLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _dateFormatter = [self createDateFormatter];
+
+        pthread_mutex_init(&_mutex, NULL);
+        _replacements = [[NSMutableDictionary alloc] init];
+
+        // Set default replacements:
+        _replacements[@"com.apple.main-thread"] = @"main";
+    }
+
+    return self;
+}
+
+- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode {
+    return [self init];
+}
+
+- (void)dealloc {
+    pthread_mutex_destroy(&_mutex);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@synthesize minQueueLength = _minQueueLength;
+@synthesize maxQueueLength = _maxQueueLength;
+
+- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel {
+    NSString *result = nil;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = _replacements[longLabel];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel {
+    pthread_mutex_lock(&_mutex);
+    {
+        if (shortLabel) {
+            _replacements[longLabel] = shortLabel;
+        } else {
+            [_replacements removeObjectForKey:longLabel];
+        }
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark DDLogFormatter
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSDateFormatter *)createDateFormatter {
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    [self configureDateFormatter:formatter];
+    return formatter;
+}
+
+- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter {
+    [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
+    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"];
+    [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+    [dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]];
+}
+
+- (NSString *)stringFromDate:(NSDate *)date {
+    return [_dateFormatter stringFromDate:date];
+}
+
+- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage {
+    // As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
+
+    NSUInteger minQueueLength = self.minQueueLength;
+    NSUInteger maxQueueLength = self.maxQueueLength;
+
+    // Get the name of the queue, thread, or machID (whichever we are to use).
+
+    NSString *queueThreadLabel = nil;
+
+    BOOL useQueueLabel = YES;
+    BOOL useThreadName = NO;
+
+    if (logMessage->_queueLabel) {
+        // If you manually create a thread, it's dispatch_queue will have one of the thread names below.
+        // Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
+
+        NSArray *names = @[
+            @"com.apple.root.low-priority",
+            @"com.apple.root.default-priority",
+            @"com.apple.root.high-priority",
+            @"com.apple.root.low-overcommit-priority",
+            @"com.apple.root.default-overcommit-priority",
+            @"com.apple.root.high-overcommit-priority",
+            @"com.apple.root.default-qos.overcommit"
+        ];
+
+        for (NSString * name in names) {
+            if ([logMessage->_queueLabel isEqualToString:name]) {
+                useQueueLabel = NO;
+                useThreadName = [logMessage->_threadName length] > 0;
+                break;
+            }
+        }
+    } else {
+        useQueueLabel = NO;
+        useThreadName = [logMessage->_threadName length] > 0;
+    }
+
+    if (useQueueLabel || useThreadName) {
+        NSString *fullLabel;
+        NSString *abrvLabel;
+
+        if (useQueueLabel) {
+            fullLabel = logMessage->_queueLabel;
+        } else {
+            fullLabel = logMessage->_threadName;
+        }
+
+        pthread_mutex_lock(&_mutex);
+        {
+            abrvLabel = _replacements[fullLabel];
+        }
+        pthread_mutex_unlock(&_mutex);
+
+        if (abrvLabel) {
+            queueThreadLabel = abrvLabel;
+        } else {
+            queueThreadLabel = fullLabel;
+        }
+    } else {
+        queueThreadLabel = logMessage->_threadID;
+    }
+
+    // Now use the thread label in the output
+
+    NSUInteger labelLength = [queueThreadLabel length];
+
+    // labelLength > maxQueueLength : truncate
+    // labelLength < minQueueLength : padding
+    //                              : exact
+
+    if ((maxQueueLength > 0) && (labelLength > maxQueueLength)) {
+        // Truncate
+
+        return [queueThreadLabel substringToIndex:maxQueueLength];
+    } else if (labelLength < minQueueLength) {
+        // Padding
+
+        NSUInteger numSpaces = minQueueLength - labelLength;
+
+        char spaces[numSpaces + 1];
+        memset(spaces, ' ', numSpaces);
+        spaces[numSpaces] = '\0';
+
+        return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces];
+    } else {
+        // Exact
+
+        return queueThreadLabel;
+    }
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)];
+    NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
+
+    return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message];
+}
+
+@end
+
+#pragma mark - DDAtomicCounter
+
+@interface DDAtomicCounter() {
+    atomic_int_fast32_t _value;
+}
+@end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDAtomicCounter
+#pragma clang diagnostic pop
+
+- (instancetype)initWithDefaultValue:(int32_t)defaultValue {
+    if ((self = [super init])) {
+        atomic_init(&_value, defaultValue);
+    }
+    return self;
+}
+
+- (int32_t)value {
+    return atomic_load_explicit(&_value, memory_order_relaxed);
+}
+
+- (int32_t)increment {
+    int32_t old = atomic_fetch_add_explicit(&_value, 1, memory_order_relaxed);
+    return (old + 1);
+}
+
+- (int32_t)decrement {
+    int32_t old = atomic_fetch_sub_explicit(&_value, 1, memory_order_relaxed);
+    return (old - 1);
+}
+
+@end

+ 204 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m

@@ -0,0 +1,204 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <sys/mount.h>
+
+#import <CocoaLumberjack/DDFileLogger+Buffering.h>
+#import "../DDFileLogger+Internal.h"
+
+static const NSUInteger kDDDefaultBufferSize = 4096; // 4 kB, block f_bsize on iphone7
+static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone7
+
+// Reads attributes from base file system to determine buffer size.
+// see statfs in sys/mount.h for descriptions of f_iosize and f_bsize.
+// f_bsize == "default", and f_iosize == "max"
+static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
+    struct statfs *mountedFileSystems = NULL;
+    int count = getmntinfo(&mountedFileSystems, 0);
+
+    for (int i = 0; i < count; i++) {
+        struct statfs mounted = mountedFileSystems[i];
+        const char *name = mounted.f_mntonname;
+
+        // We can use 2 as max here, since any length > 1 will fail the if-statement.
+        if (strnlen(name, 2) == 1 && *name == '/') {
+            return max ? (NSUInteger)mounted.f_iosize : (NSUInteger)mounted.f_bsize;
+        }
+    }
+
+    return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
+}
+
+static NSUInteger DDGetMaxBufferSizeBytes() {
+    static NSUInteger maxBufferSize = 0;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        maxBufferSize = p_DDGetDefaultBufferSizeBytesMax(YES);
+    });
+    return maxBufferSize;
+}
+
+static NSUInteger DDGetDefaultBufferSizeBytes() {
+    static NSUInteger defaultBufferSize = 0;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        defaultBufferSize = p_DDGetDefaultBufferSizeBytesMax(NO);
+    });
+    return defaultBufferSize;
+}
+
+@interface DDBufferedProxy : NSProxy
+
+@property (nonatomic) DDFileLogger *fileLogger;
+@property (nonatomic) NSOutputStream *buffer;
+
+@property (nonatomic) NSUInteger maxBufferSizeBytes;
+@property (nonatomic) NSUInteger currentBufferSizeBytes;
+
+@end
+
+@implementation DDBufferedProxy
+
+- (instancetype)initWithFileLogger:(DDFileLogger *)fileLogger {
+    _fileLogger = fileLogger;
+    _maxBufferSizeBytes = DDGetDefaultBufferSizeBytes();
+    [self flushBuffer];
+
+    return self;
+}
+
+- (void)dealloc {
+    dispatch_block_t block = ^{
+        [self lt_sendBufferedDataToFileLogger];
+        self.fileLogger = nil;
+    };
+
+    if ([self->_fileLogger isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_sync(self->_fileLogger.loggerQueue, block);
+    }
+}
+
+#pragma mark - Buffering
+
+- (void)flushBuffer {
+    [_buffer close];
+    _buffer = [NSOutputStream outputStreamToMemory];
+    [_buffer open];
+    _currentBufferSizeBytes = 0;
+}
+
+- (void)lt_sendBufferedDataToFileLogger {
+    NSData *data = [_buffer propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    [_fileLogger lt_logData:data];
+    [self flushBuffer];
+}
+
+#pragma mark - Logging
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
+    NSData *data = [_fileLogger lt_dataForMessage:logMessage];
+
+    if (data.length == 0) {
+        return;
+    }
+
+    [data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) {
+        NSUInteger bytesLength = byteRange.length;
+#ifdef NS_BLOCK_ASSERTIONS
+        __unused
+#endif
+        NSInteger written = [_buffer write:bytes maxLength:bytesLength];
+        NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
+
+        _currentBufferSizeBytes += bytesLength;
+
+        if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
+            [self lt_sendBufferedDataToFileLogger];
+        }
+    }];
+}
+
+- (void)flush {
+    // This method is public.
+    // We need to execute the rolling on our logging thread/queue.
+
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            [self lt_sendBufferedDataToFileLogger];
+            [self.fileLogger flush];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self.fileLogger isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
+        NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+
+        dispatch_sync(globalLoggingQueue, ^{
+            dispatch_sync(self.fileLogger.loggerQueue, block);
+        });
+    }
+}
+
+#pragma mark - Properties
+
+- (void)setMaxBufferSizeBytes:(NSUInteger)newBufferSizeBytes {
+    _maxBufferSizeBytes = MIN(newBufferSizeBytes, DDGetMaxBufferSizeBytes());
+}
+
+#pragma mark - Wrapping
+
+- (DDFileLogger *)wrapWithBuffer {
+    return (DDFileLogger *)self;
+}
+
+- (DDFileLogger *)unwrapFromBuffer {
+    return (DDFileLogger *)self.fileLogger;
+}
+
+#pragma mark - NSProxy
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
+    return [self.fileLogger methodSignatureForSelector:sel];
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector {
+    return [self.fileLogger respondsToSelector:aSelector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+    [invocation invokeWithTarget:self.fileLogger];
+}
+
+@end
+
+@implementation DDFileLogger (Buffering)
+
+- (instancetype)wrapWithBuffer {
+    return (DDFileLogger *)[[DDBufferedProxy alloc] initWithFileLogger:self];
+}
+
+- (instancetype)unwrapFromBuffer {
+    return self;
+}
+
+@end

+ 111 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m

@@ -0,0 +1,111 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <CocoaLumberjack/DDMultiFormatter.h>
+
+@interface DDMultiFormatter () {
+    dispatch_queue_t _queue;
+    NSMutableArray *_formatters;
+}
+
+- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message;
+
+@end
+
+
+@implementation DDMultiFormatter
+
+- (instancetype)init {
+    self = [super init];
+
+    if (self) {
+        _queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", DISPATCH_QUEUE_CONCURRENT);
+        _formatters = [NSMutableArray new];
+    }
+
+    return self;
+}
+
+#pragma mark Processing
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    __block NSString *line = logMessage->_message;
+
+    dispatch_sync(_queue, ^{
+        for (id<DDLogFormatter> formatter in self->_formatters) {
+            DDLogMessage *message = [self logMessageForLine:line originalMessage:logMessage];
+            line = [formatter formatLogMessage:message];
+
+            if (!line) {
+                break;
+            }
+        }
+    });
+
+    return line;
+}
+
+- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message {
+    DDLogMessage *newMessage = [message copy];
+
+    newMessage->_message = line;
+    return newMessage;
+}
+
+#pragma mark Formatters
+
+- (NSArray *)formatters {
+    __block NSArray *formatters;
+
+    dispatch_sync(_queue, ^{
+        formatters = [self->_formatters copy];
+    });
+
+    return formatters;
+}
+
+- (void)addFormatter:(id<DDLogFormatter>)formatter {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters addObject:formatter];
+    });
+}
+
+- (void)removeFormatter:(id<DDLogFormatter>)formatter {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters removeObject:formatter];
+    });
+}
+
+- (void)removeAllFormatters {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters removeAllObjects];
+    });
+}
+
+- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter {
+    __block BOOL hasFormatter;
+
+    dispatch_sync(_queue, ^{
+        hasFormatter = [self->_formatters containsObject:formatter];
+    });
+
+    return hasFormatter;
+}
+
+@end

+ 104 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h

@@ -0,0 +1,104 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+/**
+ * Welcome to CocoaLumberjack!
+ *
+ * The project page has a wealth of documentation if you have any questions.
+ * https://github.com/CocoaLumberjack/CocoaLumberjack
+ *
+ * If you're new to the project you may wish to read "Getting Started" at:
+ * Documentation/GettingStarted.md
+ *
+ * Otherwise, here is a quick refresher.
+ * There are three steps to using the macros:
+ *
+ * Step 1:
+ * Import the header in your implementation or prefix file:
+ *
+ * #import <CocoaLumberjack/CocoaLumberjack.h>
+ *
+ * Step 2:
+ * Define your logging level in your implementation file:
+ *
+ * // Log levels: off, error, warn, info, verbose
+ * static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
+ *
+ * Step 2 [3rd party frameworks]:
+ *
+ * Define your LOG_LEVEL_DEF to a different variable/function than ddLogLevel:
+ *
+ * // #undef LOG_LEVEL_DEF // Undefine first only if needed
+ * #define LOG_LEVEL_DEF myLibLogLevel
+ *
+ * Define your logging level in your implementation file:
+ *
+ * // Log levels: off, error, warn, info, verbose
+ * static const DDLogLevel myLibLogLevel = DDLogLevelVerbose;
+ *
+ * Step 3:
+ * Replace your NSLog statements with DDLog statements according to the severity of the message.
+ *
+ * NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!");
+ *
+ * DDLog works exactly the same as NSLog.
+ * This means you can pass it multiple variables just like NSLog.
+ **/
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for CocoaLumberjack.
+FOUNDATION_EXPORT double CocoaLumberjackVersionNumber;
+
+//! Project version string for CocoaLumberjack.
+FOUNDATION_EXPORT const unsigned char CocoaLumberjackVersionString[];
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+// Core
+#import <CocoaLumberjack/DDLog.h>
+
+// Main macros
+#import <CocoaLumberjack/DDLogMacros.h>
+#import <CocoaLumberjack/DDAssertMacros.h>
+
+// Capture ASL
+#import <CocoaLumberjack/DDASLLogCapture.h>
+
+// Loggers
+#import <CocoaLumberjack/DDLoggerNames.h>
+
+#import <CocoaLumberjack/DDTTYLogger.h>
+#import <CocoaLumberjack/DDASLLogger.h>
+#import <CocoaLumberjack/DDFileLogger.h>
+#import <CocoaLumberjack/DDOSLogger.h>
+
+// Extensions
+#import <CocoaLumberjack/DDContextFilterLogFormatter.h>
+#import <CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h>
+#import <CocoaLumberjack/DDDispatchQueueLogFormatter.h>
+#import <CocoaLumberjack/DDMultiFormatter.h>
+#import <CocoaLumberjack/DDFileLogger+Buffering.h>
+
+// CLI
+#import <CocoaLumberjack/CLIColor.h>
+
+// etc
+#import <CocoaLumberjack/DDAbstractDatabaseLogger.h>
+#import <CocoaLumberjack/DDLog+LOGV.h>
+#import <CocoaLumberjack/DDLegacyMacros.h>

+ 75 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h

@@ -0,0 +1,75 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+/**
+ * Legacy macros used for 1.9.x backwards compatibility.
+ *
+ * Imported by default when importing a DDLog.h directly and DD_LEGACY_MACROS is not defined and set to 0.
+ **/
+#if DD_LEGACY_MACROS
+
+#warning CocoaLumberjack 1.9.x legacy macros enabled. \
+Disable legacy macros by importing CocoaLumberjack.h or DDLogMacros.h instead of DDLog.h or add `#define DD_LEGACY_MACROS 0` before importing DDLog.h.
+
+#ifndef LOG_LEVEL_DEF
+    #define LOG_LEVEL_DEF ddLogLevel
+#endif
+
+#define LOG_FLAG_ERROR    DDLogFlagError
+#define LOG_FLAG_WARN     DDLogFlagWarning
+#define LOG_FLAG_INFO     DDLogFlagInfo
+#define LOG_FLAG_DEBUG    DDLogFlagDebug
+#define LOG_FLAG_VERBOSE  DDLogFlagVerbose
+
+#define LOG_LEVEL_OFF     DDLogLevelOff
+#define LOG_LEVEL_ERROR   DDLogLevelError
+#define LOG_LEVEL_WARN    DDLogLevelWarning
+#define LOG_LEVEL_INFO    DDLogLevelInfo
+#define LOG_LEVEL_DEBUG   DDLogLevelDebug
+#define LOG_LEVEL_VERBOSE DDLogLevelVerbose
+#define LOG_LEVEL_ALL     DDLogLevelAll
+
+#define LOG_ASYNC_ENABLED YES
+
+#define LOG_ASYNC_ERROR    ( NO && LOG_ASYNC_ENABLED)
+#define LOG_ASYNC_WARN     (YES && LOG_ASYNC_ENABLED)
+#define LOG_ASYNC_INFO     (YES && LOG_ASYNC_ENABLED)
+#define LOG_ASYNC_DEBUG    (YES && LOG_ASYNC_ENABLED)
+#define LOG_ASYNC_VERBOSE  (YES && LOG_ASYNC_ENABLED)
+
+#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
+        [DDLog log : isAsynchronous                                     \
+             level : lvl                                                \
+              flag : flg                                                \
+           context : ctx                                                \
+              file : __FILE__                                           \
+          function : fnct                                               \
+              line : __LINE__                                           \
+               tag : atag                                               \
+            format : (frmt), ## __VA_ARGS__]
+
+#define LOG_MAYBE(async, lvl, flg, ctx, fnct, frmt, ...)                       \
+        do { if((lvl & flg) != 0) LOG_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, ##__VA_ARGS__); } while(0)
+
+#define LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \
+        LOG_MAYBE(async, lvl, flg, ctx, __PRETTY_FUNCTION__, frmt, ## __VA_ARGS__)
+
+#define DDLogError(frmt, ...)   LOG_OBJC_MAYBE(LOG_ASYNC_ERROR,   LOG_LEVEL_DEF, LOG_FLAG_ERROR,   0, frmt, ##__VA_ARGS__)
+#define DDLogWarn(frmt, ...)    LOG_OBJC_MAYBE(LOG_ASYNC_WARN,    LOG_LEVEL_DEF, LOG_FLAG_WARN,    0, frmt, ##__VA_ARGS__)
+#define DDLogInfo(frmt, ...)    LOG_OBJC_MAYBE(LOG_ASYNC_INFO,    LOG_LEVEL_DEF, LOG_FLAG_INFO,    0, frmt, ##__VA_ARGS__)
+#define DDLogDebug(frmt, ...)   LOG_OBJC_MAYBE(LOG_ASYNC_DEBUG,   LOG_LEVEL_DEF, LOG_FLAG_DEBUG,   0, frmt, ##__VA_ARGS__)
+#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
+
+#endif

+ 54 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h

@@ -0,0 +1,54 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if TARGET_OS_OSX
+
+#import <Foundation/Foundation.h>
+#import <QuartzCore/QuartzCore.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class represents an NSColor replacement for CLI projects that don't link with AppKit
+ **/
+@interface CLIColor : NSObject
+
+/**
+ *  Convenience method for creating a `CLIColor` instance from RGBA params
+ *
+ *  @param red   red channel, between 0 and 1
+ *  @param green green channel, between 0 and 1
+ *  @param blue  blue channel, between 0 and 1
+ *  @param alpha alpha channel, between 0 and 1
+ */
++ (instancetype)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+
+/**
+ *  Get the RGBA components from a `CLIColor`
+ *
+ *  @param red   red channel, between 0 and 1
+ *  @param green green channel, between 0 and 1
+ *  @param blue  blue channel, between 0 and 1
+ *  @param alpha alpha channel, between 0 and 1
+ */
+- (void)getRed:(nullable CGFloat *)red green:(nullable CGFloat *)green blue:(nullable CGFloat *)blue alpha:(nullable CGFloat *)alpha NS_SWIFT_NAME(get(red:green:blue:alpha:));
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif

+ 46 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h

@@ -0,0 +1,46 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDASLLogger.h>
+
+@protocol DDLogger;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  This class provides the ability to capture the ASL (Apple System Logs)
+ */
+API_DEPRECATED("Use DDOSLogger instead", macosx(10.4,10.12), ios(2.0,10.0), watchos(2.0,3.0), tvos(9.0,10.0))
+@interface DDASLLogCapture : NSObject
+
+/**
+ *  Start capturing logs
+ */
++ (void)start;
+
+/**
+ *  Stop capturing logs
+ */
++ (void)stop;
+
+/**
+ *  The current capture level.
+ *  @note Default log level: DDLogLevelVerbose (i.e. capture all ASL messages).
+ */
+@property (class) DDLogLevel captureLevel;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 63 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h

@@ -0,0 +1,63 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+// Custom key set on messages sent to ASL
+extern const char* const kDDASLKeyDDLog;
+
+// Value set for kDDASLKeyDDLog
+extern const char* const kDDASLDDLogValue;
+
+/**
+ * This class provides a logger for the Apple System Log facility.
+ *
+ * As described in the "Getting Started" page,
+ * the traditional NSLog() function directs its output to two places:
+ *
+ * - Apple System Log
+ * - StdErr (if stderr is a TTY) so log statements show up in Xcode console
+ *
+ * To duplicate NSLog() functionality you can simply add this logger and a tty logger.
+ * However, if you instead choose to use file logging (for faster performance),
+ * you may choose to use a file logger and a tty logger.
+ **/
+API_DEPRECATED("Use DDOSLogger instead", macosx(10.4,10.12), ios(2.0,10.0), watchos(2.0,3.0), tvos(9.0,10.0))
+@interface DDASLLogger : DDAbstractLogger <DDLogger>
+
+/**
+ *  Singleton method
+ *
+ *  @return the shared instance
+ */
+@property (nonatomic, class, readonly, strong) DDASLLogger *sharedInstance;
+
+// Inherited from DDAbstractLogger
+
+// - (id <DDLogFormatter>)logFormatter;
+// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 127 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h

@@ -0,0 +1,127 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides an abstract implementation of a database logger.
+ *
+ * That is, it provides the base implementation for a database logger to build atop of.
+ * All that is needed for a concrete database logger is to extend this class
+ * and override the methods in the implementation file that are prefixed with "db_".
+ **/
+@interface DDAbstractDatabaseLogger : DDAbstractLogger {
+    
+@protected
+    NSUInteger _saveThreshold;
+    NSTimeInterval _saveInterval;
+    NSTimeInterval _maxAge;
+    NSTimeInterval _deleteInterval;
+    BOOL _deleteOnEverySave;
+    
+    NSInteger _saveTimerSuspended;
+    NSUInteger _unsavedCount;
+    dispatch_time_t _unsavedTime;
+    dispatch_source_t _saveTimer;
+    dispatch_time_t _lastDeleteTime;
+    dispatch_source_t _deleteTimer;
+}
+
+/**
+ * Specifies how often to save the data to disk.
+ * Since saving is an expensive operation (disk io) it is not done after every log statement.
+ * These properties allow you to configure how/when the logger saves to disk.
+ *
+ * A save is done when either (whichever happens first):
+ *
+ * - The number of unsaved log entries reaches saveThreshold
+ * - The amount of time since the oldest unsaved log entry was created reaches saveInterval
+ *
+ * You can optionally disable the saveThreshold by setting it to zero.
+ * If you disable the saveThreshold you are entirely dependent on the saveInterval.
+ *
+ * You can optionally disable the saveInterval by setting it to zero (or a negative value).
+ * If you disable the saveInterval you are entirely dependent on the saveThreshold.
+ *
+ * It's not wise to disable both saveThreshold and saveInterval.
+ *
+ * The default saveThreshold is 500.
+ * The default saveInterval is 60 seconds.
+ **/
+@property (assign, readwrite) NSUInteger saveThreshold;
+
+/**
+ *  See the description for the `saveThreshold` property
+ */
+@property (assign, readwrite) NSTimeInterval saveInterval;
+
+/**
+ * It is likely you don't want the log entries to persist forever.
+ * Doing so would allow the database to grow infinitely large over time.
+ *
+ * The maxAge property provides a way to specify how old a log statement can get
+ * before it should get deleted from the database.
+ *
+ * The deleteInterval specifies how often to sweep for old log entries.
+ * Since deleting is an expensive operation (disk io) is is done on a fixed interval.
+ *
+ * An alternative to the deleteInterval is the deleteOnEverySave option.
+ * This specifies that old log entries should be deleted during every save operation.
+ *
+ * You can optionally disable the maxAge by setting it to zero (or a negative value).
+ * If you disable the maxAge then old log statements are not deleted.
+ *
+ * You can optionally disable the deleteInterval by setting it to zero (or a negative value).
+ *
+ * If you disable both deleteInterval and deleteOnEverySave then old log statements are not deleted.
+ *
+ * It's not wise to enable both deleteInterval and deleteOnEverySave.
+ *
+ * The default maxAge is 7 days.
+ * The default deleteInterval is 5 minutes.
+ * The default deleteOnEverySave is NO.
+ **/
+@property (assign, readwrite) NSTimeInterval maxAge;
+
+/**
+ *  See the description for the `maxAge` property
+ */
+@property (assign, readwrite) NSTimeInterval deleteInterval;
+
+/**
+ *  See the description for the `maxAge` property
+ */
+@property (assign, readwrite) BOOL deleteOnEverySave;
+
+/**
+ * Forces a save of any pending log entries (flushes log entries to disk).
+ **/
+- (void)savePendingLogEntries;
+
+/**
+ * Removes any log entries that are older than maxAge.
+ **/
+- (void)deleteOldLogEntries;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 30 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h

@@ -0,0 +1,30 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+/**
+ * NSAssert replacement that will output a log message even when assertions are disabled.
+ **/
+#define DDAssert(condition, frmt, ...)                                                \
+        if (!(condition)) {                                                           \
+            NSString *description = [NSString stringWithFormat:frmt, ## __VA_ARGS__]; \
+            DDLogError(@"%@", description);                                           \
+            NSAssert(NO, @"%@", description);                                         \
+        }
+#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)
+
+/**
+ * Analog to `DDAssertionFailure` from DDAssert.swift for use in Objective C
+ */
+#define DDAssertionFailure(frmt, ...) DDAssert(NO, frmt, ##__VA_ARGS__)

+ 119 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h

@@ -0,0 +1,119 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDContextFilterLogFormatter.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides a log formatter that filters log statements from a logging context not on the whitelist.
+ * @deprecated Use DDContextAllowlistFilterLogFormatter instead.
+ *
+ * A log formatter can be added to any logger to format and/or filter its output.
+ * You can learn more about log formatters here:
+ * Documentation/CustomFormatters.md
+ *
+ * You can learn more about logging context's here:
+ * Documentation/CustomContext.md
+ *
+ * But here's a quick overview / refresher:
+ *
+ * Every log statement has a logging context.
+ * These come from the underlying logging macros defined in DDLog.h.
+ * The default logging context is zero.
+ * You can define multiple logging context's for use in your application.
+ * For example, logically separate parts of your app each have a different logging context.
+ * Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context.
+ **/
+__attribute__((deprecated("Use DDContextAllowlistFilterLogFormatter instead")))
+typedef DDContextAllowlistFilterLogFormatter DDContextWhitelistFilterLogFormatter;
+
+@interface DDContextAllowlistFilterLogFormatter (Deprecated)
+
+/**
+ *  Add a context to the whitelist
+ *  @deprecated Use -addToAllowlist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (void)addToWhitelist:(NSInteger)loggingContext __attribute__((deprecated("Use -addToAllowlist: instead")));
+
+/**
+ *  Remove context from whitelist
+ *  @deprecated Use -removeFromAllowlist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (void)removeFromWhitelist:(NSInteger)loggingContext __attribute__((deprecated("Use -removeFromAllowlist: instead")));
+
+/**
+ *  Return the whitelist
+ *  @deprecated Use allowlist instead.
+ */
+@property (nonatomic, readonly, copy) NSArray<NSNumber *> *whitelist __attribute__((deprecated("Use allowlist instead")));
+
+/**
+ *  Check if a context is on the whitelist
+ *  @deprecated Use -isOnAllowlist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (BOOL)isOnWhitelist:(NSInteger)loggingContext __attribute__((deprecated("Use -isOnAllowlist: instead")));
+
+@end
+
+
+/**
+ * This class provides a log formatter that filters log statements from a logging context on the blacklist.
+ * @deprecated Use DDContextDenylistFilterLogFormatter instead.
+ **/
+__attribute__((deprecated("Use DDContextDenylistFilterLogFormatter instead")))
+typedef DDContextDenylistFilterLogFormatter DDContextBlacklistFilterLogFormatter;
+
+@interface DDContextDenylistFilterLogFormatter (Deprecated)
+
+/**
+ *  Add a context to the blacklist
+ *  @deprecated Use -addToDenylist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (void)addToBlacklist:(NSInteger)loggingContext __attribute__((deprecated("Use -addToDenylist: instead")));
+
+/**
+ *  Remove context from blacklist
+ *  @deprecated Use -removeFromDenylist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (void)removeFromBlacklist:(NSInteger)loggingContext __attribute__((deprecated("Use -removeFromDenylist: instead")));
+
+/**
+ *  Return the blacklist
+ *  @deprecated Use denylist instead.
+ */
+@property (readonly, copy) NSArray<NSNumber *> *blacklist __attribute__((deprecated("Use denylist instead")));
+
+/**
+ *  Check if a context is on the blacklist
+ *  @deprecated Use -isOnDenylist: instead.
+ *
+ *  @param loggingContext the context
+ */
+- (BOOL)isOnBlacklist:(NSInteger)loggingContext __attribute__((deprecated("Use -isOnDenylist: instead")));
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 117 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h

@@ -0,0 +1,117 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides a log formatter that filters log statements from a logging context not on the allowlist.
+ *
+ * A log formatter can be added to any logger to format and/or filter its output.
+ * You can learn more about log formatters here:
+ * Documentation/CustomFormatters.md
+ *
+ * You can learn more about logging context's here:
+ * Documentation/CustomContext.md
+ *
+ * But here's a quick overview / refresher:
+ *
+ * Every log statement has a logging context.
+ * These come from the underlying logging macros defined in DDLog.h.
+ * The default logging context is zero.
+ * You can define multiple logging context's for use in your application.
+ * For example, logically separate parts of your app each have a different logging context.
+ * Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context.
+ **/
+@interface DDContextAllowlistFilterLogFormatter : NSObject <DDLogFormatter>
+
+/**
+ *  Designated default initializer
+ */
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ *  Add a context to the allowlist
+ *
+ *  @param loggingContext the context
+ */
+- (void)addToAllowlist:(NSInteger)loggingContext;
+
+/**
+ *  Remove context from allowlist
+ *
+ *  @param loggingContext the context
+ */
+- (void)removeFromAllowlist:(NSInteger)loggingContext;
+
+/**
+ *  Return the allowlist
+ */
+@property (nonatomic, readonly, copy) NSArray<NSNumber *> *allowlist;
+
+/**
+ *  Check if a context is on the allowlist
+ *
+ *  @param loggingContext the context
+ */
+- (BOOL)isOnAllowlist:(NSInteger)loggingContext;
+
+@end
+
+
+/**
+ * This class provides a log formatter that filters log statements from a logging context on the denylist.
+ **/
+@interface DDContextDenylistFilterLogFormatter : NSObject <DDLogFormatter>
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ *  Add a context to the denylist
+ *
+ *  @param loggingContext the context
+ */
+- (void)addToDenylist:(NSInteger)loggingContext;
+
+/**
+ *  Remove context from denylist
+ *
+ *  @param loggingContext the context
+ */
+- (void)removeFromDenylist:(NSInteger)loggingContext;
+
+/**
+ *  Return the denylist
+ */
+@property (readonly, copy) NSArray<NSNumber *> *denylist;
+
+/**
+ *  Check if a context is on the denylist
+ *
+ *  @param loggingContext the context
+ */
+- (BOOL)isOnDenylist:(NSInteger)loggingContext;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 228 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h

@@ -0,0 +1,228 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  Log formatter mode
+ */
+__attribute__((deprecated("DDDispatchQueueLogFormatter is always shareable")))
+typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
+    /**
+     *  This is the default option, means the formatter can be reused between multiple loggers and therefore is thread-safe.
+     *  There is, of course, a performance cost for the thread-safety
+     */
+    DDDispatchQueueLogFormatterModeShareble = 0,
+    /**
+     *  If the formatter will only be used by a single logger, then the thread-safety can be removed
+     *  @note: there is an assert checking if the formatter is added to multiple loggers and the mode is non-shareble
+     */
+    DDDispatchQueueLogFormatterModeNonShareble,
+};
+
+/**
+ * Quality of Service names.
+ *
+ * Since macOS 10.10 and iOS 8.0, pthreads, dispatch queues and NSOperations express their
+ * scheduling priority by using an abstract classification called Quality of Service (QOS).
+ *
+ * This formatter will add a representation of this QOS in the log message by using those
+ * string constants.
+ * For example:
+ *
+ * `2011-10-17 20:21:45.435 AppName[19928:5207 (QOS:DF)] Your log message here`
+ *
+ * Where QOS is one of:
+ * `- UI = User Interactive`
+ * `- IN = User Initiated`
+ * `- DF = Default`
+ * `- UT = Utility`
+ * `- BG = Background`
+ * `- UN = Unspecified`
+ *
+ * Note: QOS will be absent in the log messages if running on OS versions that don't support it.
+ **/
+typedef NSString * DDQualityOfServiceName NS_STRING_ENUM;
+
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUserInteractive NS_SWIFT_NAME(DDQualityOfServiceName.userInteractive) API_AVAILABLE(macos(10.10), ios(8.0));
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUserInitiated NS_SWIFT_NAME(DDQualityOfServiceName.userInitiated) API_AVAILABLE(macos(10.10), ios(8.0));
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceDefault NS_SWIFT_NAME(DDQualityOfServiceName.default) API_AVAILABLE(macos(10.10), ios(8.0));
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUtility NS_SWIFT_NAME(DDQualityOfServiceName.utility) API_AVAILABLE(macos(10.10), ios(8.0));
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceBackground NS_SWIFT_NAME(DDQualityOfServiceName.background) API_AVAILABLE(macos(10.10), ios(8.0));
+FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUnspecified NS_SWIFT_NAME(DDQualityOfServiceName.unspecified) API_AVAILABLE(macos(10.10), ios(8.0));
+
+/**
+ * This class provides a log formatter that prints the dispatch_queue label instead of the mach_thread_id.
+ *
+ * A log formatter can be added to any logger to format and/or filter its output.
+ * You can learn more about log formatters here:
+ * Documentation/CustomFormatters.md
+ *
+ * A typical `NSLog` (or `DDTTYLogger`) prints detailed info as `[<process_id>:<thread_id>]`.
+ * For example:
+ *
+ * `2011-10-17 20:21:45.435 AppName[19928:5207] Your log message here`
+ *
+ * Where:
+ * `- 19928 = process id`
+ * `-  5207 = thread id (mach_thread_id printed in hex)`
+ *
+ * When using grand central dispatch (GCD), this information is less useful.
+ * This is because a single serial dispatch queue may be run on any thread from an internally managed thread pool.
+ * For example:
+ *
+ * `2011-10-17 20:32:31.111 AppName[19954:4d07] Message from my_serial_dispatch_queue`
+ * `2011-10-17 20:32:31.112 AppName[19954:5207] Message from my_serial_dispatch_queue`
+ * `2011-10-17 20:32:31.113 AppName[19954:2c55] Message from my_serial_dispatch_queue`
+ *
+ * This formatter allows you to replace the standard `[box:info]` with the dispatch_queue name.
+ * For example:
+ *
+ * `2011-10-17 20:32:31.111 AppName[img-scaling] Message from my_serial_dispatch_queue`
+ * `2011-10-17 20:32:31.112 AppName[img-scaling] Message from my_serial_dispatch_queue`
+ * `2011-10-17 20:32:31.113 AppName[img-scaling] Message from my_serial_dispatch_queue`
+ *
+ * If the dispatch_queue doesn't have a set name, then it falls back to the thread name.
+ * If the current thread doesn't have a set name, then it falls back to the mach_thread_id in hex (like normal).
+ *
+ * Note: If manually creating your own background threads (via `NSThread/alloc/init` or `NSThread/detachNeThread`),
+ * you can use `[[NSThread currentThread] setName:(NSString *)]`.
+ **/
+@interface DDDispatchQueueLogFormatter : NSObject <DDLogFormatter>
+
+/**
+ * Standard init method.
+ * Configure using properties as desired.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ *  Initializer with ability to set the queue mode
+ *
+ *  @param mode choose between DDDispatchQueueLogFormatterModeShareble and DDDispatchQueueLogFormatterModeNonShareble, depending if the formatter is shared between several loggers or not
+ */
+- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode __attribute__((deprecated("DDDispatchQueueLogFormatter is always shareable")));
+
+/**
+ * The minQueueLength restricts the minimum size of the [detail box].
+ * If the minQueueLength is set to 0, there is no restriction.
+ *
+ * For example, say a dispatch_queue has a label of "diskIO":
+ *
+ * If the minQueueLength is 0: [diskIO]
+ * If the minQueueLength is 4: [diskIO]
+ * If the minQueueLength is 5: [diskIO]
+ * If the minQueueLength is 6: [diskIO]
+ * If the minQueueLength is 7: [diskIO ]
+ * If the minQueueLength is 8: [diskIO  ]
+ *
+ * The default minQueueLength is 0 (no minimum, so [detail box] won't be padded).
+ *
+ * If you want every [detail box] to have the exact same width,
+ * set both minQueueLength and maxQueueLength to the same value.
+ **/
+@property (assign, atomic) NSUInteger minQueueLength;
+
+/**
+ * The maxQueueLength restricts the number of characters that will be inside the [detail box].
+ * If the maxQueueLength is 0, there is no restriction.
+ *
+ * For example, say a dispatch_queue has a label of "diskIO":
+ *
+ * If the maxQueueLength is 0: [diskIO]
+ * If the maxQueueLength is 4: [disk]
+ * If the maxQueueLength is 5: [diskI]
+ * If the maxQueueLength is 6: [diskIO]
+ * If the maxQueueLength is 7: [diskIO]
+ * If the maxQueueLength is 8: [diskIO]
+ *
+ * The default maxQueueLength is 0 (no maximum, so [detail box] won't be truncated).
+ *
+ * If you want every [detail box] to have the exact same width,
+ * set both minQueueLength and maxQueueLength to the same value.
+ **/
+@property (assign, atomic) NSUInteger maxQueueLength;
+
+/**
+ * Sometimes queue labels have long names like "com.apple.main-queue",
+ * but you'd prefer something shorter like simply "main".
+ *
+ * This method allows you to set such preferred replacements.
+ * The above example is set by default.
+ *
+ * To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter.
+ **/
+- (nullable NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
+
+/**
+ *  See the `replacementStringForQueueLabel:` description
+ */
+- (void)setReplacementString:(nullable NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
+
+@end
+
+/**
+ *  Category on `DDDispatchQueueLogFormatter` to make method declarations easier to extend/modify
+ **/
+@interface DDDispatchQueueLogFormatter (OverridableMethods)
+
+/**
+ *  Date formatter default configuration
+ */
+- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter;
+
+/**
+ *  Formatter method to transfrom from date to string
+ */
+- (NSString *)stringFromDate:(NSDate *)date;
+
+/**
+ *  Method to compute the queue thread label
+ */
+- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage;
+
+/**
+ *  The actual method that formats a message (transforms a `DDLogMessage` model into a printable string)
+ */
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
+
+@end
+
+#pragma mark - DDAtomicCountable
+
+__attribute__((deprecated("DDAtomicCountable is useless since DDDispatchQueueLogFormatter is always shareable now")))
+@protocol DDAtomicCountable <NSObject>
+
+- (instancetype)initWithDefaultValue:(int32_t)defaultValue;
+- (int32_t)increment;
+- (int32_t)decrement;
+- (int32_t)value;
+
+@end
+
+__attribute__((deprecated("DDAtomicCountable is deprecated")))
+@interface DDAtomicCounter: NSObject<DDAtomicCountable>
+@end
+
+NS_ASSUME_NONNULL_END

+ 27 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h

@@ -0,0 +1,27 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDFileLogger.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DDFileLogger (Buffering)
+
+- (instancetype)wrapWithBuffer;
+- (instancetype)unwrapFromBuffer;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 530 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h

@@ -0,0 +1,530 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+@class DDLogFileInfo;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides a logger to write log statements to a file.
+ **/
+
+
+// Default configuration and safety/sanity values.
+//
+// maximumFileSize         -> kDDDefaultLogMaxFileSize
+// rollingFrequency        -> kDDDefaultLogRollingFrequency
+// maximumNumberOfLogFiles -> kDDDefaultLogMaxNumLogFiles
+// logFilesDiskQuota       -> kDDDefaultLogFilesDiskQuota
+//
+// You should carefully consider the proper configuration values for your application.
+
+extern unsigned long long const kDDDefaultLogMaxFileSize;
+extern NSTimeInterval     const kDDDefaultLogRollingFrequency;
+extern NSUInteger         const kDDDefaultLogMaxNumLogFiles;
+extern unsigned long long const kDDDefaultLogFilesDiskQuota;
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  The LogFileManager protocol is designed to allow you to control all aspects of your log files.
+ *
+ *  The primary purpose of this is to allow you to do something with the log files after they have been rolled.
+ *  Perhaps you want to compress them to save disk space.
+ *  Perhaps you want to upload them to an FTP server.
+ *  Perhaps you want to run some analytics on the file.
+ *
+ *  A default LogFileManager is, of course, provided.
+ *  The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
+ *
+ *  This protocol provides various methods to fetch the list of log files.
+ *
+ *  There are two variants: sorted and unsorted.
+ *  If sorting is not necessary, the unsorted variant is obviously faster.
+ *  The sorted variant will return an array sorted by when the log files were created,
+ *  with the most recently created log file at index 0, and the oldest log file at the end of the array.
+ *
+ *  You can fetch only the log file paths (full path including name), log file names (name only),
+ *  or an array of `DDLogFileInfo` objects.
+ *  The `DDLogFileInfo` class is documented below, and provides a handy wrapper that
+ *  gives you easy access to various file attributes such as the creation date or the file size.
+ */
+@protocol DDLogFileManager <NSObject>
+@required
+
+// Public properties
+
+/**
+ * The maximum number of archived log files to keep on disk.
+ * For example, if this property is set to 3,
+ * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
+ * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
+ *
+ * You may optionally disable this option by setting it to zero.
+ **/
+@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
+
+/**
+ * The maximum space that logs can take. On rolling logfile all old log files that exceed logFilesDiskQuota will
+ * be deleted.
+ *
+ * You may optionally disable this option by setting it to zero.
+ **/
+@property (readwrite, assign, atomic) unsigned long long logFilesDiskQuota;
+
+// Public methods
+
+/**
+ *  Returns the logs directory (path)
+ */
+@property (nonatomic, readonly, copy) NSString *logsDirectory;
+
+/**
+ * Returns an array of `NSString` objects,
+ * each of which is the filePath to an existing log file on disk.
+ **/
+@property (nonatomic, readonly, strong) NSArray<NSString *> *unsortedLogFilePaths;
+
+/**
+ * Returns an array of `NSString` objects,
+ * each of which is the fileName of an existing log file on disk.
+ **/
+@property (nonatomic, readonly, strong) NSArray<NSString *> *unsortedLogFileNames;
+
+/**
+ * Returns an array of `DDLogFileInfo` objects,
+ * each representing an existing log file on disk,
+ * and containing important information about the log file such as it's modification date and size.
+ **/
+@property (nonatomic, readonly, strong) NSArray<DDLogFileInfo *> *unsortedLogFileInfos;
+
+/**
+ * Just like the `unsortedLogFilePaths` method, but sorts the array.
+ * The items in the array are sorted by creation date.
+ * The first item in the array will be the most recently created log file.
+ **/
+@property (nonatomic, readonly, strong) NSArray<NSString *> *sortedLogFilePaths;
+
+/**
+ * Just like the `unsortedLogFileNames` method, but sorts the array.
+ * The items in the array are sorted by creation date.
+ * The first item in the array will be the most recently created log file.
+ **/
+@property (nonatomic, readonly, strong) NSArray<NSString *> *sortedLogFileNames;
+
+/**
+ * Just like the `unsortedLogFileInfos` method, but sorts the array.
+ * The items in the array are sorted by creation date.
+ * The first item in the array will be the most recently created log file.
+ **/
+@property (nonatomic, readonly, strong) NSArray<DDLogFileInfo *> *sortedLogFileInfos;
+
+// Private methods (only to be used by DDFileLogger)
+
+/**
+ * Generates a new unique log file path, and creates the corresponding log file.
+ * This method is executed directly on the file logger's internal queue.
+ * The file has to exist by the time the method returns.
+ **/
+- (nullable NSString *)createNewLogFileWithError:(NSError **)error;
+
+@optional
+
+// Private methods (only to be used by DDFileLogger)
+/**
+ * Creates a new log file ignoring any errors. Deprecated in favor of `-createNewLogFileWithError:`.
+ * Will only be called if `-createNewLogFileWithError:` is not implemented.
+ **/
+- (nullable NSString *)createNewLogFile __attribute__((deprecated("Use -createNewLogFileWithError:"))) NS_SWIFT_UNAVAILABLE("Use -createNewLogFileWithError:");
+
+// Notifications from DDFileLogger
+
+/// Called when a log file was archived. Executed on global queue with default priority.
+/// @param logFilePath The path to the log file that was archived.
+/// @param wasRolled Whether or not the archiving happend after rolling the log file.
+- (void)didArchiveLogFile:(NSString *)logFilePath wasRolled:(BOOL)wasRolled NS_SWIFT_NAME(didArchiveLogFile(atPath:wasRolled:));
+
+// Deprecated APIs
+/**
+ *  Called when a log file was archived. Executed on global queue with default priority.
+ */
+- (void)didArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didArchiveLogFile(atPath:)) __attribute__((deprecated("Use -didArchiveLogFile:wasRolled:")));
+
+/**
+ *  Called when the roll action was executed and the log was archived.
+ *  Executed on global queue with default priority.
+ */
+- (void)didRollAndArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didRollAndArchiveLogFile(atPath:)) __attribute__((deprecated("Use -didArchiveLogFile:wasRolled:")));
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Default log file manager.
+ *
+ * All log files are placed inside the logsDirectory.
+ * If a specific logsDirectory isn't specified, the default directory is used.
+ * On Mac, this is in `~/Library/Logs/<Application Name>`.
+ * On iPhone, this is in `~/Library/Caches/Logs`.
+ *
+ * Log files are named `"<bundle identifier> <date> <time>.log"`
+ * Example: `com.organization.myapp 2013-12-03 17-14.log`
+ *
+ * Archived log files are automatically deleted according to the `maximumNumberOfLogFiles` property.
+ **/
+@interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
+
+/**
+ *  Default initializer
+ */
+- (instancetype)init;
+
+/**
+ *  If logDirectory is not specified, then a folder called "Logs" is created in the app's cache directory.
+ *  While running on the simulator, the "Logs" folder is located in the library temporary directory.
+ */
+- (instancetype)initWithLogsDirectory:(nullable NSString *)logsDirectory NS_DESIGNATED_INITIALIZER;
+
+#if TARGET_OS_IPHONE
+/*
+ * Calling this constructor you can override the default "automagically" chosen NSFileProtection level.
+ * Useful if you are writing a command line utility / CydiaSubstrate addon for iOS that has no NSBundle
+ * or like SpringBoard no BackgroundModes key in the NSBundle:
+ *    iPhone:~ root# cycript -p SpringBoard
+ *    cy# [NSBundle mainBundle]
+ *    #"NSBundle </System/Library/CoreServices/SpringBoard.app> (loaded)"
+ *    cy# [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
+ *    null
+ *    cy#
+ **/
+- (instancetype)initWithLogsDirectory:(nullable NSString *)logsDirectory
+           defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel;
+#endif
+
+/*
+ * Methods to override.
+ *
+ * Log files are named `"<bundle identifier> <date> <time>.log"`
+ * Example: `com.organization.myapp 2013-12-03 17-14.log`
+ *
+ * If you wish to change default filename, you can override following two methods.
+ * - `newLogFileName` method would be called on new logfile creation.
+ * - `isLogFile:` method would be called to filter log files from all other files in logsDirectory.
+ *   You have to parse given filename and return YES if it is logFile.
+ *
+ * **NOTE**
+ * `newLogFileName` returns filename. If appropriate file already exists, number would be added
+ * to filename before extension. You have to handle this case in isLogFile: method.
+ *
+ * Example:
+ * - newLogFileName returns `"com.organization.myapp 2013-12-03.log"`,
+ *   file `"com.organization.myapp 2013-12-03.log"` would be created.
+ * - after some time `"com.organization.myapp 2013-12-03.log"` is archived
+ * - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
+ *   file `"com.organization.myapp 2013-12-03 2.log"` would be created.
+ * - after some time `"com.organization.myapp 2013-12-03 1.log"` is archived
+ * - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
+ *   file `"com.organization.myapp 2013-12-03 3.log"` would be created.
+ **/
+
+/**
+ * Generates log file name with default format `"<bundle identifier> <date> <time>.log"`
+ * Example: `MobileSafari 2013-12-03 17-14.log`
+ *
+ * You can change it by overriding `newLogFileName` and `isLogFile:` methods.
+ **/
+@property (readonly, copy) NSString *newLogFileName;
+
+/**
+ * Default log file name is `"<bundle identifier> <date> <time>.log"`.
+ * Example: `MobileSafari 2013-12-03 17-14.log`
+ *
+ * You can change it by overriding `newLogFileName` and `isLogFile:` methods.
+ **/
+- (BOOL)isLogFile:(NSString *)fileName NS_SWIFT_NAME(isLogFile(withName:));
+
+/**
+ * New log files are created empty by default in `createNewLogFile:` method
+ *
+ * If you wish to specify a common file header to use in your log files,
+ * you can set the initial log file contents by overriding `logFileHeader`
+ **/
+@property (readonly, copy, nullable) NSString *logFileHeader;
+
+/* Inherited from DDLogFileManager protocol:
+
+   @property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
+   @property (readwrite, assign, atomic) NSUInteger logFilesDiskQuota;
+
+   - (NSString *)logsDirectory;
+
+   - (NSArray *)unsortedLogFilePaths;
+   - (NSArray *)unsortedLogFileNames;
+   - (NSArray *)unsortedLogFileInfos;
+
+   - (NSArray *)sortedLogFilePaths;
+   - (NSArray *)sortedLogFileNames;
+   - (NSArray *)sortedLogFileInfos;
+ */
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Most users will want file log messages to be prepended with the date and time.
+ * Rather than forcing the majority of users to write their own formatter,
+ * we will supply a logical default formatter.
+ * Users can easily replace this formatter with their own by invoking the `setLogFormatter:` method.
+ * It can also be removed by calling `setLogFormatter:`, and passing a nil parameter.
+ *
+ * In addition to the convenience of having a logical default formatter,
+ * it will also provide a template that makes it easy for developers to copy and change.
+ **/
+@interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
+
+/**
+ *  Default initializer
+ */
+- (instancetype)init;
+
+/**
+ *  Designated initializer, requires a date formatter
+ */
+- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  The standard implementation for a file logger
+ */
+@interface DDFileLogger : DDAbstractLogger <DDLogger>
+
+/**
+ *  Default initializer.
+ */
+- (instancetype)init;
+
+/**
+ *  Designated initializer, requires a `DDLogFileManager` instance.
+ *  A global queue w/ default priority is used to run callbacks.
+ *  If needed, specify queue using `initWithLogFileManager:completionQueue:`.
+ */
+- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager;
+
+/**
+ *  Designated initializer, requires a `DDLogFileManager` instance.
+ *  The completionQueue is used to execute `didArchiveLogFile:wasRolled:`,
+ *  and the callback in `rollLogFileWithCompletionBlock:`.
+ *  If nil, a global queue w/ default priority is used.
+ */
+- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager
+                       completionQueue:(nullable dispatch_queue_t)dispatchQueue NS_DESIGNATED_INITIALIZER;
+
+/**
+ *  Deprecated. Use `willLogMessage:`
+ */
+- (void)willLogMessage __attribute__((deprecated("Use -willLogMessage:"))) NS_REQUIRES_SUPER;
+
+/**
+ *  Deprecated. Use `didLogMessage:`
+ */
+- (void)didLogMessage __attribute__((deprecated("Use -didLogMessage:"))) NS_REQUIRES_SUPER;
+
+/**
+ *  Called when the logger is about to write message. Call super before your implementation.
+ */
+- (void)willLogMessage:(DDLogFileInfo *)logFileInfo NS_REQUIRES_SUPER;
+
+/**
+ *  Called when the logger wrote message. Call super after your implementation.
+ */
+- (void)didLogMessage:(DDLogFileInfo *)logFileInfo NS_REQUIRES_SUPER;
+
+/**
+ *  Writes all in-memory log data to the permanent storage. Call super before your implementation.
+ *  Don't call this method directly, instead use the `[DDLog flushLog]` to ensure all log messages are included in flush.
+ */
+- (void)flush NS_REQUIRES_SUPER;
+
+/**
+ *  Called when the logger checks archive or not current log file.
+ *  Override this method to extend standard behavior. By default returns NO.
+ *  This is executed directly on the logger's internal queue, so keep processing light!
+ */
+- (BOOL)shouldArchiveRecentLogFileInfo:(DDLogFileInfo *)recentLogFileInfo;
+
+/**
+ * Log File Rolling:
+ *
+ * `maximumFileSize`:
+ *   The approximate maximum size (in bytes) to allow log files to grow.
+ *   If a log file is larger than this value after a log statement is appended,
+ *   then the log file is rolled.
+ *
+ * `rollingFrequency`
+ *   How often to roll the log file.
+ *   The frequency is given as an `NSTimeInterval`, which is a double that specifies the interval in seconds.
+ *   Once the log file gets to be this old, it is rolled.
+ *
+ * `doNotReuseLogFiles`
+ *   When set, will always create a new log file at application launch.
+ *
+ * Both the `maximumFileSize` and the `rollingFrequency` are used to manage rolling.
+ * Whichever occurs first will cause the log file to be rolled.
+ *
+ * For example:
+ * The `rollingFrequency` is 24 hours,
+ * but the log file surpasses the `maximumFileSize` after only 20 hours.
+ * The log file will be rolled at that 20 hour mark.
+ * A new log file will be created, and the 24 hour timer will be restarted.
+ *
+ * You may optionally disable rolling due to filesize by setting `maximumFileSize` to zero.
+ * If you do so, rolling is based solely on `rollingFrequency`.
+ *
+ * You may optionally disable rolling due to time by setting `rollingFrequency` to zero (or any non-positive number).
+ * If you do so, rolling is based solely on `maximumFileSize`.
+ *
+ * If you disable both `maximumFileSize` and `rollingFrequency`, then the log file won't ever be rolled.
+ * This is strongly discouraged.
+ **/
+@property (readwrite, assign) unsigned long long maximumFileSize;
+
+/**
+ *  See description for `maximumFileSize`
+ */
+@property (readwrite, assign) NSTimeInterval rollingFrequency;
+
+/**
+ *  See description for `maximumFileSize`
+ */
+@property (readwrite, assign, atomic) BOOL doNotReuseLogFiles;
+
+/**
+ * The DDLogFileManager instance can be used to retrieve the list of log files,
+ * and configure the maximum number of archived log files to keep.
+ *
+ * @see DDLogFileManager.maximumNumberOfLogFiles
+ **/
+@property (strong, nonatomic, readonly) id <DDLogFileManager> logFileManager;
+
+/**
+ * When using a custom formatter you can set the `logMessage` method not to append
+ * `\n` character after each output. This allows for some greater flexibility with
+ * custom formatters. Default value is YES.
+ **/
+@property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
+
+/**
+ *  You can optionally force the current log file to be rolled with this method.
+ *  CompletionBlock will be called on main queue.
+ */
+- (void)rollLogFileWithCompletionBlock:(nullable void (^)(void))completionBlock
+    NS_SWIFT_NAME(rollLogFile(withCompletion:));
+
+/**
+ *  Method is deprecated.
+ *  @deprecated Use `rollLogFileWithCompletionBlock:` method instead.
+ */
+- (void)rollLogFile __attribute__((deprecated("Use -rollLogFileWithCompletionBlock:")));
+
+// Inherited from DDAbstractLogger
+
+// - (id <DDLogFormatter>)logFormatter;
+// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
+
+/**
+ * Returns the log file that should be used.
+ * If there is an existing log file that is suitable,
+ * within the constraints of `maximumFileSize` and `rollingFrequency`, then it is returned.
+ *
+ * Otherwise a new file is created and returned. If this failes, `NULL` is returned.
+ **/
+@property (nonatomic, nullable, readonly, strong) DDLogFileInfo *currentLogFileInfo;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * `DDLogFileInfo` is a simple class that provides access to various file attributes.
+ * It provides good performance as it only fetches the information if requested,
+ * and it caches the information to prevent duplicate fetches.
+ *
+ * It was designed to provide quick snapshots of the current state of log files,
+ * and to help sort log files in an array.
+ *
+ * This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
+ * This is not what the class was designed for.
+ *
+ * If you absolutely must get updated values,
+ * you can invoke the reset method which will clear the cache.
+ **/
+@interface DDLogFileInfo : NSObject
+
+@property (strong, nonatomic, readonly) NSString *filePath;
+@property (strong, nonatomic, readonly) NSString *fileName;
+
+@property (strong, nonatomic, readonly) NSDictionary<NSFileAttributeKey, id> *fileAttributes;
+
+@property (strong, nonatomic, nullable, readonly) NSDate *creationDate;
+@property (strong, nonatomic, nullable, readonly) NSDate *modificationDate;
+
+@property (nonatomic, readonly) unsigned long long fileSize;
+
+@property (nonatomic, readonly) NSTimeInterval age;
+
+@property (nonatomic, readwrite) BOOL isArchived;
+
++ (nullable instancetype)logFileWithPath:(nullable NSString *)filePath NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithFilePath:(NSString *)filePath NS_DESIGNATED_INITIALIZER;
+
+- (void)reset;
+- (void)renameFile:(NSString *)newFileName NS_SWIFT_NAME(renameFile(to:));
+
+- (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
+
+- (void)addExtendedAttributeWithName:(NSString *)attrName;
+- (void)removeExtendedAttributeWithName:(NSString *)attrName;
+
+- (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
+- (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 82 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h

@@ -0,0 +1,82 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+/**
+ * The constant/variable/method responsible for controlling the current log level.
+ **/
+#ifndef LOG_LEVEL_DEF
+    #define LOG_LEVEL_DEF ddLogLevel
+#endif
+
+/**
+ * Whether async should be used by log messages, excluding error messages that are always sent sync.
+ **/
+#ifndef LOG_ASYNC_ENABLED
+    #define LOG_ASYNC_ENABLED YES
+#endif
+
+/**
+ * This is the single macro that all other macros below compile into.
+ * This big multiline macro makes all the other macros easier to read.
+ **/
+#define LOGV_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, avalist) \
+        [DDLog log : isAsynchronous                                          \
+             level : lvl                                                     \
+              flag : flg                                                     \
+           context : ctx                                                     \
+              file : __FILE__                                                \
+          function : fnct                                                    \
+              line : __LINE__                                                \
+               tag : atag                                                    \
+            format : frmt                                                    \
+              args : avalist]
+
+/**
+ * Define version of the macro that only execute if the log level is above the threshold.
+ * The compiled versions essentially look like this:
+ *
+ * if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
+ *
+ * When LOG_LEVEL_DEF is defined as ddLogLevel.
+ *
+ * As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
+ * This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
+ *
+ * Note that when compiler optimizations are enabled (as they are for your release builds),
+ * the log messages above your logging threshold will automatically be compiled out.
+ *
+ * (If the compiler sees LOG_LEVEL_DEF/ddLogLevel declared as a constant, the compiler simply checks to see
+ *  if the 'if' statement would execute, and if not it strips it from the binary.)
+ *
+ * We also define shorthand versions for asynchronous and synchronous logging.
+ **/
+#define LOGV_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, avalist) \
+        do { if(lvl & flg) LOGV_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, avalist); } while(0)
+
+/**
+ * Ready to use log macros with no context or tag.
+ **/
+#define DDLogVError(frmt, avalist)   LOGV_MAYBE(NO,                LOG_LEVEL_DEF, DDLogFlagError,   0, nil, __PRETTY_FUNCTION__, frmt, avalist)
+#define DDLogVWarn(frmt, avalist)    LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagWarning, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
+#define DDLogVInfo(frmt, avalist)    LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo,    0, nil, __PRETTY_FUNCTION__, frmt, avalist)
+#define DDLogVDebug(frmt, avalist)   LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug,   0, nil, __PRETTY_FUNCTION__, frmt, avalist)
+#define DDLogVVerbose(frmt, avalist) LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)

+ 927 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h

@@ -0,0 +1,927 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// The Swift Package integration has no support for the legacy macros.
+#if __has_include(<CocoaLumberjack/DDLegacyMacros.h>)
+    // Enable 1.9.x legacy macros if imported directly and it's not a swift package build.
+    #ifndef DD_LEGACY_MACROS
+        #define DD_LEGACY_MACROS 1
+    #endif
+    // DD_LEGACY_MACROS is checked in the file itself
+    #import <CocoaLumberjack/DDLegacyMacros.h>
+#endif
+
+#ifndef DD_LEGACY_MESSAGE_TAG
+    #define DD_LEGACY_MESSAGE_TAG 1
+#endif
+
+// Names of loggers.
+#import <CocoaLumberjack/DDLoggerNames.h>
+
+#if OS_OBJECT_USE_OBJC
+    #define DISPATCH_QUEUE_REFERENCE_TYPE strong
+#else
+    #define DISPATCH_QUEUE_REFERENCE_TYPE assign
+#endif
+
+@class DDLogMessage;
+@class DDLoggerInformation;
+@protocol DDLogger;
+@protocol DDLogFormatter;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Define the standard options.
+ *
+ * We default to only 4 levels because it makes it easier for beginners
+ * to make the transition to a logging framework.
+ *
+ * More advanced users may choose to completely customize the levels (and level names) to suite their needs.
+ * For more information on this see the "Custom Log Levels" page:
+ * Documentation/CustomLogLevels.md
+ *
+ * Advanced users may also notice that we're using a bitmask.
+ * This is to allow for custom fine grained logging:
+ * Documentation/FineGrainedLogging.md
+ *
+ * -- Flags --
+ *
+ * Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations.
+ * For example, say you have a lot of warning log messages, and you wanted to disable them.
+ * However, you still needed to see your error and info log messages.
+ * You could accomplish that with the following:
+ *
+ * static const DDLogLevel ddLogLevel = DDLogFlagError | DDLogFlagInfo;
+ *
+ * When LOG_LEVEL_DEF is defined as ddLogLevel.
+ *
+ * Flags may also be consulted when writing custom log formatters,
+ * as the DDLogMessage class captures the individual flag that caused the log message to fire.
+ *
+ * -- Levels --
+ *
+ * Log levels are simply the proper bitmask of the flags.
+ *
+ * -- Booleans --
+ *
+ * The booleans may be used when your logging code involves more than one line.
+ * For example:
+ *
+ * if (LOG_VERBOSE) {
+ *     for (id sprocket in sprockets)
+ *         DDLogVerbose(@"sprocket: %@", [sprocket description])
+ * }
+ *
+ * -- Async --
+ *
+ * Defines the default asynchronous options.
+ * The default philosophy for asynchronous logging is very simple:
+ *
+ * Log messages with errors should be executed synchronously.
+ *     After all, an error just occurred. The application could be unstable.
+ *
+ * All other log messages, such as debug output, are executed asynchronously.
+ *     After all, if it wasn't an error, then it was just informational output,
+ *     or something the application was easily able to recover from.
+ *
+ * -- Changes --
+ *
+ * You are strongly discouraged from modifying this file.
+ * If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project.
+ * Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h
+ *
+ * For an example of customizing your logging experience, see the "Custom Log Levels" page:
+ * Documentation/CustomLogLevels.md
+ **/
+
+/**
+ *  Flags accompany each log. They are used together with levels to filter out logs.
+ */
+typedef NS_OPTIONS(NSUInteger, DDLogFlag){
+    /**
+     *  0...00001 DDLogFlagError
+     */
+    DDLogFlagError      = (1 << 0),
+
+    /**
+     *  0...00010 DDLogFlagWarning
+     */
+    DDLogFlagWarning    = (1 << 1),
+
+    /**
+     *  0...00100 DDLogFlagInfo
+     */
+    DDLogFlagInfo       = (1 << 2),
+
+    /**
+     *  0...01000 DDLogFlagDebug
+     */
+    DDLogFlagDebug      = (1 << 3),
+
+    /**
+     *  0...10000 DDLogFlagVerbose
+     */
+    DDLogFlagVerbose    = (1 << 4)
+};
+
+/**
+ *  Log levels are used to filter out logs. Used together with flags.
+ */
+typedef NS_ENUM(NSUInteger, DDLogLevel){
+    /**
+     *  No logs
+     */
+    DDLogLevelOff       = 0,
+
+    /**
+     *  Error logs only
+     */
+    DDLogLevelError     = (DDLogFlagError),
+
+    /**
+     *  Error and warning logs
+     */
+    DDLogLevelWarning   = (DDLogLevelError   | DDLogFlagWarning),
+
+    /**
+     *  Error, warning and info logs
+     */
+    DDLogLevelInfo      = (DDLogLevelWarning | DDLogFlagInfo),
+
+    /**
+     *  Error, warning, info and debug logs
+     */
+    DDLogLevelDebug     = (DDLogLevelInfo    | DDLogFlagDebug),
+
+    /**
+     *  Error, warning, info, debug and verbose logs
+     */
+    DDLogLevelVerbose   = (DDLogLevelDebug   | DDLogFlagVerbose),
+
+    /**
+     *  All logs (1...11111)
+     */
+    DDLogLevelAll       = NSUIntegerMax
+};
+
+/**
+ *  Extracts just the file name, no path or extension
+ *
+ *  @param filePath input file path
+ *  @param copy     YES if we want the result to be copied
+ *
+ *  @return the file name
+ */
+FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy);
+
+/**
+ * The THIS_FILE macro gives you an NSString of the file name.
+ * For simplicity and clarity, the file name does not include the full path or file extension.
+ *
+ * For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy"
+ **/
+#define THIS_FILE         (DDExtractFileNameWithoutExtension(__FILE__, NO))
+
+/**
+ * The THIS_METHOD macro gives you the name of the current objective-c method.
+ *
+ * For example: DDLogWarn(@"%@ - Requires non-nil strings", THIS_METHOD) -> @"setMake:model: requires non-nil strings"
+ *
+ * Note: This does NOT work in straight C functions (non objective-c).
+ * Instead you should use the predefined __FUNCTION__ macro.
+ **/
+#define THIS_METHOD       NSStringFromSelector(_cmd)
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  The main class, exposes all logging mechanisms, loggers, ...
+ *  For most of the users, this class is hidden behind the logging functions like `DDLogInfo`
+ */
+@interface DDLog : NSObject
+
+/**
+ *  Returns the singleton `DDLog`.
+ *  The instance is used by `DDLog` class methods.
+ */
+@property (class, nonatomic, strong, readonly) DDLog *sharedInstance;
+
+/**
+ * Provides access to the underlying logging queue.
+ * This may be helpful to Logger classes for things like thread synchronization.
+ **/
+@property (class, nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggingQueue;
+
+/**
+ * Logging Primitive.
+ *
+ * This method is used by the macros or logging functions.
+ * It is suggested you stick with the macros as they're easier to use.
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param level        the log level
+ *  @param flag         the log flag
+ *  @param context      the context (if any is defined)
+ *  @param file         the current file
+ *  @param function     the current function
+ *  @param line         the current code line
+ *  @param tag          potential tag
+ *  @param format       the log format
+ */
++ (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(nullable const char *)function
+       line:(NSUInteger)line
+        tag:(nullable id)tag
+     format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
+
+/**
+ * Logging Primitive.
+ *
+ * This method is used by the macros or logging functions.
+ * It is suggested you stick with the macros as they're easier to use.
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param level        the log level
+ *  @param flag         the log flag
+ *  @param context      the context (if any is defined)
+ *  @param file         the current file
+ *  @param function     the current function
+ *  @param line         the current code line
+ *  @param tag          potential tag
+ *  @param format       the log format
+ */
+- (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(nullable const char *)function
+       line:(NSUInteger)line
+        tag:(nullable id)tag
+     format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
+
+/**
+ * Logging Primitive.
+ *
+ * This method can be used if you have a prepared va_list.
+ * Similar to `log:level:flag:context:file:function:line:tag:format:...`
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param level        the log level
+ *  @param flag         the log flag
+ *  @param context      the context (if any is defined)
+ *  @param file         the current file
+ *  @param function     the current function
+ *  @param line         the current code line
+ *  @param tag          potential tag
+ *  @param format       the log format
+ *  @param argList      the arguments list as a va_list
+ */
++ (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(nullable const char *)function
+       line:(NSUInteger)line
+        tag:(nullable id)tag
+     format:(NSString *)format
+       args:(va_list)argList NS_SWIFT_NAME(log(asynchronous:level:flag:context:file:function:line:tag:format:arguments:));
+
+/**
+ * Logging Primitive.
+ *
+ * This method can be used if you have a prepared va_list.
+ * Similar to `log:level:flag:context:file:function:line:tag:format:...`
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param level        the log level
+ *  @param flag         the log flag
+ *  @param context      the context (if any is defined)
+ *  @param file         the current file
+ *  @param function     the current function
+ *  @param line         the current code line
+ *  @param tag          potential tag
+ *  @param format       the log format
+ *  @param argList      the arguments list as a va_list
+ */
+- (void)log:(BOOL)asynchronous
+      level:(DDLogLevel)level
+       flag:(DDLogFlag)flag
+    context:(NSInteger)context
+       file:(const char *)file
+   function:(nullable const char *)function
+       line:(NSUInteger)line
+        tag:(nullable id)tag
+     format:(NSString *)format
+       args:(va_list)argList NS_SWIFT_NAME(log(asynchronous:level:flag:context:file:function:line:tag:format:arguments:));
+
+/**
+ * Logging Primitive.
+ *
+ * This method can be used if you manually prepared DDLogMessage.
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param logMessage   the log message stored in a `DDLogMessage` model object
+ */
++ (void)log:(BOOL)asynchronous
+    message:(DDLogMessage *)logMessage NS_SWIFT_NAME(log(asynchronous:message:));
+
+/**
+ * Logging Primitive.
+ *
+ * This method can be used if you manually prepared DDLogMessage.
+ *
+ *  @param asynchronous YES if the logging is done async, NO if you want to force sync
+ *  @param logMessage   the log message stored in a `DDLogMessage` model object
+ */
+- (void)log:(BOOL)asynchronous
+    message:(DDLogMessage *)logMessage NS_SWIFT_NAME(log(asynchronous:message:));
+
+/**
+ * Since logging can be asynchronous, there may be times when you want to flush the logs.
+ * The framework invokes this automatically when the application quits.
+ **/
++ (void)flushLog;
+
+/**
+ * Since logging can be asynchronous, there may be times when you want to flush the logs.
+ * The framework invokes this automatically when the application quits.
+ **/
+- (void)flushLog;
+
+/**
+ * Loggers
+ *
+ * In order for your log statements to go somewhere, you should create and add a logger.
+ *
+ * You can add multiple loggers in order to direct your log statements to multiple places.
+ * And each logger can be configured separately.
+ * So you could have, for example, verbose logging to the console, but a concise log file with only warnings & errors.
+ **/
+
+/**
+ * Adds the logger to the system.
+ *
+ * This is equivalent to invoking `[DDLog addLogger:logger withLogLevel:DDLogLevelAll]`.
+ **/
++ (void)addLogger:(id <DDLogger>)logger;
+
+/**
+ * Adds the logger to the system.
+ *
+ * This is equivalent to invoking `[DDLog addLogger:logger withLogLevel:DDLogLevelAll]`.
+ **/
+- (void)addLogger:(id <DDLogger>)logger;
+
+/**
+ * Adds the logger to the system.
+ *
+ * The level that you provide here is a preemptive filter (for performance).
+ * That is, the level specified here will be used to filter out logMessages so that
+ * the logger is never even invoked for the messages.
+ *
+ * More information:
+ * When you issue a log statement, the logging framework iterates over each logger,
+ * and checks to see if it should forward the logMessage to the logger.
+ * This check is done using the level parameter passed to this method.
+ *
+ * For example:
+ *
+ * `[DDLog addLogger:consoleLogger withLogLevel:DDLogLevelVerbose];`
+ * `[DDLog addLogger:fileLogger    withLogLevel:DDLogLevelWarning];`
+ *
+ * `DDLogError(@"oh no");` => gets forwarded to consoleLogger & fileLogger
+ * `DDLogInfo(@"hi");`     => gets forwarded to consoleLogger only
+ *
+ * It is important to remember that Lumberjack uses a BITMASK.
+ * Many developers & third party frameworks may define extra log levels & flags.
+ * For example:
+ *
+ * `#define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000`
+ *
+ * So if you specify `DDLogLevelVerbose` to this method, you won't see the framework's trace messages.
+ *
+ * `(SOME_FRAMEWORK_LOG_FLAG_TRACE & DDLogLevelVerbose) => (01000000 & 00011111) => NO`
+ *
+ * Consider passing `DDLogLevelAll` to this method, which has all bits set.
+ * You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
+ * except the ones you explicitly don't want. For example, if you wanted everything except verbose & debug:
+ *
+ * `((DDLogLevelAll ^ DDLogLevelVerbose) | DDLogLevelInfo)`
+ **/
++ (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level;
+
+/**
+ * Adds the logger to the system.
+ *
+ * The level that you provide here is a preemptive filter (for performance).
+ * That is, the level specified here will be used to filter out logMessages so that
+ * the logger is never even invoked for the messages.
+ *
+ * More information:
+ * When you issue a log statement, the logging framework iterates over each logger,
+ * and checks to see if it should forward the logMessage to the logger.
+ * This check is done using the level parameter passed to this method.
+ *
+ * For example:
+ *
+ * `[DDLog addLogger:consoleLogger withLogLevel:DDLogLevelVerbose];`
+ * `[DDLog addLogger:fileLogger    withLogLevel:DDLogLevelWarning];`
+ *
+ * `DDLogError(@"oh no");` => gets forwarded to consoleLogger & fileLogger
+ * `DDLogInfo(@"hi");`     => gets forwarded to consoleLogger only
+ *
+ * It is important to remember that Lumberjack uses a BITMASK.
+ * Many developers & third party frameworks may define extra log levels & flags.
+ * For example:
+ *
+ * `#define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000`
+ *
+ * So if you specify `DDLogLevelVerbose` to this method, you won't see the framework's trace messages.
+ *
+ * `(SOME_FRAMEWORK_LOG_FLAG_TRACE & DDLogLevelVerbose) => (01000000 & 00011111) => NO`
+ *
+ * Consider passing `DDLogLevelAll` to this method, which has all bits set.
+ * You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
+ * except the ones you explicitly don't want. For example, if you wanted everything except verbose & debug:
+ *
+ * `((DDLogLevelAll ^ DDLogLevelVerbose) | DDLogLevelInfo)`
+ **/
+- (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level;
+
+/**
+ *  Remove the logger from the system
+ */
++ (void)removeLogger:(id <DDLogger>)logger;
+
+/**
+ *  Remove the logger from the system
+ */
+- (void)removeLogger:(id <DDLogger>)logger;
+
+/**
+ *  Remove all the current loggers
+ */
++ (void)removeAllLoggers;
+
+/**
+ *  Remove all the current loggers
+ */
+- (void)removeAllLoggers;
+
+/**
+ *  Return all the current loggers
+ */
+@property (class, nonatomic, copy, readonly) NSArray<id<DDLogger>> *allLoggers;
+
+/**
+ *  Return all the current loggers
+ */
+@property (nonatomic, copy, readonly) NSArray<id<DDLogger>> *allLoggers;
+
+/**
+ *  Return all the current loggers with their level (aka DDLoggerInformation).
+ */
+@property (class, nonatomic, copy, readonly) NSArray<DDLoggerInformation *> *allLoggersWithLevel;
+
+/**
+ *  Return all the current loggers with their level (aka DDLoggerInformation).
+ */
+@property (nonatomic, copy, readonly) NSArray<DDLoggerInformation *> *allLoggersWithLevel;
+
+/**
+ * Registered Dynamic Logging
+ *
+ * These methods allow you to obtain a list of classes that are using registered dynamic logging,
+ * and also provides methods to get and set their log level during run time.
+ **/
+
+/**
+ *  Returns an array with the classes that are using registered dynamic logging
+ */
+@property (class, nonatomic, copy, readonly) NSArray<Class> *registeredClasses;
+
+/**
+ *  Returns an array with the classes names that are using registered dynamic logging
+ */
+@property (class, nonatomic, copy, readonly) NSArray<NSString*> *registeredClassNames;
+
+/**
+ *  Returns the current log level for a certain class
+ *
+ *  @param aClass `Class` param
+ */
++ (DDLogLevel)levelForClass:(Class)aClass;
+
+/**
+ *  Returns the current log level for a certain class
+ *
+ *  @param aClassName string param
+ */
++ (DDLogLevel)levelForClassWithName:(NSString *)aClassName;
+
+/**
+ *  Set the log level for a certain class
+ *
+ *  @param level  the new level
+ *  @param aClass `Class` param
+ */
++ (void)setLevel:(DDLogLevel)level forClass:(Class)aClass;
+
+/**
+ *  Set the log level for a certain class
+ *
+ *  @param level      the new level
+ *  @param aClassName string param
+ */
++ (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  This protocol describes a basic logger behavior. 
+ *  Basically, it can log messages, store a logFormatter plus a bunch of optional behaviors.
+ *  (i.e. flush, get its loggerQueue, get its name, ...
+ */
+@protocol DDLogger <NSObject>
+
+/**
+ *  The log message method
+ *
+ *  @param logMessage the message (model)
+ */
+- (void)logMessage:(DDLogMessage *)logMessage NS_SWIFT_NAME(log(message:));
+
+/**
+ * Formatters may optionally be added to any logger.
+ *
+ * If no formatter is set, the logger simply logs the message as it is given in logMessage,
+ * or it may use its own built in formatting style.
+ **/
+@property (nonatomic, strong, nullable) id <DDLogFormatter> logFormatter;
+
+@optional
+
+/**
+ * Since logging is asynchronous, adding and removing loggers is also asynchronous.
+ * In other words, the loggers are added and removed at appropriate times with regards to log messages.
+ *
+ * - Loggers will not receive log messages that were executed prior to when they were added.
+ * - Loggers will not receive log messages that were executed after they were removed.
+ *
+ * These methods are executed in the logging thread/queue.
+ * This is the same thread/queue that will execute every logMessage: invocation.
+ * Loggers may use these methods for thread synchronization or other setup/teardown tasks.
+ **/
+- (void)didAddLogger;
+
+/**
+ * Since logging is asynchronous, adding and removing loggers is also asynchronous.
+ * In other words, the loggers are added and removed at appropriate times with regards to log messages.
+ *
+ * - Loggers will not receive log messages that were executed prior to when they were added.
+ * - Loggers will not receive log messages that were executed after they were removed.
+ *
+ * These methods are executed in the logging thread/queue given in parameter.
+ * This is the same thread/queue that will execute every logMessage: invocation.
+ * Loggers may use the queue parameter to set specific values on the queue with dispatch_set_specific() function.
+ **/
+- (void)didAddLoggerInQueue:(dispatch_queue_t)queue;
+
+/**
+ *  See the above description for `didAddLogger`
+ */
+- (void)willRemoveLogger;
+
+/**
+ * Some loggers may buffer IO for optimization purposes.
+ * For example, a database logger may only save occasionally as the disk IO is slow.
+ * In such loggers, this method should be implemented to flush any pending IO.
+ *
+ * This allows invocations of DDLog's flushLog method to be propagated to loggers that need it.
+ *
+ * Note that DDLog's flushLog method is invoked automatically when the application quits,
+ * and it may be also invoked manually by the developer prior to application crashes, or other such reasons.
+ **/
+- (void)flush;
+
+/**
+ * Each logger is executed concurrently with respect to the other loggers.
+ * Thus, a dedicated dispatch queue is used for each logger.
+ * Logger implementations may optionally choose to provide their own dispatch queue.
+ **/
+@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggerQueue;
+
+/**
+ * If the logger implementation does not choose to provide its own queue,
+ * one will automatically be created for it.
+ * The created queue will receive its name from this method.
+ * This may be helpful for debugging or profiling reasons.
+ **/
+@property (copy, nonatomic, readonly) DDLoggerName loggerName;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  This protocol describes the behavior of a log formatter
+ */
+@protocol DDLogFormatter <NSObject>
+@required
+
+/**
+ * Formatters may optionally be added to any logger.
+ * This allows for increased flexibility in the logging environment.
+ * For example, log messages for log files may be formatted differently than log messages for the console.
+ *
+ * For more information about formatters, see the "Custom Formatters" page:
+ * Documentation/CustomFormatters.md
+ *
+ * The formatter may also optionally filter the log message by returning nil,
+ * in which case the logger will not log the message.
+ **/
+- (nullable NSString *)formatLogMessage:(DDLogMessage *)logMessage NS_SWIFT_NAME(format(message:));
+
+@optional
+
+/**
+ * A single formatter instance can be added to multiple loggers.
+ * These methods provides hooks to notify the formatter of when it's added/removed.
+ *
+ * This is primarily for thread-safety.
+ * If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
+ * Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter with 10.0 behavior),
+ * it could possibly use these hooks to switch to thread-safe versions of the code.
+ **/
+- (void)didAddToLogger:(id <DDLogger>)logger;
+
+/**
+ * A single formatter instance can be added to multiple loggers.
+ * These methods provides hooks to notify the formatter of when it's added/removed.
+ *
+ * This is primarily for thread-safety.
+ * If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
+ * Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter with 10.0 behavior),
+ * it could possibly use these hooks to switch to thread-safe versions of the code or use dispatch_set_specific()
+.* to add its own specific values.
+ **/
+- (void)didAddToLogger:(id <DDLogger>)logger inQueue:(dispatch_queue_t)queue;
+
+/**
+ *  See the above description for `didAddToLogger:`
+ */
+- (void)willRemoveFromLogger:(id <DDLogger>)logger;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  This protocol describes a dynamic logging component
+ */
+@protocol DDRegisteredDynamicLogging
+
+/**
+ * Implement these methods to allow a file's log level to be managed from a central location.
+ *
+ * This is useful if you'd like to be able to change log levels for various parts
+ * of your code from within the running application.
+ *
+ * Imagine pulling up the settings for your application,
+ * and being able to configure the logging level on a per file basis.
+ *
+ * The implementation can be very straight-forward:
+ *
+ * ```
+ * + (int)ddLogLevel
+ * {
+ *     return ddLogLevel;
+ * }
+ *
+ * + (void)ddSetLogLevel:(DDLogLevel)level
+ * {
+ *     ddLogLevel = level;
+ * }
+ * ```
+ **/
+@property (class, nonatomic, readwrite, setter=ddSetLogLevel:) DDLogLevel ddLogLevel;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef NS_DESIGNATED_INITIALIZER
+    #define NS_DESIGNATED_INITIALIZER
+#endif
+
+/**
+ *  Log message options, allow copying certain log elements
+ */
+typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
+    /**
+     *  Use this to use a copy of the file path
+     */
+    DDLogMessageCopyFile        = 1 << 0,
+    /**
+     *  Use this to use a copy of the function name
+     */
+    DDLogMessageCopyFunction    = 1 << 1,
+    /**
+     *  Use this to use avoid a copy of the message
+     */
+    DDLogMessageDontCopyMessage = 1 << 2
+};
+
+/**
+ * The `DDLogMessage` class encapsulates information about the log message.
+ * If you write custom loggers or formatters, you will be dealing with objects of this class.
+ **/
+@interface DDLogMessage : NSObject <NSCopying>
+{
+    // Direct accessors to be used only for performance
+    @public
+    NSString *_message;
+    DDLogLevel _level;
+    DDLogFlag _flag;
+    NSInteger _context;
+    NSString *_file;
+    NSString *_fileName;
+    NSString *_function;
+    NSUInteger _line;
+    #if DD_LEGACY_MESSAGE_TAG
+    id _tag __attribute__((deprecated("Use _representedObject instead", "_representedObject")));;
+    #endif
+    id _representedObject;
+    DDLogMessageOptions _options;
+    NSDate * _timestamp;
+    NSString *_threadID;
+    NSString *_threadName;
+    NSString *_queueLabel;
+    NSUInteger _qos;
+}
+
+/**
+ *  Default `init` for empty messages.
+ */
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Standard init method for a log message object.
+ * Used by the logging primitives. (And the macros use the logging primitives.)
+ *
+ * If you find need to manually create logMessage objects, there is one thing you should be aware of:
+ *
+ * If no flags are passed, the method expects the file and function parameters to be string literals.
+ * That is, it expects the given strings to exist for the duration of the object's lifetime,
+ * and it expects the given strings to be immutable.
+ * In other words, it does not copy these strings, it simply points to them.
+ * This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters,
+ * so it makes sense to optimize and skip the unnecessary allocations.
+ * However, if you need them to be copied you may use the options parameter to specify this.
+ *
+ *  @param message   the message
+ *  @param level     the log level
+ *  @param flag      the log flag
+ *  @param context   the context (if any is defined)
+ *  @param file      the current file
+ *  @param function  the current function
+ *  @param line      the current code line
+ *  @param tag       potential tag
+ *  @param options   a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
+ *  @param timestamp the log timestamp
+ *
+ *  @return a new instance of a log message model object
+ */
+- (instancetype)initWithMessage:(NSString *)message
+                          level:(DDLogLevel)level
+                           flag:(DDLogFlag)flag
+                        context:(NSInteger)context
+                           file:(NSString *)file
+                       function:(nullable NSString *)function
+                           line:(NSUInteger)line
+                            tag:(nullable id)tag
+                        options:(DDLogMessageOptions)options
+                      timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Read-only properties
+ **/
+
+/**
+ *  The log message
+ */
+@property (readonly, nonatomic) NSString *message;
+@property (readonly, nonatomic) DDLogLevel level;
+@property (readonly, nonatomic) DDLogFlag flag;
+@property (readonly, nonatomic) NSInteger context;
+@property (readonly, nonatomic) NSString *file;
+@property (readonly, nonatomic) NSString *fileName;
+@property (readonly, nonatomic, nullable) NSString * function;
+@property (readonly, nonatomic) NSUInteger line;
+#if DD_LEGACY_MESSAGE_TAG
+@property (readonly, nonatomic, nullable) id tag __attribute__((deprecated("Use representedObject instead", "representedObject")));
+#endif
+@property (readonly, nonatomic, nullable) id representedObject;
+@property (readonly, nonatomic) DDLogMessageOptions options;
+@property (readonly, nonatomic) NSDate *timestamp;
+@property (readonly, nonatomic) NSString *threadID; // ID as it appears in NSLog calculated from the machThreadID
+@property (readonly, nonatomic, nullable) NSString *threadName;
+@property (readonly, nonatomic) NSString *queueLabel;
+@property (readonly, nonatomic) NSUInteger qos API_AVAILABLE(macos(10.10), ios(8.0));
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The `DDLogger` protocol specifies that an optional formatter can be added to a logger.
+ * Most (but not all) loggers will want to support formatters.
+ *
+ * However, writing getters and setters in a thread safe manner,
+ * while still maintaining maximum speed for the logging process, is a difficult task.
+ *
+ * To do it right, the implementation of the getter/setter has strict requirements:
+ * - Must NOT require the `logMessage:` method to acquire a lock.
+ * - Must NOT require the `logMessage:` method to access an atomic property (also a lock of sorts).
+ *
+ * To simplify things, an abstract logger is provided that implements the getter and setter.
+ *
+ * Logger implementations may simply extend this class,
+ * and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their `logMessage:` method!
+ **/
+@interface DDAbstractLogger : NSObject <DDLogger>
+{
+    // Direct accessors to be used only for performance
+    @public
+    id <DDLogFormatter> _logFormatter;
+    dispatch_queue_t _loggerQueue;
+}
+
+@property (nonatomic, strong, nullable) id <DDLogFormatter> logFormatter;
+@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE) dispatch_queue_t loggerQueue;
+
+// For thread-safety assertions
+
+/**
+ *  Return YES if the current logger uses a global queue for logging
+ */
+@property (nonatomic, readonly, getter=isOnGlobalLoggingQueue)  BOOL onGlobalLoggingQueue;
+
+/**
+ *  Return YES if the current logger uses the internal designated queue for logging
+ */
+@property (nonatomic, readonly, getter=isOnInternalLoggerQueue) BOOL onInternalLoggerQueue;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLoggerInformation : NSObject
+
+@property (nonatomic, readonly) id <DDLogger> logger;
+@property (nonatomic, readonly) DDLogLevel level;
+
++ (instancetype)informationWithLogger:(id <DDLogger>)logger
+                             andLevel:(DDLogLevel)level;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 101 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h

@@ -0,0 +1,101 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+/**
+ * The constant/variable/method responsible for controlling the current log level.
+ **/
+#ifndef LOG_LEVEL_DEF
+    #define LOG_LEVEL_DEF ddLogLevel
+#endif
+
+/**
+ * Whether async should be used by log messages, excluding error messages that are always sent sync.
+ **/
+#ifndef LOG_ASYNC_ENABLED
+    #define LOG_ASYNC_ENABLED YES
+#endif
+
+/**
+ * These are the two macros that all other macros below compile into.
+ * These big multiline macros makes all the other macros easier to read.
+ **/
+#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
+        [DDLog log : isAsynchronous                                     \
+             level : lvl                                                \
+              flag : flg                                                \
+           context : ctx                                                \
+              file : __FILE__                                           \
+          function : fnct                                               \
+              line : __LINE__                                           \
+               tag : atag                                               \
+            format : (frmt), ## __VA_ARGS__]
+
+#define LOG_MACRO_TO_DDLOG(ddlog, isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
+        [ddlog log : isAsynchronous                                     \
+             level : lvl                                                \
+              flag : flg                                                \
+           context : ctx                                                \
+              file : __FILE__                                           \
+          function : fnct                                               \
+              line : __LINE__                                           \
+               tag : atag                                               \
+            format : (frmt), ## __VA_ARGS__]
+
+/**
+ * Define version of the macro that only execute if the log level is above the threshold.
+ * The compiled versions essentially look like this:
+ *
+ * if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
+ *
+ * When LOG_LEVEL_DEF is defined as ddLogLevel.
+ *
+ * As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
+ * This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
+ *
+ * Note that when compiler optimizations are enabled (as they are for your release builds),
+ * the log messages above your logging threshold will automatically be compiled out.
+ *
+ * (If the compiler sees LOG_LEVEL_DEF/ddLogLevel declared as a constant, the compiler simply checks to see
+ *  if the 'if' statement would execute, and if not it strips it from the binary.)
+ *
+ * We also define shorthand versions for asynchronous and synchronous logging.
+ **/
+#define LOG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \
+        do { if((lvl & flg) != 0) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
+
+#define LOG_MAYBE_TO_DDLOG(ddlog, async, lvl, flg, ctx, tag, fnct, frmt, ...) \
+        do { if((lvl & flg) != 0) LOG_MACRO_TO_DDLOG(ddlog, async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
+
+/**
+ * Ready to use log macros with no context or tag.
+ **/
+#define DDLogError(frmt, ...)   LOG_MAYBE(NO,                LOG_LEVEL_DEF, DDLogFlagError,   0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogWarn(frmt, ...)    LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagWarning, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogInfo(frmt, ...)    LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo,    0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogDebug(frmt, ...)   LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug,   0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogVerbose(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+
+#define DDLogErrorToDDLog(ddlog, frmt, ...)   LOG_MAYBE_TO_DDLOG(ddlog, NO,                LOG_LEVEL_DEF, DDLogFlagError,   0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogWarnToDDLog(ddlog, frmt, ...)    LOG_MAYBE_TO_DDLOG(ddlog, LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagWarning, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogInfoToDDLog(ddlog, frmt, ...)    LOG_MAYBE_TO_DDLOG(ddlog, LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo,    0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogDebugToDDLog(ddlog, frmt, ...)   LOG_MAYBE_TO_DDLOG(ddlog, LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug,   0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
+#define DDLogVerboseToDDLog(ddlog, frmt, ...) LOG_MAYBE_TO_DDLOG(ddlog, LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)

+ 30 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h

@@ -0,0 +1,30 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NSString *DDLoggerName NS_EXTENSIBLE_STRING_ENUM;
+
+FOUNDATION_EXPORT DDLoggerName const DDLoggerNameOS NS_SWIFT_NAME(DDLoggerName.os);     // DDOSLogger
+FOUNDATION_EXPORT DDLoggerName const DDLoggerNameFile NS_SWIFT_NAME(DDLoggerName.file); // DDFileLogger
+
+FOUNDATION_EXPORT DDLoggerName const DDLoggerNameTTY NS_SWIFT_NAME(DDLoggerName.tty);   // DDTTYLogger
+
+API_DEPRECATED("Use DDOSLogger instead", macosx(10.4, 10.12), ios(2.0, 10.0), watchos(2.0, 3.0), tvos(9.0, 10.0))
+FOUNDATION_EXPORT DDLoggerName const DDLoggerNameASL NS_SWIFT_NAME(DDLoggerName.asl);   // DDASLLogger
+
+NS_ASSUME_NONNULL_END

+ 60 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h

@@ -0,0 +1,60 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This formatter can be used to chain different formatters together.
+ * The log message will processed in the order of the formatters added.
+ **/
+@interface DDMultiFormatter : NSObject <DDLogFormatter>
+
+/**
+ *  Array of chained formatters
+ */
+@property (nonatomic, readonly) NSArray<id<DDLogFormatter>> *formatters;
+
+/**
+ *  Add a new formatter
+ */
+- (void)addFormatter:(id<DDLogFormatter>)formatter NS_SWIFT_NAME(add(_:));
+
+/**
+ *  Remove a formatter
+ */
+- (void)removeFormatter:(id<DDLogFormatter>)formatter NS_SWIFT_NAME(remove(_:));
+
+/**
+ *  Remove all existing formatters
+ */
+- (void)removeAllFormatters NS_SWIFT_NAME(removeAll());
+
+/**
+ *  Check if a certain formatter is used
+ */
+- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 55 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h

@@ -0,0 +1,55 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <Foundation/Foundation.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides a logger for the Apple os_log facility.
+ **/
+API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
+@interface DDOSLogger : DDAbstractLogger <DDLogger>
+
+/**
+ *  Singleton method
+ *
+ *  @return the shared instance with OS_LOG_DEFAULT.
+ */
+@property (nonatomic, class, readonly, strong) DDOSLogger *sharedInstance;
+
+/**
+ Designated initializer
+ 
+ @param subsystem Desired subsystem in log. E.g. "org.example"
+ @param category Desired category in log. E.g. "Point of interests."
+ @return New instance of DDOSLogger.
+ 
+ @discussion This method requires either both or no parameter to be set. Much like `(String, String)?` in Swift.
+ If both parameters are nil, this method will return a logger configured with `OS_LOG_DEFAULT`.
+ If both parameters are non-nil, it will return a logger configured with `os_log_create(subsystem, category)`
+ */
+- (instancetype)initWithSubsystem:(nullable NSString *)subsystem category:(nullable NSString *)category NS_DESIGNATED_INITIALIZER;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 186 - 0
Pods/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h

@@ -0,0 +1,186 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2021, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+#import <CocoaLumberjack/DDLog.h>
+
+#define LOG_CONTEXT_ALL INT_MAX
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#if !(TARGET_OS_OSX)
+    // iOS or tvOS or watchOS
+    #import <UIKit/UIColor.h>
+    typedef UIColor DDColor;
+    static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
+#elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
+    // OS X CLI
+    #import <CocoaLumberjack/CLIColor.h>
+    typedef CLIColor DDColor;
+    static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
+#else
+    // OS X with AppKit
+    #import <AppKit/NSColor.h>
+    typedef NSColor DDColor;
+    static inline DDColor  * _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
+#endif
+#pragma clang diagnostic pop
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides a logger for Terminal output or Xcode console output,
+ * depending on where you are running your code.
+ *
+ * As described in the "Getting Started" page,
+ * the traditional NSLog() function directs it's output to two places:
+ *
+ * - Apple System Log (so it shows up in Console.app)
+ * - StdErr (if stderr is a TTY, so log statements show up in Xcode console)
+ *
+ * To duplicate NSLog() functionality you can simply add this logger and an asl logger.
+ * However, if you instead choose to use file logging (for faster performance),
+ * you may choose to use only a file logger and a tty logger.
+ **/
+@interface DDTTYLogger : DDAbstractLogger <DDLogger>
+
+/**
+ *  Singleton instance. Returns `nil` if the initialization of the DDTTYLogger fails.
+ */
+@property (nonatomic, class, readonly, strong, nullable) DDTTYLogger *sharedInstance;
+
+/* Inherited from the DDLogger protocol:
+ *
+ * Formatters may optionally be added to any logger.
+ *
+ * If no formatter is set, the logger simply logs the message as it is given in logMessage,
+ * or it may use its own built in formatting style.
+ *
+ * More information about formatters can be found here:
+ * Documentation/CustomFormatters.md
+ *
+ * The actual implementation of these methods is inherited from DDAbstractLogger.
+
+   - (id <DDLogFormatter>)logFormatter;
+   - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
+
+ */
+
+/**
+ * Want to use different colors for different log levels?
+ * Enable this property.
+ *
+ * If you run the application via the Terminal (not Xcode),
+ * the logger will map colors to xterm-256color or xterm-color (if available).
+ *
+ * Xcode does NOT natively support colors in the Xcode debugging console.
+ * You'll need to install the XcodeColors plugin to see colors in the Xcode console.
+ * https://github.com/robbiehanson/XcodeColors
+ *
+ * The default value is NO.
+ **/
+@property (readwrite, assign) BOOL colorsEnabled;
+
+/**
+ * When using a custom formatter you can set the `logMessage` method not to append
+ * `\n` character after each output. This allows for some greater flexibility with
+ * custom formatters. Default value is YES.
+ **/
+@property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
+
+/**
+ Using this initializer is not supported. Please use `DDTTYLogger.sharedInstance`.
+ **/
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * The default color set (foregroundColor, backgroundColor) is:
+ *
+ * - DDLogFlagError   = (red, nil)
+ * - DDLogFlagWarning = (orange, nil)
+ *
+ * You can customize the colors however you see fit.
+ * Please note that you are passing a flag, NOT a level.
+ *
+ * GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:DDLogFlagInfo];  // <- Good :)
+ *  BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:DDLogLevelInfo]; // <- BAD! :(
+ *
+ * DDLogFlagInfo  = 0...00100
+ * DDLogLevelInfo = 0...00111 <- Would match DDLogFlagInfo and DDLogFlagWarning and DDLogFlagError
+ *
+ * If you run the application within Xcode, then the XcodeColors plugin is required.
+ *
+ * If you run the application from a shell, then DDTTYLogger will automatically map the given color to
+ * the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.)
+ *
+ * This method invokes setForegroundColor:backgroundColor:forFlag:context: and applies it to `LOG_CONTEXT_ALL`.
+ **/
+- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forFlag:(DDLogFlag)mask;
+
+/**
+ * Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context.
+ *
+ * A logging context is often used to identify log messages coming from a 3rd party framework,
+ * although logging context's can be used for many different functions.
+ *
+ * Use LOG_CONTEXT_ALL to set the default color for all contexts that have no specific color set defined.
+ *
+ * Logging context's are explained in further detail here:
+ * Documentation/CustomContext.md
+ **/
+- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt;
+
+/**
+ * Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile.
+ * For example, you could do something like this:
+ *
+ * static NSString *const PurpleTag = @"PurpleTag";
+ *
+ * #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__)
+ *
+ * And then where you configure CocoaLumberjack:
+ *
+ * purple = DDMakeColor((64/255.0), (0/255.0), (128/255.0));
+ *
+ * or any UIColor/NSColor constructor.
+ *
+ * Note: For CLI OS X projects that don't link with AppKit use CLIColor objects instead
+ *
+ * [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag];
+ * [DDLog addLogger:[DDTTYLogger sharedInstance]];
+ *
+ * This would essentially give you a straight NSLog replacement that prints in purple:
+ *
+ * DDLogPurple(@"I'm a purple log message!");
+ **/
+- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forTag:(id <NSCopying>)tag;
+
+/**
+ * Clearing color profiles.
+ **/
+- (void)clearColorsForFlag:(DDLogFlag)mask;
+- (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context;
+- (void)clearColorsForTag:(id <NSCopying>)tag;
+- (void)clearColorsForAllFlags;
+- (void)clearColorsForAllTags;
+- (void)clearAllColors;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 63 - 0
Pods/Local Podspecs/SVGKit.podspec.json

@@ -0,0 +1,63 @@
+{
+  "name": "SVGKit",
+  "version": "3.1.0",
+  "license": "MIT",
+  "platforms": {
+    "osx": "10.9",
+    "ios": "5.0",
+    "tvos": "9.0"
+  },
+  "summary": "Display and interact with SVG Images on iOS, using native rendering (CoreAnimation).",
+  "homepage": "https://github.com/SVGKit/SVGKit",
+  "authors": {
+    "Steven Fusco": "github@stevenfusco.com",
+    "adamgit": "adam.m.s.martin@gmail.com",
+    "Kevin Stich": "stich@50cubes.com",
+    "Joshua May": "notjosh@gmail.com",
+    "Eric Man": "meric.au@gmail.com",
+    "Matt Rajca": "matt.rajca@me.com",
+    "Moritz Pfeiffer": "moritz.pfeiffer@alp-phone.ch"
+  },
+  "source": {
+    "git": "https://github.com/SVGKit/SVGKit.git",
+    "tag": "3.1.0"
+  },
+  "source_files": [
+    "Source/*.{h,m}",
+    "Source/**/*.{h,m}"
+  ],
+  "exclude_files": "Source/include/*.h",
+  "private_header_files": "Source/SVGKDefine_Private.h",
+  "ios": {
+    "private_header_files": [
+      "Source/AppKit additions/SVGKImageRep.h",
+      "Source/Exporters/SVGKExporterNSImage.h"
+    ]
+  },
+  "tvos": {
+    "private_header_files": [
+      "Source/AppKit additions/SVGKImageRep.h",
+      "Source/Exporters/SVGKExporterNSImage.h"
+    ]
+  },
+  "osx": {
+    "private_header_files": "Source/Exporters/SVGKExporterUIImage.h"
+  },
+  "libraries": "xml2",
+  "frameworks": [
+    "QuartzCore",
+    "CoreText"
+  ],
+  "dependencies": {
+    "CocoaLumberjack": [
+      "~> 3.0"
+    ]
+  },
+  "module_map": "SVGKitLibrary/SVGKit-iOS/SVGKit.modulemap",
+  "requires_arc": true,
+  "pod_target_xcconfig": {
+    "CLANG_CXX_LANGUAGE_STANDARD": "gnu++11",
+    "CLANG_CXX_LIBRARY": "libc++",
+    "HEADER_SEARCH_PATHS": "$(SDKROOT)/usr/include/libxml2"
+  }
+}

+ 27 - 2
Pods/Manifest.lock

@@ -1,4 +1,7 @@
 PODS:
+  - CocoaLumberjack (3.7.2):
+    - CocoaLumberjack/Core (= 3.7.2)
+  - CocoaLumberjack/Core (3.7.2)
   - DBDebugToolkit (0.6.0)
   - InputBarAccessoryView (4.3.1):
     - InputBarAccessoryView/Core (= 4.3.1)
@@ -17,9 +20,14 @@ PODS:
   - SDWebImage (5.9.5):
     - SDWebImage/Core (= 5.9.5)
   - SDWebImage/Core (5.9.5)
+  - SDWebImageSVGKitPlugin (1.2.0):
+    - SDWebImage/Core (~> 5.6)
+    - SVGKit (>= 2.1)
   - SDWebImageWebPCoder (0.6.1):
     - libwebp (~> 1.0)
     - SDWebImage/Core (~> 5.7)
+  - SVGKit (3.1.0):
+    - CocoaLumberjack (~> 3.0)
   - SwiftFormat/CLI (0.40.9)
   - SwiftLint (0.43.1)
   - SwiftyBeaver (1.9.4)
@@ -30,36 +38,53 @@ DEPENDENCIES:
   - ReachabilitySwift
   - SCSiriWaveformView
   - SDWebImage (~> 5.9.1)
+  - SDWebImageSVGKitPlugin
   - SDWebImageWebPCoder
+  - SVGKit (from `https://github.com/SVGKit/SVGKit.git`, branch `3.x`)
   - SwiftFormat/CLI
   - SwiftLint
   - SwiftyBeaver
 
 SPEC REPOS:
   trunk:
+    - CocoaLumberjack
     - DBDebugToolkit
     - InputBarAccessoryView
     - libwebp
     - ReachabilitySwift
     - SCSiriWaveformView
     - SDWebImage
+    - SDWebImageSVGKitPlugin
     - SDWebImageWebPCoder
     - SwiftFormat
     - SwiftLint
     - SwiftyBeaver
 
+EXTERNAL SOURCES:
+  SVGKit:
+    :branch: 3.x
+    :git: https://github.com/SVGKit/SVGKit.git
+
+CHECKOUT OPTIONS:
+  SVGKit:
+    :commit: e9bc636845fb4d6d5d8e25a9bd08e75c5cf92de9
+    :git: https://github.com/SVGKit/SVGKit.git
+
 SPEC CHECKSUMS:
+  CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da
   DBDebugToolkit: 03eb3483da4daf03b853b83ade5a8da9c8b7aff4
   InputBarAccessoryView: 58a348be7ea2736c7eec60e5c315511c2dbb39fd
   libwebp: e90b9c01d99205d03b6bb8f2c8c415e5a4ef66f0
   ReachabilitySwift: 4032e2f59586e11e3b0ebe15b167abdd587a388b
   SCSiriWaveformView: 15b9dd6f94c7536e2032b34a35c6ff74d38c7411
   SDWebImage: 0b2ba0d56479bf6a45ecddbfd5558bea93150d25
+  SDWebImageSVGKitPlugin: 06a811c05b9e839982baeb5251d15fe7a79abb82
   SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
+  SVGKit: 3c8468aab0026048532a3b27a0c81cdd939f0649
   SwiftFormat: 6b67b6e7fe73d664f0cbb4f13721f130462c86a5
   SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52
   SwiftyBeaver: 576177b2c5c94b3aedd5993914e91271a0524e88
 
-PODFILE CHECKSUM: bda1eaa52c3523837e45c30f0792783dc02e1de9
+PODFILE CHECKSUM: 632fce258414c6c6231891ec0ba5fb604075674b
 
-COCOAPODS: 1.10.1
+COCOAPODS: 1.11.2

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2359 - 1421
Pods/Pods.xcodeproj/project.pbxproj


+ 19 - 0
Pods/SDWebImageSVGKitPlugin/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2019 lizhuoli1126@126.com <lizhuoli1126@126.com>
+
+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.

+ 144 - 0
Pods/SDWebImageSVGKitPlugin/README.md

@@ -0,0 +1,144 @@
+# SDWebImageSVGKitPlugin
+
+[![CI Status](https://img.shields.io/travis/SDWebImage/SDWebImageSVGKitPlugin.svg?style=flat)](https://travis-ci.org/SDWebImage/SDWebImageSVGKitPlugin)
+[![Version](https://img.shields.io/cocoapods/v/SDWebImageSVGKitPlugin.svg?style=flat)](https://cocoapods.org/pods/SDWebImageSVGKitPlugin)
+[![License](https://img.shields.io/cocoapods/l/SDWebImageSVGKitPlugin.svg?style=flat)](https://cocoapods.org/pods/SDWebImageSVGKitPlugin)
+[![Platform](https://img.shields.io/cocoapods/p/SDWebImageSVGKitPlugin.svg?style=flat)](https://cocoapods.org/pods/SDWebImageSVGKitPlugin)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/SDWebImage/SDWebImageSVGKitPlugin)
+
+
+## What's for
+SDWebImageSVGKitPlugin is a SVG coder plugin for [SDWebImage](https://github.com/rs/SDWebImage/) framework, which provide the image loading support for [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) using [SVGKit](https://github.com/SVGKit/SVGKit) SVG engine.
+
+Note: iOS 13+/macOS 10.15+ supports native SVG rendering (called [Symbol Image](https://developer.apple.com/documentation/uikit/uiimage/configuring_and_displaying_symbol_images_in_your_ui/)), with system framework to load SVG. Check [SDWebImageSVGCoder](https://github.com/SDWebImage/SDWebImageSVGCoder) for more information.
+
+## Example
+
+To run the example project, clone the repo, and run `pod install` from the Example directory first.
+
+You can modify the code or use some other SVG files to check the compatibility.
+
+## Requirements
+
++ iOS 8+
++ tvOS 9+
++ macOS 10.10+
+
+## Installation
+
+#### CocoaPods
+
+SDWebImageSVGKitPlugin is available through [CocoaPods](https://cocoapods.org). To install
+it, simply add the following line to your Podfile:
+
+```ruby
+pod 'SDWebImageSVGKitPlugin'
+```
+
+Note: The [SVGKit](https://github.com/SVGKit/SVGKit) dependency seems does not follow sem-version and didn't not release versions frequently. You can manually specify a branch or CID dependency for it. Like below:
+
+```
+pod 'SVGKit', :git => 'https://github.com/SVGKit/SVGKit.git', :branch => '3.x'
+```
+
+#### Carthage
+
+SDWebImageSVGKitPlugin is available through [Carthage](https://github.com/Carthage/Carthage).
+
+```
+github "SDWebImage/SDWebImageSVGKitPlugin"
+```
+
+## Usage
+
+### Use UIImageView (render SVG as bitmap image)
+
+To use SVG coder, you should firstly add the `SDImageSVGKCoder` to the coders manager. Then you can call the View Category method to start load SVG images.
+
+Because SVG is a [vector image](https://en.wikipedia.org/wiki/Vector_graphics) format, which means it does not have a fixed bitmap size. However, `UIImage` or `CGImage` are all [bitmap image](https://en.wikipedia.org/wiki/Raster_graphics). For `UIImageView`, we will only parse SVG with a fixed image size (from the SVG viewPort information). But we also support you to specify a desired size during image loading using `SDWebImageContextThumbnailPixelSize` context option. And you can specify whether or not to keep aspect ratio during scale using `SDWebImageContextImagePreserveAspectRatio` context option.
+
++ Objective-C
+
+```objectivec
+SDImageSVGKCoder *svgCoder = [SDImageSVGKCoder sharedCoder];
+[[SDImageCodersManager sharedManager] addCoder:svgCoder];
+UIImageView *imageView;
+// this arg is optional, if don't provide, use the viewport size instead
+CGSize svgImageSize = CGSizeMake(100, 100);
+[imageView sd_setImageWithURL:url placeholderImage:nil options:0 context:@{SDWebImageContextThumbnailPixelSize : @(svgImageSize)];
+```
+
++ Swift
+
+```swift
+let svgCoder = SDImageSVGKCoder.shared
+SDImageCodersManager.shared.addCoder(svgCoder)
+let imageView: UIImageView
+imageView.sd_setImage(with: url)
+// this arg is optional, if don't provide, use the viewport size instead
+let svgImageSize = CGSize(width: 100, height: 100)
+imageView.sd_setImage(with: url, placeholderImage: nil, options: [], context: [.imageThumbnailPixelSize : svgImageSize])
+```
+
+### Use SVGKImageView (render SVG as vector image)
+
+[SVGKit](https://github.com/SVGKit/SVGKit) also provide some built-in image view class for vector image loading (scale to any size without losing detail). The `SVGKLayeredImageView` && `SVGKFastImageView` are the subclass of `SVGKImageView` base class. We supports these image view class as well. You can just use the same API like normal `UIImageView`.
+
+For the documentation about `SVGKLayeredImageView`, `SVGKFastImageView` or `SVGKImageView`, check [SVGKit](https://github.com/SVGKit/SVGKit) repo for more information.
+
+**Note**: If you only use these image view class and don't use SVG on `UIImageView`, you don't need to register the SVG coder to coders manager. These image view loading was using the [Custom Image Class](https://github.com/rs/SDWebImage/wiki/Advanced-Usage#customization) feature of SDWebImage.
+
+**Attention**: These built-in image view class does not works well on `UIView.contentMode` property, you need to re-scale the layer tree after image was loaded. We provide a simple out-of-box solution to support it. Set the `sd_adjustContentMode` property to `YES` then all things done.
+
++ Objective-C
+
+```objectivec
+SVGKImageView *imageView; // can be either `SVGKLayeredImageView` or `SVGKFastImageView`
+imageView.contentMode = UIViewContentModeScaleAspectFill;
+imageView.sd_adjustContentMode = YES; // make `contentMode` works
+[imageView sd_setImageWithURL:url];
+```
+
++ Swift:
+
+```swift
+let imageView: SVGKImageView // can be either `SVGKLayeredImageView` or `SVGKFastImageView`
+imageView.contentMode = .aspectFill
+imageView.sd_adjustContentMode = true // make `contentMode` works
+imageView.sd_setImage(with: url)
+```
+
+## Export SVG data
+
+`SDWebImageSVGKitPlugin` provide an easy way to export the SVG image generated from framework, to the original SVG data.
+
++ Objective-C
+
+```objectivec
+UIImage *image; // Image generated from SDWebImage framework, actually a `SDSVGKImage` instance.
+NSData *imageData = [image sd_imageDataAsFormat:SDImageFormatSVG];
+```
+
++ Swift
+
+```swift
+let image: UIImage // Image generated from SDWebImage framework, actually a `SDSVGKImage` instance.
+let imageData = image.sd_imageData(as: .SVG)
+```
+
+## Screenshot
+
+<img src="https://raw.githubusercontent.com/SDWebImage/SDWebImageSVGKitPlugin/master/Example/Screenshot/SVGDemo.png" width="300" />
+<img src="https://raw.githubusercontent.com/SDWebImage/SDWebImageSVGKitPlugin/master/Example/Screenshot/SVGDemo-macOS.png" width="600" />
+
+These SVG images are from [wikimedia](https://commons.wikimedia.org/wiki/Main_Page), you can try the demo with your own SVG image as well.
+
+## Author
+
+DreamPiggy
+
+## License
+
+SDWebImageSVGKitPlugin is available under the MIT license. See the LICENSE file for more info.
+
+

+ 21 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDImageSVGKCoder.h

@@ -0,0 +1,21 @@
+//
+//  SDImageSVGKCoder.h
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/9/27.
+//
+
+#import <SDWebImage/SDWebImage.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ SDImageSVGKCoder is a SVG image coder, which use the SVGKit for SVG rendering. It support a subset of SVG 1.1 spec. For anything related to SVG rendering, please report issue to https://github.com/SVGKit/SVGKit/.
+ */
+@interface SDImageSVGKCoder : NSObject <SDImageCoder>
+
+@property (nonatomic, class, readonly) SDImageSVGKCoder *sharedCoder;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 130 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDImageSVGKCoder.m

@@ -0,0 +1,130 @@
+//
+//  SDImageSVGKCoder.m
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/9/27.
+//
+
+#import "SDImageSVGKCoder.h"
+#import "SDSVGKImage.h"
+#import "SDWebImageSVGKitDefine.h"
+#import <SVGKit/SVGKit.h>
+
+#define kSVGTagEnd @"</svg>"
+
+@implementation SDImageSVGKCoder
+
++ (SDImageSVGKCoder *)sharedCoder {
+    static dispatch_once_t onceToken;
+    static SDImageSVGKCoder *coder;
+    dispatch_once(&onceToken, ^{
+        coder = [[SDImageSVGKCoder alloc] init];
+    });
+    return coder;
+}
+
+#pragma mark - Decode
+
+- (BOOL)canDecodeFromData:(NSData *)data {
+    return [self.class isSVGFormatForData:data];
+}
+
+- (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)options {
+    if (!data) {
+        return nil;
+    }
+    // Parse SVG
+    SVGKImage *svgImage = [[SVGKImage alloc] initWithData:data];
+    if (!svgImage) {
+        return nil;
+    }
+    
+    CGSize imageSize = CGSizeZero;
+    BOOL preserveAspectRatio = YES;
+    
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    // Parse args
+    SDWebImageContext *context = options[SDImageCoderWebImageContext];
+    if (context[SDWebImageContextSVGKImageSize]) {
+        NSValue *sizeValue = context[SDWebImageContextSVGKImageSize];
+#if SD_UIKIT
+        imageSize = sizeValue.CGSizeValue;
+#else
+        imageSize = sizeValue.sizeValue;
+#endif
+    } else if (options[SDImageCoderDecodeThumbnailPixelSize]) {
+        NSValue *sizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
+#if SD_UIKIT
+        imageSize = sizeValue.CGSizeValue;
+#else
+        imageSize = sizeValue.sizeValue;
+#endif
+    }
+    if (context[SDWebImageContextSVGKImagePreserveAspectRatio]) {
+        preserveAspectRatio = [context[SDWebImageContextSVGKImagePreserveAspectRatio] boolValue];
+    } else if (options[SDImageCoderDecodePreserveAspectRatio]) {
+        preserveAspectRatio = [options[SDImageCoderDecodePreserveAspectRatio] boolValue];
+    }
+#pragma clang diagnostic pop
+    
+    if (!CGSizeEqualToSize(imageSize, CGSizeZero)) {
+        if (preserveAspectRatio) {
+            [svgImage scaleToFitInside:imageSize];
+        } else {
+            svgImage.size = imageSize;
+        }
+    }
+    
+    UIImage *image = svgImage.UIImage;
+    if (!image) {
+        return nil;
+    }
+    
+    // SVG is vector image, so no need scale factor
+    image.sd_imageFormat = SDImageFormatSVG;
+    
+    return image;
+}
+
+#pragma mark - Encode
+
+- (BOOL)canEncodeToFormat:(SDImageFormat)format {
+    return format == SDImageFormatSVG;
+}
+
+- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format options:(SDImageCoderOptions *)options {
+    // Only support SVGKImage wrapper
+    if (![image isKindOfClass:SDSVGKImage.class]) {
+        return nil;
+    }
+    SVGKImage *svgImage = ((SDSVGKImage *)image).SVGKImage;
+    if (!svgImage) {
+        return nil;
+    }
+    SVGKSource *source = svgImage.source;
+    // Should be NSData type source
+    if (![source isKindOfClass:SVGKSourceNSData.class]) {
+        return nil;
+    }
+    return ((SVGKSourceNSData *)source).rawData;
+}
+
+#pragma mark - Helper
+
++ (BOOL)isSVGFormatForData:(NSData *)data {
+    if (!data) {
+        return NO;
+    }
+    if (data.length <= 100) {
+        return NO;
+    }
+    // Check end with SVG tag
+    NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(data.length - 100, 100)] encoding:NSASCIIStringEncoding];
+    if (![testString containsString:kSVGTagEnd]) {
+        return NO;
+    }
+    return YES;
+}
+
+@end

+ 37 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDSVGKImage.h

@@ -0,0 +1,37 @@
+//
+//  SDSVGKImage.h
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/10.
+//
+
+#import <SDWebImage/SDWebImage.h>
+#import <SVGKit/SVGKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDSVGKImage : UIImage <SDAnimatedImage>
+
+@property (nonatomic, strong, nullable, readonly) SVGKImage *SVGKImage;
+
+/**
+ Create the wrapper with specify `SVGKImage` instance. The instance should be nonnull.
+ This is a convenience method for some use cases, for example, create a placeholder with `SVGKImage`.
+ 
+ @param image The `SVGKImage` instance
+ @return An initialized object
+ */
+- (nonnull instancetype)initWithSVGKImage:(nonnull SVGKImage *)image;
+
+// This class override these methods from UIImage
+// You should use these methods to create a new SVG image. Use other methods just call super instead.
++ (nullable instancetype)imageWithContentsOfFile:(nonnull NSString *)path;
++ (nullable instancetype)imageWithData:(nonnull NSData *)data;
++ (nullable instancetype)imageWithData:(nonnull NSData *)data scale:(CGFloat)scale;
+- (nullable instancetype)initWithContentsOfFile:(nonnull NSString *)path;
+- (nullable instancetype)initWithData:(nonnull NSData *)data;
+- (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 146 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDSVGKImage.m

@@ -0,0 +1,146 @@
+//
+//  SDSVGKImage.m
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/10.
+//
+
+#import "SDSVGKImage.h"
+#import "SDWebImageSVGKitDefine.h"
+
+@interface SDSVGKImage ()
+
+@property (nonatomic, strong, nullable) SVGKImage *SVGKImage;
+
+@end
+
+@implementation SDSVGKImage
+
+- (instancetype)initWithSVGKImage:(SVGKImage *)image {
+    NSParameterAssert(image);
+    UIImage *posterImage = image.UIImage;
+#if SD_UIKIT
+    UIImageOrientation imageOrientation = posterImage.imageOrientation;
+#else
+    CGImagePropertyOrientation imageOrientation = kCGImagePropertyOrientationUp;
+#endif
+    self = [super initWithCGImage:posterImage.CGImage scale:posterImage.scale orientation:imageOrientation];
+    if (self) {
+        self.SVGKImage = image;
+    }
+    return self;
+}
+
++ (instancetype)imageWithContentsOfFile:(NSString *)path {
+    return [[self alloc] initWithContentsOfFile:path];
+}
+
++ (instancetype)imageWithData:(NSData *)data {
+    return [[self alloc] initWithData:data];
+}
+
++ (instancetype)imageWithData:(NSData *)data scale:(CGFloat)scale {
+    return [[self alloc] initWithData:data scale:scale];
+}
+
+- (instancetype)initWithData:(NSData *)data {
+    return [self initWithData:data scale:1];
+}
+
+- (instancetype)initWithContentsOfFile:(NSString *)path {
+    NSData *data = [NSData dataWithContentsOfFile:path];
+    return [self initWithData:data];
+}
+
+- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale {
+    return [self initWithData:data scale:scale options:nil];
+}
+
+- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale options:(SDImageCoderOptions *)options {
+    SVGKImage *svgImage = [[SVGKImage alloc] initWithData:data];
+    if (!svgImage) {
+        return nil;
+    }
+    CGSize imageSize = CGSizeZero;
+    
+    // Check specified image size
+    SDWebImageContext *context = options[SDImageCoderWebImageContext];
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    if (context[SDWebImageContextSVGKImageSize]) {
+        NSValue *sizeValue = context[SDWebImageContextSVGKImageSize];
+#if SD_UIKIT
+        imageSize = sizeValue.CGSizeValue;
+#else
+        imageSize = sizeValue.sizeValue;
+#endif
+    } else if (options[SDImageCoderDecodeThumbnailPixelSize]) {
+        NSValue *sizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
+#if SD_UIKIT
+        imageSize = sizeValue.CGSizeValue;
+#else
+        imageSize = sizeValue.sizeValue;
+#endif
+    }
+    if (!CGSizeEqualToSize(imageSize, CGSizeZero)) {
+        svgImage.size = imageSize;
+    }
+    return [self initWithSVGKImage:svgImage];
+}
+
+- (instancetype)initWithAnimatedCoder:(id<SDAnimatedImageCoder>)animatedCoder scale:(CGFloat)scale {
+    // Does not support progressive load for SVG images at all
+    return nil;
+}
+
+#pragma mark - SDAnimatedImageProvider
+
+- (nullable NSData *)animatedImageData {
+    return nil;
+}
+
+- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
+    return 0;
+}
+
+- (nullable UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
+    return nil;
+}
+
+- (NSUInteger)animatedImageFrameCount {
+    return 0;
+}
+
+- (NSUInteger)animatedImageLoopCount {
+    return 0;
+}
+
+@end
+
+@implementation SDSVGKImage (Metadata)
+
+- (BOOL)sd_isAnimated {
+    return NO;
+}
+
+- (NSUInteger)sd_imageLoopCount {
+    return self.animatedImageLoopCount;
+}
+
+- (void)setSd_imageLoopCount:(NSUInteger)sd_imageLoopCount {
+    return;
+}
+
+- (SDImageFormat)sd_imageFormat {
+    return SDImageFormatSVG;
+}
+
+- (void)setSd_imageFormat:(SDImageFormat)sd_imageFormat {
+    return;
+}
+
+- (BOOL)sd_isVector {
+    return YES;
+}
+
+@end

+ 34 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDWebImageSVGKitDefine.h

@@ -0,0 +1,34 @@
+//
+//  SDWebImageSVGKitDefine.h
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/11.
+//
+
+#import <SDWebImage/SDWebImage.h>
+
+@class SVGKImage;
+
+#if SD_UIKIT
+/**
+ Adjust `SVGKImage`'s viewPort && viewBox to match the specify `contentMode` of view size.
+ @note Though this util method can be used outside this framework. For simple SVG image loading, it's recommaned to use `sd_adjustContentMode` property on `SVGKImageView+WebCache`.
+
+ @param image `SVGKImage` instance, should not be nil.
+ @param contentMode The contentMode to be applied. All possible contentMode are supported.
+ @param viewSize Target view size, typically specify the `view.bounds.size`.
+ */
+FOUNDATION_EXPORT void SDAdjustSVGContentMode(SVGKImage * __nonnull image, UIViewContentMode contentMode, CGSize viewSize);
+#endif
+
+/**
+ A CGSize raw value which specify the desired SVG image size during image loading. Because vector image like SVG format, may not contains a fixed size, or you want to get a larger size bitmap representation UIImage. (NSValue)
+ If you don't provide this value, use viewBox size of SVG for default value;
+ */
+FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextSVGKImageSize __attribute__((deprecated("Use the new context option (for WebCache category), or coder option (for SDImageCoder protocol) instead", "SDWebImageContextImageThumbnailPixelSize")));
+
+/**
+ A BOOL value which specify the whether SVG image should keep aspect ratio during image loading. Because when you specify image size via `SDWebImageContextSVGKImageSize`, we need to know whether to keep aspect ratio or not when image size aspect ratio is not equal to SVG viewBox size aspect ratio. (NSNumber)
+ If you don't provide this value, use YES for default value.
+ */
+FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextSVGKImagePreserveAspectRatio __attribute__((deprecated("Use the new context option (for WebCache category), or coder option (for SDImageCoder protocol) instead", "SDWebImageContextImagePreserveAspectRatio")));

+ 156 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SDWebImageSVGKitDefine.m

@@ -0,0 +1,156 @@
+//
+//  SDWebImageSVGKitDefine.m
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/11.
+//
+
+#import "SDWebImageSVGKitDefine.h"
+#import <SVGKit/SVGKit.h>
+
+#if SD_UIKIT
+void SDAdjustSVGContentMode(SVGKImage * svgImage, UIViewContentMode contentMode, CGSize viewSize) {
+    NSCParameterAssert(svgImage);
+    if (!svgImage.hasSize) {
+        // `SVGKImage` does not has size, specify the content size, earily return
+        svgImage.size = viewSize;
+        return;
+    }
+    CGSize imageSize = svgImage.size;
+    if (imageSize.height == 0 || viewSize.height == 0) {
+        return;
+    }
+    CGFloat wScale = viewSize.width / imageSize.width;
+    CGFloat hScale = viewSize.height / imageSize.height;
+    CGFloat imageAspect = imageSize.width / imageSize.height;
+    CGFloat viewAspect = viewSize.width / viewSize.height;
+    CGFloat xPosition;
+    CGFloat yPosition;
+    
+    // Geometry calculation
+    switch (contentMode) {
+        case UIViewContentModeScaleToFill: {
+            svgImage.size = viewSize;
+        }
+            break;
+        case UIViewContentModeScaleAspectFit: {
+            CGFloat scale;
+            if (imageAspect > viewAspect) {
+                // scale width
+                scale = wScale;
+            } else {
+                // scale height
+                scale = hScale;
+            }
+            CGSize targetSize = CGSizeApplyAffineTransform(imageSize, CGAffineTransformMakeScale(scale, scale));
+            if (imageAspect > viewAspect) {
+                // need center y as well
+                xPosition = 0;
+                yPosition = ABS(targetSize.height - viewSize.height) / 2;
+            } else {
+                // need center x as well
+                xPosition = ABS(targetSize.width - viewSize.width) / 2;
+                yPosition = 0;
+            }
+            svgImage.size = targetSize;
+            svgImage.DOMTree.viewport = SVGRectMake(xPosition, yPosition, targetSize.width, targetSize.height);
+            // masksToBounds to clip the sublayer which beyond the viewport to match `UIImageView` behavior
+            svgImage.CALayerTree.masksToBounds = YES;
+        }
+            break;
+        case UIViewContentModeScaleAspectFill: {
+            CGFloat scale;
+            if (imageAspect < viewAspect) {
+                // scale width
+                scale = wScale;
+            } else {
+                // scale height
+                scale = hScale;
+            }
+            CGSize targetSize = CGSizeApplyAffineTransform(imageSize, CGAffineTransformMakeScale(scale, scale));
+            if (imageAspect < viewAspect) {
+                // need center y as well
+                xPosition = 0;
+                yPosition = ABS(targetSize.height - viewSize.height) / 2;
+            } else {
+                // need center x as well
+                xPosition = ABS(targetSize.width - viewSize.width) / 2;
+                yPosition = 0;
+            }
+            svgImage.size = targetSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeTop: {
+            xPosition = (imageSize.width - viewSize.width) / 2;
+            yPosition = 0;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeTopLeft: {
+            xPosition = 0;
+            yPosition = 0;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeTopRight: {
+            xPosition = imageSize.width - viewSize.width;
+            yPosition = 0;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeCenter: {
+            xPosition = (imageSize.width - viewSize.width) / 2;
+            yPosition = (imageSize.height - viewSize.height) / 2;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeLeft: {
+            xPosition = 0;
+            yPosition = (imageSize.height - viewSize.height) / 2;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeRight: {
+            xPosition = imageSize.width - viewSize.width;
+            yPosition = (imageSize.height - viewSize.height) / 2;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeBottom: {
+            xPosition = (imageSize.width - viewSize.width) / 2;
+            yPosition = imageSize.height - viewSize.height;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeBottomLeft: {
+            xPosition = 0;
+            yPosition = imageSize.height - viewSize.height;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeBottomRight: {
+            xPosition = imageSize.width - viewSize.width;
+            yPosition = imageSize.height - viewSize.height;
+            svgImage.size = imageSize;
+            svgImage.DOMTree.viewBox = SVGRectMake(xPosition, yPosition, imageSize.width, imageSize.height);
+        }
+            break;
+        case UIViewContentModeRedraw: {
+            svgImage.CALayerTree.needsDisplayOnBoundsChange = YES;
+        }
+            break;
+    }
+}
+#endif
+
+SDWebImageContextOption _Nonnull const SDWebImageContextSVGKImageSize = @"svgkImageSize";
+SDWebImageContextOption _Nonnull const SDWebImageContextSVGKImagePreserveAspectRatio = @"svgkImagePreserveAspectRatio";

+ 169 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SVGKImageView+WebCache.h

@@ -0,0 +1,169 @@
+//
+//  SVGKImageView+WebCache.h
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/10.
+//
+
+#import <SVGKit/SVGKit.h>
+#import "SDWebImageSVGKitDefine.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SVGKImageView (WebCache)
+
+/**
+ * Unlike `UIView`, `SVGKImageView`'s subclass does not follows `UIView.contentMode` property, the position of SVG vector image is controled by `SVGKImage` instance with SVG's viewport && viewbox information. You can adjust it after image was loading, however it's not so convenient.
+ * So we provide a simple solution. If you want the same behavior like UIImageView to use `contentMode` property to position SVG images, set this value to YES and we'll resize the SVG image to correspond `contentMode` of `SVGKImageView` during image loading.
+ */
+@property (nonatomic, assign) BOOL sd_adjustContentMode API_UNAVAILABLE(macos);
+
+/**
+ * Set the imageView `image` with an `url`.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url The url for the image.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
+
+/**
+ * Set the imageView `image` with an `url` and a placeholder.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url         The url for the image.
+ * @param placeholder The image to be set initially, until the image request finishes.
+ * @see sd_setImageWithURL:placeholderImage:options:
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder and custom options.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url         The url for the image.
+ * @param placeholder The image to be set initially, until the image request finishes.
+ * @param options     The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder, custom options and context.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url         The url for the image.
+ * @param placeholder The image to be set initially, until the image request finishes.
+ * @param options     The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ * @param context     A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options
+                   context:(nullable SDWebImageContext *)context;
+
+/**
+ * Set the imageView `image` with an `url`.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+                 completed:(nullable SDExternalCompletionBlock)completedBlock;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param placeholder    The image to be set initially, until the image request finishes.
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                 completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder and custom options.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param placeholder    The image to be set initially, until the image request finishes.
+ * @param options        The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options
+                 completed:(nullable SDExternalCompletionBlock)completedBlock;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder and custom options.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param placeholder    The image to be set initially, until the image request finishes.
+ * @param options        The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ * @param progressBlock  A block called while image is downloading
+ *                       @note the progress block is executed on a background queue
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options
+                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
+                 completed:(nullable SDExternalCompletionBlock)completedBlock;
+
+/**
+ * Set the imageView `image` with an `url`, placeholder, custom options and context.
+ *
+ * The download is asynchronous and cached.
+ *
+ * @param url            The url for the image.
+ * @param placeholder    The image to be set initially, until the image request finishes.
+ * @param options        The options to use when downloading the image. @see SDWebImageOptions for the possible values.
+ * @param context        A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
+ * @param progressBlock  A block called while image is downloading
+ *                       @note the progress block is executed on a background queue
+ * @param completedBlock A block called when operation has been completed. This block has no return value
+ *                       and takes the requested UIImage as first parameter. In case of error the image parameter
+ *                       is nil and the second parameter may contain an NSError. The third parameter is a Boolean
+ *                       indicating if the image was retrieved from the local cache or from the network.
+ *                       The fourth parameter is the original image url.
+ */
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options
+                   context:(nullable SDWebImageContext *)context
+                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
+                 completed:(nullable SDExternalCompletionBlock)completedBlock;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 102 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Classes/SVGKImageView+WebCache.m

@@ -0,0 +1,102 @@
+//
+//  SVGKImageView+WebCache.m
+//  SDWebImageSVGPlugin
+//
+//  Created by DreamPiggy on 2018/10/10.
+//
+
+#import "SVGKImageView+WebCache.h"
+#import "SDSVGKImage.h"
+#import <objc/runtime.h>
+
+@implementation SVGKImageView (WebCache)
+
+- (void)setSd_adjustContentMode:(BOOL)sd_adjustContentMode {
+    objc_setAssociatedObject(self, @selector(sd_adjustContentMode), @(sd_adjustContentMode), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (BOOL)sd_adjustContentMode {
+    NSNumber *value = objc_getAssociatedObject(self, @selector(sd_adjustContentMode));
+    return value.boolValue;
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url {
+    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:context progress:nil completed:nil];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
+    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock {
+    [self sd_setImageWithURL:url placeholderImage:placeholder options:options context:nil progress:progressBlock completed:completedBlock];
+}
+
+- (void)sd_setImageWithURL:(nullable NSURL *)url
+          placeholderImage:(nullable UIImage *)placeholder
+                   options:(SDWebImageOptions)options
+                   context:(nullable SDWebImageContext *)context
+                  progress:(nullable SDImageLoaderProgressBlock)progressBlock
+                 completed:(nullable SDExternalCompletionBlock)completedBlock {
+    Class imageClass = [SDSVGKImage class];
+    SDWebImageMutableContext *mutableContext;
+    if (context) {
+        mutableContext = [context mutableCopy];
+    } else {
+        mutableContext = [NSMutableDictionary dictionary];
+    }
+    mutableContext[SDWebImageContextAnimatedImageClass] = imageClass;
+    __weak typeof(self)weakSelf = self;
+    [self sd_internalSetImageWithURL:url
+                    placeholderImage:placeholder
+                             options:options
+                             context:mutableContext
+                       setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
+                           __strong typeof(weakSelf)strongSelf = weakSelf;
+                           if (!strongSelf) {
+                               return;
+                           }
+                           SVGKImage *svgImage;
+                           if ([image isKindOfClass:[SDSVGKImage class]]) {
+                               svgImage = ((SDSVGKImage *)image).SVGKImage;
+                           }
+                           if (svgImage) {
+#if SD_UIKIT
+                               if (self.sd_adjustContentMode) {
+                                   SDAdjustSVGContentMode(svgImage, strongSelf.contentMode, strongSelf.bounds.size);
+                               }
+#endif
+                               strongSelf.image = svgImage;
+                           } else {
+                               strongSelf.image = nil;
+                           }
+                       }
+                            progress:progressBlock
+                           completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
+                               if (completedBlock) {
+                                   completedBlock(image, error, cacheType, imageURL);
+                               }
+                           }];
+}
+
+@end

+ 19 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Module/SDWebImageSVGKitPlugin.h

@@ -0,0 +1,19 @@
+#ifdef __OBJC__
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import <SDWebImageSVGKitPlugin/SDImageSVGKCoder.h>
+#import <SDWebImageSVGKitPlugin/SDSVGKImage.h>
+#import <SDWebImageSVGKitPlugin/SVGKImageView+WebCache.h>
+#import <SDWebImageSVGKitPlugin/SDWebImageSVGKitDefine.h>
+
+FOUNDATION_EXPORT double SDWebImageSVGKitPluginVersionNumber;
+FOUNDATION_EXPORT const unsigned char SDWebImageSVGKitPluginVersionString[];
+

+ 6 - 0
Pods/SDWebImageSVGKitPlugin/SDWebImageSVGKitPlugin/Module/SDWebImageSVGKitPlugin.modulemap

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

+ 8 - 0
Pods/SVGKit/LICENSE

@@ -0,0 +1,8 @@
+Copyright (c) 2010-2011 Matt Rajca, 2011-2015 various authors
+Parts Copyright (c) Tipbit Inc
+
+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.

+ 122 - 0
Pods/SVGKit/README.mdown

@@ -0,0 +1,122 @@
+# SVGKit
+
+SVGKit is a Cocoa framework for rendering SVG files natively: it's fast and powerful. Some additional info and links [are on the wiki](https://github.com/SVGKit/SVGKit/wiki)
+
+
+## Versions:
+
+Latest info at: https://github.com/SVGKit/SVGKit/wiki/Versions
+
+  - v3.x = current "in development" branch with latest changes, fixes, features
+    - NB: this is now automatically selected in GitHub as the "default" branch when you visit SVGKit's project page
+        
+# Getting Started
+
+## Run the Demo - iOS
+
+1. Open up "Demo-iOS.xcodeproj", and run it (on simulator or device). Try different SVG's. Zoom, pan, and (with the Monkey only:) hit the "Animate" button. Tap the images to see bounding-boxes / hit dectection (might need you to hit the Debug button first)
+1. If you have ANY problems building the library and embedding it in your app, compare your build settings to the Demo-iOS build settings - if something's different, it's probably the problem.
+
+# Installation (add SVGKit to your app)
+
+You have 3 main options for installing SVGKit:
+
+1. Drag/drop the .framework file into your project <-- PREFERRED / RECOMMENDED
+1. Use CocoaPods
+1. Use Carthage
+1. Drag/drop the static library into your project, and configure the build settings in your project
+
+NB: the "static library" is our backwards-compatible, manual install that always works if you have problems with CocoaPods/Carthage/Framework.
+
+## OPTION 1: Use the Framework - drag, drop, done!
+
+1. Open "SVGKit-iOS.xcodeproj"
+1. Build the project once
+1. In Project Navigator, expand the Products folder
+1. Drag/drop SVGKit.framework into your app-project
+
+NB: Frameworks are the preferred way to use libraries in Xcode. But this is a new feature, it might have bugs. If you have ANY problems, please create an Issue, but also try the Static Library method below
+
+You MAY also need to manually add the following Apple Frameworks to your app:
+  - CoreText
+  - CoreImage
+  - libxml2.dylib
+  - QuartzCore
+  - CoreGraphics
+  - UIKit
+
+You MAY also need to add the following 3rd party Frameworks to your app (included with SVGKit):
+  - CocoaLumberjack (in the SVGKit-iOS project, select the "3rd-party-frameworks" folder, and drag/drop whichever versions you need: iOS, tvOS, etc)
+    Remember to embed the library too (should show under Target -> General -> Embedded Binaries)
+    
+## OPTION 2: Using CocoaPods
+[CocoaPods](https://github.com/CocoaPods/CocoaPods) is a dependency manager for CocoaTouch. To do CocoaPods, adding the following in your podfile:
+
+```
+pod 'SVGKit'
+```
+
+It is also recommended that you setup your podfile to get SVGKit from the latest branch (October 2018: currently 3.x).
+
+```
+pod 'SVGKit', :git => 'https://github.com/SVGKit/SVGKit.git', :branch => '3.x'
+```
+
+## OPTION 3: Using Carthage
+[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager for Cocoa. To use Carthage, adding the following in your Cartfile:
+
+```
+github "SVGKit/SVGKit"
+```
+
+It is also recommended that you setup your Cartfile to get SVGKit from the current version (October 2018: 3.x branch).
+
+```
+github "SVGKit/SVGKit" "3.x"
+```
+
+## OPTION 4: Build the static library
+
+We have a build script that automatically builds ALL versions of the library at once, and ships them as a single file: http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4/3647187#3647187
+
+It's all setup already, all you need to do is:
+
+1. Open "SVGKit-iOS.xcodeproj", switch to "SVGKit-iOS" target and Build it (cmd-B)
+3. in left navbar, scroll to bottom, and open the "Products" section
+4. right click the library ("libSVGKitBLAHBLAH.a") and select "show in finder"
+5. GO UP ONE FOLDER
+6. select the "Debug-universal" (or Release-universal if you were building in Release mode) folder
+7. Drag/drop the .a file and the "usr" folder into your project (select the "Copy files" checkbox)
+8. In Build Settings, select "Other Linker Flags" and add "-ObjC"
+9. Edit your build settings and set "C/C++ Compiler Version" = "LLVM Compiler 2.0"
+10. Add ALL the frameworks and 3rd party libraries listed below (go to "Build Phases", and "Link Binary with Libraries"):
+  - CoreText
+  - CoreImage
+  - libxml2.dylib
+  - QuartzCore
+  - CoreGraphics
+  - UIKit
+
+
+Everything else is automatic.
+
+## Usage - OS X
+
+Added in version 2.1.0 / Autumn 2018: support for macOS.
+
+You can use nearly the same API like iOS. Including `SVGKFastImageView`, `SVGKLayeredImageView`, and you can use `SVGKImage.NSImage` to export SVG layer to bitmap image.
+
+We also provide a macOS demo for SVGKit. To run the Demo, open `Demo-OSX.xcodeproj` and built to run. You can browser the different SVG files using those two different type of view to check the compatibility.
+
+## Recipes for using the library
+
+Here are some old posts (some of these APIs have changed slightly since they were written) on using SVGKit, with advice on which methods to use and why:
+
+  - GETTING STARTED, plus NEW FEATURES:  http://t-machine.org/index.php/2012/12/31/svgkit-2013-usage/
+  - QUICK RECIPES for common uses:  http://t-machine.org/index.php/2013/01/02/svgkit-2013-recipes/
+     - additiona: How to scale an SVG image on screen: http://t-machine.org/index.php/2013/04/14/svgkit-scaling-svg-images/
+  - CONTRIBUTING to the project, CORE ARCHITECTURE: http://t-machine.org/index.php/2012/12/31/svgkit-2013-development/
+ 
+  - (November 2013): New (experimental) feature - writing SVG's out to disk, preserving any changes you made programmatically: http://t-machine.org/index.php/2013/11/17/svgkit-programmatic-editing-of-svg-files-on-ios/
+
+  - Using SVGFastImageView in SwiftUI: https://gist.github.com/ozgurshn/c816a035ff4722dc26650f4a4ed40105

+ 6 - 0
Pods/SVGKit/SVGKitLibrary/SVGKit-iOS/SVGKit.modulemap

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

+ 43 - 0
Pods/SVGKit/Source/AppKit additions/SVGKImageRep.h

@@ -0,0 +1,43 @@
+//
+//  SVGKitImageRep.h
+//  SVGKit
+//
+//  Created by C.W. Betts on 12/5/12.
+//
+//
+
+#import "SVGKDefine.h"
+
+#if SVGKIT_MAC
+#import <Cocoa/Cocoa.h>
+@class SVGKImage;
+@class SVGKSource;
+
+@interface SVGKImageRep : NSImageRep
+@property (nonatomic, strong, readonly) SVGKImage *image;
+
+//Function used by NSImageRep to init.
++ (instancetype)imageRepWithData:(NSData *)d;
++ (instancetype)imageRepWithContentsOfFile:(NSString *)filename;
++ (instancetype)imageRepWithContentsOfURL:(NSURL *)url;
++ (instancetype)imageRepWithSVGImage:(SVGKImage*)theImage;
++ (instancetype)imageRepWithSVGSource:(SVGKSource*)theSource;
+
+- (instancetype)initWithData:(NSData *)theData;
+- (instancetype)initWithContentsOfURL:(NSURL *)theURL;
+- (instancetype)initWithContentsOfFile:(NSString *)thePath;
+- (instancetype)initWithSVGString:(NSString *)theString;
+- (instancetype)initWithSVGImage:(SVGKImage*)theImage;
+- (instancetype)initWithSVGSource:(SVGKSource*)theSource;
+
+- (NSData *)TIFFRepresentation;
+- (NSData *)TIFFRepresentationWithSize:(NSSize)theSize;
+- (NSData *)TIFFRepresentationUsingCompression:(NSTIFFCompression)comp factor:(float)factor;
+- (NSData *)TIFFRepresentationUsingCompression:(NSTIFFCompression)comp factor:(float)factor size:(NSSize)asize;
+
++ (void)loadSVGKImageRep;
++ (void)unloadSVGKImageRep;
+
+@end
+
+#endif /* SVGKIT_MAC */

+ 293 - 0
Pods/SVGKit/Source/AppKit additions/SVGKImageRep.m

@@ -0,0 +1,293 @@
+//
+//  SVGKitImageRep.m
+//  SVGKit
+//
+//  Created by C.W. Betts on 12/5/12.
+//
+//
+
+#import "SVGKit.h"
+
+#if SVGKIT_MAC
+#import "SVGKSourceNSData.h"
+#import "SVGKSourceLocalFile.h"
+#import "SVGKSourceURL.h"
+#import "SVGKSourceString.h"
+#import "SVGKImageRep.h"
+#import "SVGKImage+CGContext.h"
+#include <tgmath.h>
+
+@interface SVGKImageRep ()
+@property (nonatomic, strong, readwrite, setter = setTheSVG:) SVGKImage *image;
+@property (nonatomic, assign) BOOL antiAlias;
+@property (nonatomic, assign) CGFloat curveFlatness;
+@property (nonatomic, assign) CGInterpolationQuality interpolationQuality;
+@end
+
+@implementation SVGKImageRep
+
+- (NSData *)TIFFRepresentationWithSize:(NSSize)theSize
+{
+	self.image.size = theSize;
+    NSImageRep *imageRep = self.image.NSImage.representations.firstObject;
+    if (![imageRep isKindOfClass:[NSBitmapImageRep class]]) {
+        return nil;
+    }
+	return [(NSBitmapImageRep *)imageRep TIFFRepresentation];
+}
+
+- (NSData *)TIFFRepresentation
+{
+	return [self TIFFRepresentationWithSize:self.size];
+}
+
+- (NSData *)TIFFRepresentationUsingCompression:(NSTIFFCompression)comp factor:(float)factor
+{
+	return [self TIFFRepresentationUsingCompression:comp factor:factor size:self.size];
+}
+
+- (NSData *)TIFFRepresentationUsingCompression:(NSTIFFCompression)comp factor:(float)factor size:(NSSize)asize
+{
+	self.image.size = asize;
+    NSImageRep *imageRep = self.image.NSImage.representations.firstObject;
+    if (![imageRep isKindOfClass:[NSBitmapImageRep class]]) {
+        return nil;
+    }
+    return [(NSBitmapImageRep *)imageRep TIFFRepresentation];
+}
+
++ (NSArray<NSString *> *)imageUnfilteredTypes {
+    static NSArray *UTItypes;
+    if (UTItypes == nil) {
+        UTItypes = @[@"public.svg-image"];
+    }
+    return UTItypes;
+}
+
++ (BOOL)canInitWithData:(NSData *)d
+{
+	SVGKParseResult *parseResult;
+	@autoreleasepool {
+		parseResult = [SVGKParser parseSourceUsingDefaultSVGKParser:[SVGKSourceNSData sourceFromData:d URLForRelativeLinks:nil]];
+	}
+	if (parseResult == nil) {
+		return NO;
+	}
+	if (!parseResult.parsedDocument) {
+		return NO;
+	}
+	return YES;
+}
+
++ (instancetype)imageRepWithData:(NSData *)d
+{
+	return [[self alloc] initWithData:d];
+}
+
++ (instancetype)imageRepWithContentsOfFile:(NSString *)filename
+{
+	return [[self alloc] initWithContentsOfFile:filename];
+}
+
++ (instancetype)imageRepWithContentsOfURL:(NSURL *)url
+{
+	return [[self alloc] initWithContentsOfURL:url];
+}
+
++ (instancetype)imageRepWithSVGSource:(SVGKSource*)theSource
+{
+	return [[self alloc] initWithSVGSource:theSource];
+}
+
++ (instancetype)imageRepWithSVGImage:(SVGKImage*)theImage
+{
+	return [[self alloc] initWithSVGImage:theImage];
+}
+
++ (void)load
+{
+	[self loadSVGKImageRep];
+}
+
+- (instancetype)initWithData:(NSData *)theData
+{
+	return [self initWithSVGImage:[[SVGKImage alloc] initWithData:theData] copy:NO];
+}
+
+- (instancetype)initWithContentsOfURL:(NSURL *)theURL
+{
+	return [self initWithSVGImage:[SVGKImage imageWithContentsOfURL:theURL] copy:NO];
+}
+
+- (instancetype)initWithContentsOfFile:(NSString *)thePath
+{
+	return [self initWithSVGImage:[[SVGKImage alloc] initWithContentsOfFile:thePath] copy:NO];
+}
+
+- (instancetype)initWithSVGString:(NSString *)theString
+{
+    return [self initWithSVGSource:[SVGKSourceString sourceFromContentsOfString:theString]];
+}
+
+- (instancetype)initWithSVGSource:(SVGKSource*)theSource
+{
+	return [self initWithSVGImage:[[SVGKImage alloc] initWithSource:theSource] copy:NO];
+}
+
+- (instancetype)initWithSVGImage:(SVGKImage*)theImage copy:(BOOL)copyImage
+{
+	if (self = [super init]) {
+		if (theImage == nil) {
+			return nil;
+		}
+		SVGKImage *tmpImage;
+		if (copyImage) {
+			tmpImage = [theImage copy];
+			if (tmpImage) {
+				theImage = tmpImage;
+			}
+		}
+		
+		self.image = theImage;
+		
+		if (![self.image hasSize]) {
+			self.image.size = CGSizeMake(32, 32);
+		}
+		
+        self.colorSpaceName = NSCalibratedRGBColorSpace;
+        self.alpha = YES;
+        self.opaque = NO;
+		[self setSize:self.image.size sizeImage:NO];
+		self.interpolationQuality = kCGInterpolationDefault;
+		self.antiAlias = YES;
+		self.curveFlatness = 1.0;
+        
+        // Setting these to zero will tell NSImage that this image is resolution-independant.
+        self.pixelsHigh = 0;
+        self.pixelsWide = 0;
+        self.bitsPerSample = 0;
+	}
+	return self;
+}
+
+- (instancetype)init
+{
+    NSAssert(NO, @"init not supported, use initWithData:");
+    
+    return nil;
+}
+
+- (void)setSize:(NSSize)aSize
+{
+	[self setSize:aSize sizeImage:YES];
+}
+
+- (void)setSize:(NSSize)aSize sizeImage:(BOOL)theSize
+{
+    [super setSize:aSize];
+    if (theSize) {
+        self.image.size = aSize;
+    }
+}
+
++ (void)loadSVGKImageRep
+{
+	[NSImageRep registerImageRepClass:[SVGKImageRep class]];
+}
+
++ (void)unloadSVGKImageRep
+{
+	[NSImageRep unregisterImageRepClass:[SVGKImageRep class]];
+}
+
+- (instancetype)initWithSVGImage:(SVGKImage*)theImage
+{
+	//Copy over the image, just in case
+	return [self initWithSVGImage:theImage copy:YES];
+}
+
+- (BOOL)drawInRect:(NSRect)rect
+{
+	NSSize scaledSize = rect.size;
+	if (!CGSizeEqualToSize(self.image.size, scaledSize)) {
+		//For when we're at the full size.
+		if (CGSizeEqualToSize(self.size, scaledSize)) {
+			return [super drawInRect:rect];
+		} else {
+			[self.image scaleToFitInside:scaledSize];
+		}
+	} else if (CGSizeEqualToSize(self.size, scaledSize) &&
+			   CGSizeEqualToSize(self.image.size, self.size)) {
+		return [super drawInRect:rect];
+	}
+
+	CGContextRef imRepCtx = [[NSGraphicsContext currentContext] graphicsPort];
+	CGLayerRef layerRef = CGLayerCreateWithContext(imRepCtx, rect.size, NULL);
+	if (!layerRef) {
+		return NO;
+	}
+
+	CGContextRef layerCont = CGLayerGetContext(layerRef);
+	CGContextSaveGState(layerCont);
+    [self.image renderToContext:layerCont antiAliased:_antiAlias curveFlatnessFactor:_curveFlatness interpolationQuality:_interpolationQuality flipYaxis:YES];
+	CGContextRestoreGState(layerCont);
+
+	CGContextDrawLayerInRect(imRepCtx, rect, layerRef);
+	CGLayerRelease(layerRef);
+	
+	return YES;
+}
+
+- (BOOL)draw
+{
+	//Just in case someone resized the image rep.
+	NSSize scaledSize = self.size;
+	if (!CGSizeEqualToSize(self.image.size, scaledSize)) {
+		self.image.size = scaledSize;
+	}
+
+	CGContextRef imRepCtx = [[NSGraphicsContext currentContext] graphicsPort];
+	CGLayerRef layerRef = CGLayerCreateWithContext(imRepCtx, scaledSize, NULL);
+	if (!layerRef) {
+		return NO;
+	}
+
+	CGContextRef layerCont = CGLayerGetContext(layerRef);
+	CGContextSaveGState(layerCont);
+    [self.image renderToContext:layerCont antiAliased:_antiAlias curveFlatnessFactor:_curveFlatness interpolationQuality:_interpolationQuality flipYaxis:YES];
+	CGContextRestoreGState(layerCont);
+
+	CGContextDrawLayerAtPoint(imRepCtx, CGPointZero, layerRef);
+	CGLayerRelease(layerRef);
+	
+	return YES;
+}
+
+#pragma mark - Inherited protocols
+
+#pragma mark NSCopying
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    SVGKImageRep *toRet = [[SVGKImageRep alloc] initWithSVGImage:self.image];
+    
+    return toRet;
+}
+
+#pragma mark NSCoding
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+    if (self = [super initWithCoder:coder]) {
+        
+    }
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+    [super encodeWithCoder:aCoder];
+}
+
+@end
+
+#endif /* SVGKIT_MAC */

+ 42 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/AppleSucksDOMImplementation.h

@@ -0,0 +1,42 @@
+/*
+ PLEASE NOTE: Apple has made a PRIVATE implementation of this class, and because of their
+ stupid App Store rules they ban everyone else in the world from using a class with the
+ same name. Instead of making the class public, they have stolen it out of the global
+ namespace. This is the wrong thing to do, but we are required to rename our classes
+ because of this.
+ 
+ SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490
+ 
+ interface DOMImplementation {
+ boolean            hasFeature(in DOMString feature, 
+ in DOMString version);
+ // Introduced in DOM Level 2:
+ DocumentType       createDocumentType(in DOMString qualifiedName, 
+ in DOMString publicId, 
+ in DOMString systemId)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ Document           createDocument(in DOMString namespaceURI, 
+ in DOMString qualifiedName, 
+ in DocumentType doctype)
+ raises(DOMException);
+ };
+*/
+
+#import <Foundation/Foundation.h>
+
+#import "DocumentType.h"
+
+@interface AppleSucksDOMImplementation : NSObject
+
+-(BOOL) hasFeature:(NSString*) feature version:(NSString*) version;
+
+// Introduced in DOM Level 2:
+-(DocumentType*) createDocumentType:(NSString*) qualifiedName publicId:(NSString*) publicId systemId:(NSString*) systemId;
+
+// Introduced in DOM Level 2:
+-(Document*) createDocument:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName doctype:(DocumentType*) doctype;
+
+@end

+ 33 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/AppleSucksDOMImplementation.m

@@ -0,0 +1,33 @@
+
+#import "AppleSucksDOMImplementation.h"
+
+@implementation AppleSucksDOMImplementation
+
+-(BOOL) hasFeature:(NSString*) feature version:(NSString*) version
+{
+	NSAssert( FALSE, @"Apple has made a private class with the same name as this class - we have to rename it because ObjectiveC sucks, and Apple used a foolish name, so we CANNOT adhere to the spec now" );
+	
+	NSAssert( FALSE, @"Not implemented yet" );
+	return FALSE;
+}
+
+// Introduced in DOM Level 2:
+-(DocumentType*) createDocumentType:(NSString*) qualifiedName publicId:(NSString*) publicId systemId:(NSString*) systemId
+{
+	NSAssert( FALSE, @"Apple has made a private class with the same name as this class - we have to rename it because ObjectiveC sucks, and Apple used a foolish name, so we CANNOT adhere to the spec now" );
+	
+	NSAssert( FALSE, @"Not implemented yet" );
+	return nil;
+}
+
+
+// Introduced in DOM Level 2:
+-(Document*) createDocument:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName doctype:(DocumentType*) doctype
+{
+	NSAssert( FALSE, @"Apple has made a private class with the same name as this class - we have to rename it because ObjectiveC sucks, and Apple used a foolish name, so we CANNOT adhere to the spec now" );
+	
+	NSAssert( FALSE, @"Not implemented yet" );
+	return nil;
+}
+
+@end

+ 37 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Attr.h

@@ -0,0 +1,37 @@
+/*
+ SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-637646024
+ 
+ interface Attr : Node {
+ readonly attribute DOMString        name;
+ readonly attribute boolean          specified;
+ attribute DOMString        value;
+ // raises(DOMException) on setting
+ 
+ // Introduced in DOM Level 2:
+ readonly attribute Element          ownerElement;
+ };
+*/
+#import <Foundation/Foundation.h>
+
+/** objc won't allow this: @class Node;*/
+#import "Node.h"
+@class Element;
+
+@interface Attr : Node
+
+/*! NB: The official DOM spec FAILS TO SPECIFY what the value of "name" is */
+@property(nonatomic,strong,readonly) NSString* name;
+@property(nonatomic,readonly) BOOL specified;
+@property(nonatomic,strong,readonly) NSString* value;
+
+// Introduced in DOM Level 2:
+@property(nonatomic,strong,readonly) Element* ownerElement;
+
+#pragma mark - ObjC methods
+
+- (id)initWithName:(NSString*) n value:(NSString*) v;
+- (id)initWithNamespace:(NSString*) ns qualifiedName:(NSString*) qn value:(NSString*) v;
+
+@end

+ 54 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Attr.m

@@ -0,0 +1,54 @@
+//
+//  Attr.m
+//  SVGKit
+//
+//  Created by adam on 22/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "Attr.h"
+
+#import "Node+Mutable.h"
+
+@interface Attr()
+ @property(nonatomic,strong,readwrite) NSString* name;
+ @property(nonatomic,readwrite) BOOL specified;
+ @property(nonatomic,strong,readwrite) NSString* value;
+ 
+ // Introduced in DOM Level 2:
+ @property(nonatomic,strong,readwrite) Element* ownerElement;
+@end
+
+@implementation Attr
+
+@synthesize name;
+@synthesize specified;
+@synthesize value;
+
+// Introduced in DOM Level 2:
+@synthesize ownerElement;
+
+- (id)initWithName:(NSString*) n value:(NSString*) v
+{
+    self = [super initType:DOMNodeType_ATTRIBUTE_NODE name:n value:v];
+    if (self)
+	{
+		self.name = n;
+		self.value = v;
+    }
+    return self;
+}
+
+- (id)initWithNamespace:(NSString*) ns qualifiedName:(NSString*) qn value:(NSString *)v
+{
+    self = [super initType:DOMNodeType_ATTRIBUTE_NODE name:qn value:v inNamespace:ns];
+	if (self)
+	{
+		self.name = qn;
+		self.value = v;
+    }
+    return self;
+}
+
+
+@end

+ 18 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CDATASection.h

@@ -0,0 +1,18 @@
+/*
+ From SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-667469212
+ 
+ interface CDATASection : Text {
+ };
+ */
+#import <Foundation/Foundation.h>
+
+@class Text;
+#import "Text.h"
+
+@interface CDATASection : Text
+
+- (id)initWithValue:(NSString*) v;
+
+@end

+ 20 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CDATASection.m

@@ -0,0 +1,20 @@
+//
+//  CDATASection.m
+//  SVGKit
+//
+//  Created by adam on 22/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "CDATASection.h"
+
+@implementation CDATASection
+
+- (id)initWithValue:(NSString*) v
+{
+    self = [super initType:DOMNodeType_CDATA_SECTION_NODE name:@"#cdata-section" value:v];
+    if (self) {
+    }
+    return self;
+}
+@end

+ 102 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue.h

@@ -0,0 +1,102 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSPrimitiveValue
+ 
+ interface CSSPrimitiveValue : CSSValue {
+ 
+ // UnitTypes
+ const unsigned short      CSS_UNKNOWN                    = 0;
+ const unsigned short      CSS_NUMBER                     = 1;
+ const unsigned short      CSS_PERCENTAGE                 = 2;
+ const unsigned short      CSS_EMS                        = 3;
+ const unsigned short      CSS_EXS                        = 4;
+ const unsigned short      CSS_PX                         = 5;
+ const unsigned short      CSS_CM                         = 6;
+ const unsigned short      CSS_MM                         = 7;
+ const unsigned short      CSS_IN                         = 8;
+ const unsigned short      CSS_PT                         = 9;
+ const unsigned short      CSS_PC                         = 10;
+ const unsigned short      CSS_DEG                        = 11;
+ const unsigned short      CSS_RAD                        = 12;
+ const unsigned short      CSS_GRAD                       = 13;
+ const unsigned short      CSS_MS                         = 14;
+ const unsigned short      CSS_S                          = 15;
+ const unsigned short      CSS_HZ                         = 16;
+ const unsigned short      CSS_KHZ                        = 17;
+ const unsigned short      CSS_DIMENSION                  = 18;
+ const unsigned short      CSS_STRING                     = 19;
+ const unsigned short      CSS_URI                        = 20;
+ const unsigned short      CSS_IDENT                      = 21;
+ const unsigned short      CSS_ATTR                       = 22;
+ const unsigned short      CSS_COUNTER                    = 23;
+ const unsigned short      CSS_RECT                       = 24;
+ const unsigned short      CSS_RGBCOLOR                   = 25;
+ 
+ readonly attribute unsigned short   primitiveType;
+ void               setFloatValue(in unsigned short unitType,
+ in float floatValue)
+ raises(DOMException);
+ float              getFloatValue(in unsigned short unitType)
+ raises(DOMException);
+ void               setStringValue(in unsigned short stringType,
+ in DOMString stringValue)
+ raises(DOMException);
+ DOMString          getStringValue()
+ raises(DOMException);
+ Counter            getCounterValue()
+ raises(DOMException);
+ Rect               getRectValue()
+ raises(DOMException);
+ RGBColor           getRGBColorValue()
+ raises(DOMException);
+ */
+#import "CSSValue.h"
+
+typedef enum CSSPrimitiveType
+{
+	CSS_UNKNOWN                    = 0,
+	CSS_NUMBER                     = 1,
+	CSS_PERCENTAGE                 = 2,
+	CSS_EMS                        = 3,
+	CSS_EXS                        = 4,
+	CSS_PX                         = 5,
+	CSS_CM                         = 6,
+	CSS_MM                         = 7,
+	CSS_IN                         = 8,
+	CSS_PT                         = 9,
+	CSS_PC                         = 10,
+	CSS_DEG                        = 11,
+	CSS_RAD                        = 12,
+	CSS_GRAD                       = 13,
+	CSS_MS                         = 14,
+	CSS_S                          = 15,
+	CSS_HZ                         = 16,
+	CSS_KHZ                        = 17,
+	CSS_DIMENSION                  = 18,
+	CSS_STRING                     = 19,
+	CSS_URI                        = 20,
+	CSS_IDENT                      = 21,
+	CSS_ATTR                       = 22,
+	CSS_COUNTER                    = 23,
+	CSS_RECT                       = 24,
+	CSS_RGBCOLOR                   = 25
+} CSSPrimitiveType;
+
+@interface CSSPrimitiveValue : CSSValue
+
+@property(nonatomic) CSSPrimitiveType primitiveType;
+
+-(void) setFloatValue:(CSSPrimitiveType) unitType floatValue:(float) floatValue;
+
+-(float) getFloatValue:(CSSPrimitiveType) unitType;
+
+-(void) setStringValue:(CSSPrimitiveType) stringType stringValue:(NSString*) stringValue;
+
+-(NSString*) getStringValue;
+
+-(/* FIXME: have to add this type: Counter*/ void) getCounterValue;
+
+-(/* FIXME: have to add this type: Rect*/ void) getRectValue;
+
+-(/* FIXME: have to add this type: RGBColor*/ void) getRGBColorValue;
+
+@end

+ 296 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue.m

@@ -0,0 +1,296 @@
+#import "CSSPrimitiveValue.h"
+#import "CSSValue_ForSubclasses.h"
+#import "CSSPrimitiveValue_ConfigurablePixelsPerInch.h"
+
+#import "DOMGlobalSettings.h"
+
+#define INCHES_PER_CENTIMETRE ( 0.393700787f )
+#define INCHES_PER_MILLIMETER ( 0.0393701f )
+
+@interface CSSPrimitiveValue()
+
+@property(nonatomic) float internalValue;
+@property(nonatomic,strong) NSString* internalString;
+
+@end
+
+@implementation CSSPrimitiveValue
+
+@synthesize pixelsPerInch;
+
+@synthesize internalValue;
+@synthesize internalString;
+
+@synthesize primitiveType;
+
+
+- (id)init
+{
+    self = [super initWithUnitType:CSS_PRIMITIVE_VALUE];
+    if (self) {
+		self.pixelsPerInch = 1.0f; // this can be overridden by classes that import the CSSPrimitiveValue_ConfigurablePixelsPerInch.h header
+    }
+    return self;
+}
+
+-(void) setFloatValue:(CSSPrimitiveType) unitType floatValue:(float) floatValue
+{
+	self.primitiveType = unitType;
+	self.internalValue = floatValue;
+	
+	self.internalString = nil;
+}
+
+-(float) getFloatValue:(CSSPrimitiveType) unitType
+{
+	/** Easy case: you're asking for the same unit as the originally stored units */
+	if( unitType == self.primitiveType )
+		return self.internalValue;
+	
+	switch( self.primitiveType )
+	{
+		case CSS_UNKNOWN:
+		{
+			if( self.internalValue == 0.0f )
+				return self.internalValue;
+			else
+			{
+				NSAssert( FALSE, @"Asked to convert a UNKNOWN value to a different type (%i)", unitType );
+			}
+		}
+			
+		case CSS_CM:
+		case CSS_IN:
+		case CSS_MM:
+		case CSS_PT:
+		case CSS_PC:
+		{
+			float valueAsInches;
+			switch( self.primitiveType )
+			{
+				case CSS_CM:
+				{
+					valueAsInches = self.internalValue * INCHES_PER_CENTIMETRE;
+				}break;
+				case CSS_MM:
+				{
+					valueAsInches = self.internalValue * INCHES_PER_MILLIMETER;
+				}break;
+				case CSS_PT:
+				{
+					valueAsInches = self.internalValue / 72.0f;
+				}break;
+				case CSS_PC:
+				{
+					valueAsInches = self.internalValue * 12.0f / 72.0f;
+				}break;
+				case CSS_IN:
+				{
+					valueAsInches = self.internalValue;
+				}break;
+					
+				default:
+				{
+					valueAsInches = 0;
+					NSAssert( FALSE, @"This line is impossible but Apple's compiler is crap" );
+				}
+			}
+			
+			switch( unitType )
+			{
+				case CSS_CM:
+				{
+					return valueAsInches / INCHES_PER_CENTIMETRE;
+				}break;
+				case CSS_MM:
+				{
+					return valueAsInches / INCHES_PER_MILLIMETER;
+				}break;
+				case CSS_PT:
+				{
+					return valueAsInches * 72.0f;
+				}break;
+				case CSS_PC:
+				{
+					return valueAsInches / 12.0f * 72.0f;
+				}break;
+				case CSS_PX:
+				{
+					return valueAsInches * self.pixelsPerInch;
+				}break;
+				
+				default:
+				{
+					NSAssert( FALSE, @"Asked to convert a value in centimetres to an incompatible unit type (%i)", unitType );
+				}
+			}
+		} break;
+		
+		case CSS_DEG:
+		case CSS_GRAD:
+		case CSS_RAD:
+		{
+			NSAssert( FALSE, @"Asked to convert an Angle value to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+		} break;
+			
+		case CSS_COUNTER:
+		{
+			NSAssert( FALSE, @"Asked to convert a Counter value to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+		} break;
+			
+		case CSS_DIMENSION:
+		{
+			NSAssert( FALSE, @"Asked to convert a Dimension value to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+		} break;
+			
+		case CSS_EMS:
+		case CSS_EXS:
+		case CSS_PX:
+		{
+			NSAssert( FALSE, @"Asked to convert a Relative Length value to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+		}break;
+			
+		case CSS_HZ:
+		case CSS_MS:
+		case CSS_KHZ:
+		case CSS_S:
+		{
+			NSAssert( FALSE, @"Asked to convert a Time or Frequency value to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+		}break;
+			
+		case CSS_NUMBER:
+		{
+			if( unitType == CSS_PX ) /** Dom 1 spec allows this, SVG Spec says "this is correct by spec", and DOM 2 spec says this is illegal. Most CSS interpreters do it... */
+			{
+				return self.internalValue;
+			}
+			else
+			{
+				NSAssert( FALSE, @"Asked to convert a Number to a different type (NO conversions for this type are currently supported) (%i)", unitType );
+			}
+		}break;
+			
+		case CSS_PERCENTAGE:
+		{
+			if( unitType == CSS_NUMBER )
+			{
+				return self.internalValue / 100.0f; // convert percentages to values from 0.0 - 1.0
+			}
+			else
+				NSAssert( FALSE, @"Asked to convert a Percentage value to a different type (%i)", unitType );
+		}break;
+		
+		default:
+		{
+			NSAssert( FALSE, @"Asked to convert a (%i) value to a (%i) (couldn't find a valid conversion route). Float (4 d.p.) = %2.4f, String = %@", self.primitiveType, unitType, self.internalValue, self.internalString );
+		}
+	}
+	
+	return 0.0f; // this will never happen. you should have Asserted by now, or else returned early with the correct value
+}
+
+-(void) setStringValue:(CSSPrimitiveType) stringType stringValue:(NSString*) stringValue
+{
+	self.primitiveType = stringType;
+	self.internalString = stringValue;
+	
+	self.internalValue = 0.0f;
+}
+
+-(NSString*) getStringValue
+{
+	return self.internalString;
+}
+
+-(/* FIXME: have to add this type: Counter*/ void) getCounterValue
+{
+	NSAssert(FALSE, @"This method not supported");
+}
+
+-(/* FIXME: have to add this type: Rect*/ void) getRectValue
+{
+	NSAssert(FALSE, @"This method not supported");
+}
+
+-(/* FIXME: have to add this type: RGBColor*/ void) getRGBColorValue
+{
+	NSAssert(FALSE, @"This method not supported");
+}
+
+#pragma mark - non DOM spec methods needed to implement Objective-C code for this class
+
+-(void)setCssText:(NSString *)newCssText
+{
+	_cssText = newCssText;
+	
+	/** the css text value has been set, so we need to split the elements up and save them in the internal array */
+	if( _cssText == nil
+	|| _cssText.length == 0 )
+	{
+		self.internalValue = 0.0f;
+		self.internalString = @"";
+		self.primitiveType = CSS_UNKNOWN;
+	}
+	else if( [_cssText hasSuffix:@"%"])
+		[self setFloatValue:CSS_PERCENTAGE floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"em"])
+		[self setFloatValue:CSS_EMS floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"ex"])
+		[self setFloatValue:CSS_EXS floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"px"])
+		[self setFloatValue:CSS_PX floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"cm"])
+		[self setFloatValue:CSS_CM floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"mm"])
+		[self setFloatValue:CSS_MM floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"in"])
+		[self setFloatValue:CSS_IN floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"pt"])
+		[self setFloatValue:CSS_PT floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"pc"])
+		[self setFloatValue:CSS_PC floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"deg"])
+		[self setFloatValue:CSS_DEG floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"rad"])
+		[self setFloatValue:CSS_RAD floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"grad"])
+		[self setFloatValue:CSS_GRAD floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"ms"])
+		[self setFloatValue:CSS_MS floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"s"])
+		[self setFloatValue:CSS_S floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"khz"]) // -----------NB: check this before checking HZ !
+		[self setFloatValue:CSS_KHZ floatValue:[_cssText floatValue]];
+	else if( [_cssText hasSuffix:@"hz"])
+		[self setFloatValue:CSS_HZ floatValue:[_cssText floatValue]];
+	else
+	{
+		/**
+		 Three possible outcomes left:
+		 
+		 1. It's a pure number, no units (in CSS, that's rare - but in SVG it's common, and defined by Spec to be "the same as PX")
+		 2. It's a string, one of many different CSS string types
+		 3. It's a corrupt file
+		 */
+		
+		/**
+		 NSScaner is an Apple class that SPECIFICALLY will refuse to return a number if there are any non-numberic characters in the string */
+		NSScanner *scanner = [NSScanner scannerWithString: _cssText];
+		float floatToHoldTheOutput;
+		if( [scanner scanFloat:&floatToHoldTheOutput])
+		{
+			/* Option 1: it's a pure number */
+			[self setFloatValue:CSS_NUMBER floatValue:floatToHoldTheOutput];
+		}
+		else
+		{
+			/* Option 2: it's a string - or corrupt, which we're not going to handle here */
+#if DEBUG_DOM_PARSING
+			SVGKitLogVerbose(@"[%@] WARNING: not bothering to work out 'what kind of CSS string' this string is. CSS is stupid. String = %@", [self class], _cssText );
+#endif
+			[self setStringValue:CSS_STRING stringValue:_cssText]; // -------- NB: we allow any string-to-string conversion, so it's not a huge problem that we dont correctly detect "url" versus "other kind of string". I hate CSS Parsing...
+		}
+	}
+}
+
+@end

+ 7 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSPrimitiveValue_ConfigurablePixelsPerInch.h

@@ -0,0 +1,7 @@
+#import "CSSPrimitiveValue.h"
+
+@interface CSSPrimitiveValue ()
+
+@property(nonatomic) float pixelsPerInch;
+
+@end

+ 45 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSRule.h

@@ -0,0 +1,45 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSRule
+ 
+ interface CSSRule {
+ 
+ // RuleType
+ const unsigned short      UNKNOWN_RULE                   = 0;
+ const unsigned short      STYLE_RULE                     = 1;
+ const unsigned short      CHARSET_RULE                   = 2;
+ const unsigned short      IMPORT_RULE                    = 3;
+ const unsigned short      MEDIA_RULE                     = 4;
+ const unsigned short      FONT_FACE_RULE                 = 5;
+ const unsigned short      PAGE_RULE                      = 6;
+ 
+ readonly attribute unsigned short   type;
+ attribute DOMString        cssText;
+ // raises(DOMException) on setting
+ 
+ readonly attribute CSSStyleSheet    parentStyleSheet;
+ readonly attribute CSSRule          parentRule;
+ */
+#import <Foundation/Foundation.h>
+
+@class CSSStyleSheet;
+
+typedef enum CSSRuleType
+{
+	UNKNOWN_RULE                   = 0,
+	STYLE_RULE                     = 1,
+	CHARSET_RULE                   = 2,
+	IMPORT_RULE                    = 3,
+	MEDIA_RULE                     = 4,
+	FONT_FACE_RULE                 = 5,
+	PAGE_RULE                      = 6
+} CSSRuleType;
+
+@interface CSSRule : NSObject
+
+@property(nonatomic) unsigned short type;
+@property(nonatomic,strong) NSString* cssText;
+
+@property(nonatomic,strong) CSSStyleSheet* parentStyleSheet;
+@property(nonatomic,strong) CSSRule* parentRule;
+	
+@end

+ 12 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSRule.m

@@ -0,0 +1,12 @@
+#import "CSSRule.h"
+
+@implementation CSSRule
+
+@synthesize type;
+@synthesize cssText;
+
+@synthesize parentStyleSheet;
+@synthesize parentRule;
+
+
+@end

+ 7 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList+Mutable.h

@@ -0,0 +1,7 @@
+#import "CSSRuleList.h"
+
+@interface CSSRuleList ()
+
+@property(nonatomic,strong) NSMutableArray* internalArray;
+
+@end

+ 18 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList.h

@@ -0,0 +1,18 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSRuleList
+ 
+ interface CSSRuleList {
+ readonly attribute unsigned long    length;
+ CSSRule            item(in unsigned long index);
+ */
+#import <Foundation/Foundation.h>
+
+#import "CSSRule.h"
+
+@interface CSSRuleList : NSObject
+
+@property(nonatomic,readonly) unsigned long length;
+
+-(CSSRule*) item:(unsigned long) index;
+
+@end

+ 33 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSRuleList.m

@@ -0,0 +1,33 @@
+#import "CSSRuleList.h"
+#import "CSSRuleList+Mutable.h"
+
+@implementation CSSRuleList
+
+@synthesize internalArray;
+
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        self.internalArray = [NSMutableArray array];
+    }
+    return self;
+}
+
+-(unsigned long)length
+{
+	return self.internalArray.count;
+}
+
+-(CSSRule *)item:(unsigned long)index
+{
+	return [self.internalArray objectAtIndex:index];
+}
+
+-(NSString *)description
+{
+	return [NSString stringWithFormat:@"CSSRuleList: array(%@)", self.internalArray];
+}
+
+@end

+ 49 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleDeclaration.h

@@ -0,0 +1,49 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSStyleDeclaration
+ 
+ interface CSSStyleDeclaration {
+ attribute DOMString        cssText;
+ // raises(DOMException) on setting
+ 
+ DOMString          getPropertyValue(in DOMString propertyName);
+ CSSValue           getPropertyCSSValue(in DOMString propertyName);
+ DOMString          removeProperty(in DOMString propertyName)
+ raises(DOMException);
+ DOMString          getPropertyPriority(in DOMString propertyName);
+ void               setProperty(in DOMString propertyName,
+ in DOMString value,
+ in DOMString priority)
+ raises(DOMException);
+ readonly attribute unsigned long    length;
+ DOMString          item(in unsigned long index);
+ readonly attribute CSSRule          parentRule;
+ */
+#import <Foundation/Foundation.h>
+
+#import "CSSValue.h"
+#import "CSSRule.h"
+
+@interface CSSStyleDeclaration : NSObject
+
+/** NOTE: requires special treatment!
+ 
+ From spec:
+ 
+ "The parsable textual representation of the declaration block (excluding the surrounding curly braces). Setting this attribute will result in the parsing of the new value and resetting of all the properties in the declaration block including the removal or addition of properties."
+ */
+@property(nonatomic,strong) NSString* cssText;
+
+-(NSString*) getPropertyValue:(NSString*) propertyName;
+-(CSSValue*) getPropertyCSSValue:(NSString*) propertyName;
+-(NSString*) removeProperty:(NSString*) propertyName;
+
+-(NSString*) getPropertyPriority:(NSString*) propertyName;
+-(void) setProperty:(NSString*) propertyName value:(NSString*) value priority:(NSString*) priority;
+
+@property(nonatomic) unsigned long length;
+
+-(NSString*) item:(long) index;
+
+@property(nonatomic,strong) CSSRule* parentRule;
+
+@end

+ 165 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleDeclaration.m

@@ -0,0 +1,165 @@
+#import "CSSStyleDeclaration.h"
+
+#import "CSSValue.h"
+#import "CSSValueList.h"
+#import "CSSPrimitiveValue.h"
+#import "CocoaLumberjack/DDFileLogger.h"
+
+#import "SVGKDefine_Private.h"
+
+@interface CSSStyleDeclaration()
+
+@property(nonatomic,strong) NSMutableDictionary* internalDictionaryOfStylesByCSSClass;
+
+@end
+
+@implementation CSSStyleDeclaration
+
+@synthesize internalDictionaryOfStylesByCSSClass;
+
+@synthesize cssText = _cssText;
+@synthesize length;
+@synthesize parentRule;
+
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        self.internalDictionaryOfStylesByCSSClass = [NSMutableDictionary dictionary];
+    }
+    return self;
+}
+
+#define MAX_ACCUM 256
+#define MAX_NAME 256
+
+/** From spec:
+ 
+ "The parsable textual representation of the declaration block (excluding the surrounding curly braces). Setting this attribute will result in the parsing of the new value and resetting of all the properties in the declaration block including the removal or addition of properties."
+ */
+-(void)setCssText:(NSString *)newCSSText
+{
+	_cssText = newCSSText;
+	
+	/** and now post-process it, *as required by* the CSS/DOM spec... */
+	NSMutableDictionary* processedStyles = [self NSDictionaryFromCSSAttributes:_cssText];
+	
+	self.internalDictionaryOfStylesByCSSClass = processedStyles;
+  
+}
+
+-(NSMutableDictionary *) NSDictionaryFromCSSAttributes: (NSString *)css {
+	NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
+	NSCharacterSet* trimChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+	
+	const char *cstr = [css UTF8String];
+	size_t len = strlen(cstr);
+	
+	char name[MAX_NAME];
+	bzero(name, MAX_NAME);
+	
+	char accum[MAX_ACCUM];
+	bzero(accum, MAX_ACCUM);
+	
+	size_t accumIdx = 0;
+	
+	for (size_t n = 0; n <= len; n++) {
+		char c = cstr[n];
+		
+		if (c == ':') {
+			strncpy(name, accum, MAX_NAME);
+			name[accumIdx] = '\0';
+			
+			bzero(accum, MAX_ACCUM);
+			accumIdx = 0;
+			
+			continue;
+		}
+		else if (c == ';' || c == '\0') {
+            if( accumIdx > 0 ) //if there is a ';' and '\0' to end the style, avoid adding an empty key-value pair
+            {
+                accum[accumIdx] = '\0';
+                
+                NSString *keyString = [[NSString stringWithUTF8String:name]
+									   stringByTrimmingCharactersInSet:trimChars];
+				NSString *cssValueString = [[NSString stringWithUTF8String:accum]
+											stringByTrimmingCharactersInSet:trimChars];
+				
+				CSSValue *cssValue;
+				if( [cssValueString rangeOfString:@" "].length > 0 )
+					cssValue = [[CSSValueList alloc] init];
+				else
+					cssValue = [[CSSPrimitiveValue alloc] init];
+				cssValue.cssText = cssValueString; // has the side-effect of parsing, if required
+				
+                [dict setObject:cssValue
+                         forKey:keyString];
+                
+                bzero(name, MAX_NAME);
+				
+                bzero(accum, MAX_ACCUM);
+                accumIdx = 0;
+            }
+			
+			continue;
+		}
+		
+		accum[accumIdx++] = c;
+		if (accumIdx >= MAX_ACCUM) {
+			SVGKitLogWarn(@"Buffer ovverun while parsing style sheet - skipping");
+			return dict;
+		}
+	}
+	
+	return dict;
+}
+
+-(NSString*) getPropertyValue:(NSString*) propertyName
+{
+	CSSValue* v = [self getPropertyCSSValue:propertyName];
+	
+	if( v == nil )
+		return nil;
+	else
+		return v.cssText;
+}
+
+-(CSSValue*) getPropertyCSSValue:(NSString*) propertyName
+{
+	return [self.internalDictionaryOfStylesByCSSClass objectForKey:propertyName];
+}
+
+-(NSString*) removeProperty:(NSString*) propertyName
+{
+	NSString* oldValue = [self getPropertyValue:propertyName];
+	[self.internalDictionaryOfStylesByCSSClass removeObjectForKey:propertyName];
+	return oldValue;
+}
+
+-(NSString*) getPropertyPriority:(NSString*) propertyName
+{
+	NSAssert(FALSE, @"CSS 'property priorities' - Not supported");
+	
+	return nil;
+}
+
+-(void) setProperty:(NSString*) propertyName value:(NSString*) value priority:(NSString*) priority
+{
+	NSAssert(FALSE, @"CSS 'property priorities' - Not supported");
+}
+
+-(NSString*) item:(long) index
+{
+	/** this is stupid slow, but until Apple *can be bothered* to add a "stable-order" dictionary to their libraries, this is the only sensibly easy way of implementing this method */
+	NSArray* sortedKeys = [[self.internalDictionaryOfStylesByCSSClass allKeys] sortedArrayUsingSelector:@selector(compare:)];
+	CSSValue* v = [sortedKeys objectAtIndex:index];
+	return v.cssText;
+}
+
+-(NSString *)description
+{
+	return [NSString stringWithFormat:@"CSSStyleDeclaration: dictionary(%@)", self.internalDictionaryOfStylesByCSSClass];
+}
+
+@end

+ 24 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleRule.h

@@ -0,0 +1,24 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSStyleRule
+ 
+ interface CSSStyleRule : CSSRule {
+ attribute DOMString        selectorText;
+ // raises(DOMException) on setting
+ 
+ readonly attribute CSSStyleDeclaration  style;
+ */
+#import <Foundation/Foundation.h>
+
+#import "CSSRule.h"
+#import "CSSStyleDeclaration.h"
+
+@interface CSSStyleRule : CSSRule
+
+@property(nonatomic,strong) NSString* selectorText;
+@property(nonatomic,strong) CSSStyleDeclaration* style;
+
+#pragma mark - methods needed for ObjectiveC implementation
+
+- (id)initWithSelectorText:(NSString*) selector styleText:(NSString*) styleText;
+
+@end

+ 37 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleRule.m

@@ -0,0 +1,37 @@
+
+#import "CSSStyleRule.h"
+
+@implementation CSSStyleRule
+
+@synthesize selectorText;
+@synthesize style;
+
+
+- (id)init
+{
+	NSAssert(FALSE, @"Can't be init'd, use the right method, idiot");
+	return nil;
+}
+
+#pragma mark - methods needed for ObjectiveC implementation
+
+- (id)initWithSelectorText:(NSString*) selector styleText:(NSString*) styleText;
+{
+    self = [super init];
+    if (self) {
+        self.selectorText = selector;
+		
+		CSSStyleDeclaration* styleDeclaration = [[CSSStyleDeclaration alloc] init];
+		styleDeclaration.cssText = styleText;
+		
+		self.style = styleDeclaration;
+    }
+    return self;
+}
+
+-(NSString *)description
+{
+	return [NSString stringWithFormat:@"%@ : { %@ }", self.selectorText, self.style ];
+}
+
+@end

+ 31 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleSheet.h

@@ -0,0 +1,31 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSStyleSheet
+ 
+ interface CSSStyleSheet : stylesheets::StyleSheet {
+ readonly attribute CSSRule          ownerRule;
+ readonly attribute CSSRuleList      cssRules;
+ unsigned long      insertRule(in DOMString rule,
+ in unsigned long index)
+ raises(DOMException);
+ void               deleteRule(in unsigned long index)
+ raises(DOMException);
+ */
+#import <Foundation/Foundation.h>
+
+#import "CSSRule.h"
+#import "CSSRuleList.h"
+
+@interface CSSStyleSheet : NSObject
+
+
+@property(nonatomic,strong) CSSRule* ownerRule;
+@property(nonatomic,strong) CSSRuleList* cssRules;
+
+-(long) insertRule:(NSString*) rule index:(unsigned long) index;
+-(void) deleteRule:(unsigned long) index;
+
+#pragma mark - methods needed for ObjectiveC implementation
+
+- (id)initWithString:(NSString*) styleSheetBody;
+
+@end

+ 92 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSStyleSheet.m

@@ -0,0 +1,92 @@
+#import "CSSStyleSheet.h"
+
+#import "CSSRuleList+Mutable.h"
+
+#import "CSSStyleRule.h"
+
+@implementation CSSStyleSheet
+
+@synthesize ownerRule;
+@synthesize cssRules;
+
+
+/**
+ Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
+
+ Parameters
+ 
+ rule of type DOMString
+ The parsable text representing the rule. For rule sets this contains both the selector and the style declaration. For at-rules, this specifies both the at-identifier and the rule content.
+ index of type unsigned long
+ The index within the style sheet's rule list of the rule before which to insert the specified rule. If the specified index is equal to the length of the style sheet's rule collection, the rule will be added to the end of the style sheet.
+ 
+ Return Value
+ 
+ unsigned long The index within the style sheet's rule collection of the newly inserted rule.
+ */
+-(long)insertRule:(NSString *)rule index:(unsigned long)index
+{
+	if( index == self.cssRules.length )
+		index = self.cssRules.length + 1; // forces it to insert "before the one that doesn't exist" (stupid API design!)
+	
+	NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+	rule = [rule stringByTrimmingCharactersInSet:whitespaceSet];
+	
+	//             SVGKitLogVerbose(@"A substringie %@", idStyleString);
+	
+	NSArray* stringSplitContainer = [rule componentsSeparatedByString:@"{"];
+	if( [stringSplitContainer count] >= 2 ) //not necessary unless using shitty svgs
+	{
+		CSSStyleRule* newRule = [[CSSStyleRule alloc] initWithSelectorText:[stringSplitContainer objectAtIndex:0] styleText:[stringSplitContainer objectAtIndex:1]];
+		
+		[self.cssRules.internalArray insertObject:newRule atIndex:index-1]; // CSS says you insert "BEFORE" the index, which is the opposite of most C-based programming languages
+		
+		return index-1;
+	}
+	else
+		NSAssert(FALSE, @"No idea what to do here");
+	
+	
+	return -1; // failed, assert fired!
+}
+
+-(void)deleteRule:(unsigned long)index
+{
+	[self.cssRules.internalArray removeObjectAtIndex:index];
+}
+
+#pragma mark - methods needed for ObjectiveC implementation
+
+- (id)initWithString:(NSString*) styleSheetBody
+{
+    self = [super init];
+    if (self)
+	{
+		self.cssRules = [[CSSRuleList alloc]init];
+		@autoreleasepool { //creating lots of autoreleased strings, not helpful for older devices
+			
+			/**
+			 We have to manually handle the "ignore anything that is between / *  and * / because those are comments"
+			 
+			 NB: you NEED the NSRegularExpressionDotMatchesLineSeparators argument - which Apple DOES NOT HONOUR in NSString - hence have to use NSRegularExpression
+			 */
+			NSError* error;
+			NSRegularExpression* regexp = [NSRegularExpression regularExpressionWithPattern:@"/\\*.*?\\*/" options: NSRegularExpressionDotMatchesLineSeparators error:&error];
+			styleSheetBody = [regexp stringByReplacingMatchesInString:styleSheetBody options:0 range:NSMakeRange(0,styleSheetBody.length) withTemplate:@""];
+			
+			NSArray *classNameAndStyleStrings = [styleSheetBody componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"}"]];
+			for( NSString *idStyleString in classNameAndStyleStrings )
+			{
+				if( [idStyleString length] > 1 ) //not necessary unless using shitty svgs
+				{
+					[self insertRule:idStyleString index:self.cssRules.length];
+				}
+				
+			}
+		}
+	
+    }
+    return self;
+}
+
+@end

+ 35 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue.h

@@ -0,0 +1,35 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSValue
+ 
+ interface CSSValue {
+ 
+ // UnitTypes
+ const unsigned short      CSS_INHERIT                    = 0;
+ const unsigned short      CSS_PRIMITIVE_VALUE            = 1;
+ const unsigned short      CSS_VALUE_LIST                 = 2;
+ const unsigned short      CSS_CUSTOM                     = 3;
+ 
+ attribute DOMString        cssText;
+ // raises(DOMException) on setting
+ 
+ readonly attribute unsigned short   cssValueType;
+ */
+#import <Foundation/Foundation.h>
+
+typedef enum CSSUnitType
+{
+	CSS_INHERIT                    = 0,
+	CSS_PRIMITIVE_VALUE            = 1,
+	CSS_VALUE_LIST                 = 2,
+	CSS_CUSTOM                     = 3
+} CSSUnitType;
+
+@interface CSSValue : NSObject
+{
+	NSString* _cssText; // for subclasses to manually set
+}
+
+@property(nonatomic,strong) NSString* cssText;
+@property(nonatomic) CSSUnitType cssValueType;
+
+@end

+ 25 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue.m

@@ -0,0 +1,25 @@
+#import "CSSValue.h"
+#import "CSSValue_ForSubclasses.h"
+
+@implementation CSSValue
+
+@synthesize cssText = _cssText;
+@synthesize cssValueType;
+
+
+- (id)init
+{
+    NSAssert(FALSE, @"This class cannot be init'd using init. It would break it, badly. Use the correct init call instead (if you don't know what that is, you shouldn't be init'ing this class)");
+    
+	return nil;
+}
+
+- (id)initWithUnitType:(CSSUnitType) t
+{
+    self = [super init];
+    if (self) {
+		self.cssValueType = t;
+    }
+    return self;
+}
+@end

+ 17 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSValueList.h

@@ -0,0 +1,17 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-CSSValueList
+ 
+ interface CSSValueList : CSSValue {
+ readonly attribute unsigned long    length;
+ CSSValue           item(in unsigned long index);
+ */
+
+#import "CSSValue.h"
+
+@interface CSSValueList : CSSValue
+
+@property(nonatomic,readonly) unsigned long length;
+
+-(CSSValue*) item:(unsigned long) index;
+
+@end

+ 47 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSValueList.m

@@ -0,0 +1,47 @@
+#import "CSSValueList.h"
+#import "CSSValue_ForSubclasses.h"
+#import "SVGKDefine_Private.h"
+
+@interface CSSValueList()
+
+@property(nonatomic,strong) NSArray* internalArray;
+
+@end
+
+@implementation CSSValueList
+
+@synthesize internalArray;
+
+
+- (id)init
+{
+    self = [super initWithUnitType:CSS_VALUE_LIST];
+    if (self) {
+        self.internalArray = [NSArray array];
+    }
+    return self;
+}
+
+-(unsigned long)length
+{
+	return self.internalArray.count;
+}
+
+-(CSSValue*) item:(unsigned long) index
+{
+	return [self.internalArray objectAtIndex:index];
+}
+
+#pragma mark - non DOM spec methods needed to implement Objective-C code for this class
+
+-(void)setCssText:(NSString *)newCssText
+{
+	_cssText = newCssText;
+	
+	/** the css text value has been set, so we need to split the elements up and save them in the internal array */
+	SVGKitLogVerbose(@"[%@] received new CSS Text, need to split this and save as CSSValue instances: %@", [self class], _cssText);
+	
+	self.internalArray = [_cssText componentsSeparatedByString:@" "];
+}
+
+@end

+ 7 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CSSValue_ForSubclasses.h

@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@interface CSSValue()
+
+- (id)initWithUnitType:(CSSUnitType) t;
+
+@end

+ 48 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CharacterData.h

@@ -0,0 +1,48 @@
+/*
+ From SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-FF21A306
+ 
+ interface CharacterData : Node {
+ attribute DOMString        data;
+ // raises(DOMException) on setting
+ // raises(DOMException) on retrieval
+ 
+ readonly attribute unsigned long    length;
+ DOMString          substringData(in unsigned long offset, 
+ in unsigned long count)
+ raises(DOMException);
+ void               appendData(in DOMString arg)
+ raises(DOMException);
+ void               insertData(in unsigned long offset, 
+ in DOMString arg)
+ raises(DOMException);
+ void               deleteData(in unsigned long offset, 
+ in unsigned long count)
+ raises(DOMException);
+ void               replaceData(in unsigned long offset, 
+ in unsigned long count, 
+ in DOMString arg)
+ raises(DOMException);
+ };
+
+ */
+
+#import <Foundation/Foundation.h>
+
+/** objc won't allow this: @class Node;*/
+#import "Node.h"
+
+@interface CharacterData : Node
+
+@property(nonatomic,strong,readonly) NSString* data;
+	
+@property(nonatomic,readonly) unsigned long length;
+
+-(NSString*) substringData:(unsigned long) offset count:(unsigned long) count;
+-(void) appendData:(NSString*) arg;
+-(void) insertData:(unsigned long) offset arg:(NSString*) arg;
+-(void) deleteData:(unsigned long) offset count:(unsigned long) count;
+-(void) replaceData:(unsigned long) offset count:(unsigned long) count arg:(NSString*) arg;
+
+@end

+ 41 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/CharacterData.m

@@ -0,0 +1,41 @@
+//
+//  CharacterData.m
+//  SVGKit
+//
+//  Created by adam on 22/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "CharacterData.h"
+
+@implementation CharacterData
+
+@synthesize data;
+
+@synthesize length;
+
+
+-(NSString*) substringData:(unsigned long) offset count:(unsigned long) count
+{
+	NSAssert( FALSE, @"Not implemented yet" );
+	return nil;
+}
+
+-(void) appendData:(NSString*) arg
+{
+	NSAssert( FALSE, @"Not implemented yet" );
+}
+-(void) insertData:(unsigned long) offset arg:(NSString*) arg
+{
+	NSAssert( FALSE, @"Not implemented yet" );
+}
+-(void) deleteData:(unsigned long) offset count:(unsigned long) count
+{
+	NSAssert( FALSE, @"Not implemented yet" );
+}
+-(void) replaceData:(unsigned long) offset count:(unsigned long) count arg:(NSString*) arg
+{
+	NSAssert( FALSE, @"Not implemented yet" );
+}
+
+@end

+ 21 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Comment.h

@@ -0,0 +1,21 @@
+/*
+ From SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1728279322
+
+ interface Comment : CharacterData {
+ };
+*/
+
+#import <Foundation/Foundation.h>
+
+#import "CharacterData.h"
+#ifdef __CARBONCORE__
+// macOS's out-of-date Carbon API defined the `Comment` struct and cause naming conflict, so we need re-define it and use macro to avoid changing exist API
+#define Comment SVGKComment
+#endif
+@interface Comment : CharacterData
+
+- (id)initWithValue:(NSString*) v;
+
+@end

+ 21 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Comment.m

@@ -0,0 +1,21 @@
+//
+//  Comment.m
+//  SVGKit
+//
+//  Created by adam on 22/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "Comment.h"
+
+@implementation Comment
+
+- (id)initWithValue:(NSString*) v
+{
+    self = [super initType:DOMNodeType_COMMENT_NODE name:@"#comment" value:v];
+    if (self) {
+    }
+    return self;
+}
+
+@end

+ 1 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DOMGlobalSettings.h

@@ -0,0 +1 @@
+#define DEBUG_DOM_PARSING 0

+ 29 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DOMHelperUtilities.h

@@ -0,0 +1,29 @@
+/**
+ There are some shared methods in DOM specification, where two classes have the same method, but
+ are NOT subclass/superclass of each other. This is very bad from OOP design POV, because it means
+ we end up with copy/paste duplicated code, very VERY likely to gain long term bugs.
+
+ Also, those methods REQUIRE a second, recursive, method or else you can't implement them easily.
+ 
+ So, we move their implementations into this helper class, so they can share implementation.
+ 
+ (c.f. Element vs Document - identical methods for getElementsByName)
+ */
+#import <Foundation/Foundation.h>
+
+@class Node, NodeList, Element; // avoiding #import here, to avoid C header loop problems.
+
+#define DEBUG_DOM_MATCH_ELEMENTS_IDS_AND_NAMES 0 // For debugging SVGKit: causes debug output on getElementById etc
+
+@interface DOMHelperUtilities : NSObject
+
+/*! This useful method provides both the DOM level 1 and the DOM level 2 implementations of searching the tree for a node - because THEY ARE DIFFERENT
+ yet very similar
+ */
++(void) privateGetElementsByName:(NSString*) name inNamespace:(NSString*) namespaceURI childrenOfElement:(Node*) parent addToList:(NodeList*) accumulator;
+
+/*! This is used in multiple base classes in DOM 1 and DOM 2 where they do NOT have shared superclasses, so we have to implement it here in a separate
+ clas as a standalone method */
++(Element*) privateGetElementById:(NSString*) idValue childrenOfElement:(Node*) parent;
+
+@end

+ 87 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DOMHelperUtilities.m

@@ -0,0 +1,87 @@
+
+#import "DOMHelperUtilities.h"
+
+#import "Element.h"
+#import "NodeList.h"
+#import "NodeList+Mutable.h" // needed for access to underlying array, because SVG doesnt specify how lists are made mutable
+
+@implementation DOMHelperUtilities
+
+/*! This useful method provides both the DOM level 1 and the DOM level 2 implementations of searching the tree for a node - because THEY ARE DIFFERENT
+ yet very similar
+ */
++(void) privateGetElementsByName:(NSString*) name inNamespace:(NSString*) namespaceURI childrenOfElement:(Node*) parent addToList:(NodeList*) accumulator
+{
+	/** According to spec, this is only valid for ELEMENT nodes */
+	if( [parent isKindOfClass:[Element class]] )
+	{
+		if( namespaceURI != nil && ! [parent.namespaceURI isEqualToString:namespaceURI] )
+		{
+			// skip
+		}
+		else
+		{
+			Element* parentAsElement = (Element*) parent;
+			
+			/** According to spec, "tag name" for an Element is the value of its .nodeName property; that means SOMETIMES its a qualified name! */
+			BOOL includeThisNode = FALSE;
+			
+			
+			if( [name isEqualToString:@"*"] )
+				includeThisNode = TRUE;
+			
+			if( !includeThisNode )
+			{
+				if( namespaceURI == nil ) // No namespace? then do a qualified compare
+				{
+					includeThisNode = [parentAsElement.tagName isEqualToString:name];
+				}
+				else // namespace? then do an UNqualified compare
+				{
+					includeThisNode = [parentAsElement.localName isEqualToString:name];
+				}
+			}
+			
+			if( includeThisNode )
+			{
+				[accumulator.internalArray addObject:parent];
+			}
+		}
+	}
+	
+	for( Node* childNode in parent.childNodes )
+	{
+		[self privateGetElementsByName:name inNamespace:namespaceURI childrenOfElement:childNode addToList:accumulator];
+	}
+}
+
++(Element*) privateGetElementById:(NSString*) idValue childrenOfElement:(Node*) parent
+{
+	/** According to spec, this is only valid for ELEMENT nodes */
+	if( [parent isKindOfClass:[Element class]] )
+	{
+		Element* parentAsElement = (Element*) parent;
+
+		if( [[parentAsElement getAttribute:@"id"] isEqualToString:idValue])
+			return parentAsElement;
+#if DEBUG_DOM_MATCH_ELEMENTS_IDS_AND_NAMES
+		else
+		{
+			SVGKitLogVerbose(@"parent <%@ id='%@'..> does not match id='%@'", parentAsElement.nodeName, [parentAsElement getAttribute:@"id"], idValue );
+			SVGKitLogVerbose(@"parent <%@ id='%@'..> has %li child nodes = %@", parentAsElement.nodeName, [parentAsElement getAttribute:@"id"], parent.childNodes.length, parent.childNodes );
+		}
+#endif
+	}
+	
+	for( Node* childNode in parent.childNodes )
+	{
+		Element* childResult = [self privateGetElementById:idValue childrenOfElement:childNode];
+		
+		if( childResult != nil )
+			return childResult;
+	}
+	
+	return nil;
+}
+
+@end

+ 7 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Document+Mutable.h

@@ -0,0 +1,7 @@
+#import "Document.h"
+
+@interface Document ()
+
+@property(nonatomic,strong,readwrite) Element*          documentElement;
+
+@end

+ 111 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Document.h

@@ -0,0 +1,111 @@
+/*
+//  Document.h
+
+ NOT a Cocoa / Apple document,
+ NOT an SVG document,
+ BUT INSTEAD: a DOM document (blame w3.org for the too-generic name).
+ 
+ Required for SVG-DOM
+ 
+ c.f.:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#i-Document
+ 
+ interface Document : Node {
+ readonly attribute DocumentType     doctype;
+ readonly attribute DOMImplementation  implementation;
+ readonly attribute Element          documentElement;
+ Element            createElement(in DOMString tagName)
+ raises(DOMException);
+ DocumentFragment   createDocumentFragment();
+ Text               createTextNode(in DOMString data);
+ Comment            createComment(in DOMString data);
+ CDATASection       createCDATASection(in DOMString data)
+ raises(DOMException);
+ ProcessingInstruction createProcessingInstruction(in DOMString target, 
+ in DOMString data)
+ raises(DOMException);
+ Attr               createAttribute(in DOMString name)
+ raises(DOMException);
+ EntityReference    createEntityReference(in DOMString name)
+ raises(DOMException);
+ NodeList           getElementsByTagName(in DOMString tagname);
+ // Introduced in DOM Level 2:
+ Node               importNode(in Node importedNode, 
+ in boolean deep)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ Element            createElementNS(in DOMString namespaceURI, 
+ in DOMString qualifiedName)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ Attr               createAttributeNS(in DOMString namespaceURI, 
+ in DOMString qualifiedName)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ NodeList           getElementsByTagNameNS(in DOMString namespaceURI, 
+ in DOMString localName);
+ // Introduced in DOM Level 2:
+ Element            getElementById(in DOMString elementId);
+ };
+
+ 
+ */
+
+#import <Foundation/Foundation.h>
+
+/** ObjectiveC won't allow this: @class Node; */
+#import "Node.h"
+@class Element;
+#import "Element.h"
+//@class Comment;
+#import "Comment.h"
+@class CDATASection;
+#import "CDATASection.h"
+@class DocumentFragment;
+#import "DocumentFragment.h"
+@class EntityReference;
+#import "EntityReference.h"
+@class NodeList;
+#import "NodeList.h"
+@class ProcessingInstruction;
+#import "ProcessingInstruction.h"
+@class DocumentType;
+#import "DocumentType.h"
+@class AppleSucksDOMImplementation;
+#import "AppleSucksDOMImplementation.h"
+
+@interface Document : Node
+
+@property(nonatomic,strong,readonly) DocumentType*     doctype;
+@property(nonatomic,strong,readonly) AppleSucksDOMImplementation*  implementation;
+@property(nonatomic,strong,readonly) Element*          documentElement;
+
+
+-(Element*) createElement:(NSString*) tagName __attribute__((ns_returns_retained));
+-(DocumentFragment*) createDocumentFragment __attribute__((ns_returns_retained));
+-(Text*) createTextNode:(NSString*) data __attribute__((ns_returns_retained));
+-(Comment*) createComment:(NSString*) data __attribute__((ns_returns_retained));
+-(CDATASection*) createCDATASection:(NSString*) data __attribute__((ns_returns_retained));
+-(ProcessingInstruction*) createProcessingInstruction:(NSString*) target data:(NSString*) data __attribute__((ns_returns_retained));
+-(Attr*) createAttribute:(NSString*) data __attribute__((ns_returns_retained));
+-(EntityReference*) createEntityReference:(NSString*) data __attribute__((ns_returns_retained));
+
+-(NodeList*) getElementsByTagName:(NSString*) data;
+
+// Introduced in DOM Level 2:
+-(Node*) importNode:(Node*) importedNode deep:(BOOL) deep;
+
+// Introduced in DOM Level 2:
+-(Element*) createElementNS:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName __attribute__((ns_returns_retained));
+
+// Introduced in DOM Level 2:
+-(Attr*) createAttributeNS:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName;
+
+// Introduced in DOM Level 2:
+-(NodeList*) getElementsByTagNameNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+// Introduced in DOM Level 2:
+-(Element*) getElementById:(NSString*) elementId;
+
+@end

+ 111 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Document.m

@@ -0,0 +1,111 @@
+#import "Document.h"
+#import "Document+Mutable.h"
+
+#import "DOMHelperUtilities.h"
+
+#import "NodeList+Mutable.h" // needed for access to underlying array, because SVG doesnt specify how lists are made mutable
+
+#import "SVGKDefine_Private.h"
+
+@implementation Document
+
+@synthesize doctype;
+@synthesize implementation;
+@synthesize documentElement;
+
+
+
+-(Element*) createElement:(NSString*) tagName
+{
+	Element* newElement = [[Element alloc] initWithLocalName:tagName attributes:nil];
+	
+	SVGKitLogVerbose( @"[%@] WARNING: SVG Spec, missing feature: if there are known attributes with default values, Attr nodes representing them SHOULD BE automatically created and attached to the element.", [self class] );
+	
+	return newElement;
+}
+
+-(DocumentFragment*) createDocumentFragment
+{
+	return [[DocumentFragment alloc] init];
+}
+
+-(Text*) createTextNode:(NSString*) data
+{
+	return [[Text alloc] initWithValue:data];
+}
+
+-(Comment*) createComment:(NSString*) data
+{
+	return [[Comment alloc] initWithValue:data];
+}
+
+-(CDATASection*) createCDATASection:(NSString*) data
+{
+	return [[CDATASection alloc] initWithValue:data];
+}
+
+-(ProcessingInstruction*) createProcessingInstruction:(NSString*) target data:(NSString*) data
+{
+	return [[ProcessingInstruction alloc] initProcessingInstruction:target value:data];
+}
+
+-(Attr*) createAttribute:(NSString*) n
+{
+	return [[Attr alloc] initWithName:n value:@""];
+}
+
+-(EntityReference*) createEntityReference:(NSString*) data
+{
+	NSAssert( FALSE, @"Not implemented. According to spec: Creates an EntityReference object. In addition, if the referenced entity is known, the child list of the EntityReference  node is made the same as that of the corresponding Entity  node. Note: If any descendant of the Entity node has an unbound namespace prefix, the corresponding descendant of the created EntityReference node is also unbound; (its namespaceURI is null). The DOM Level 2 does not support any mechanism to resolve namespace prefixes." );
+	return nil;
+}
+
+-(NodeList*) getElementsByTagName:(NSString*) data
+{
+	NodeList* accumulator = [[NodeList alloc] init];
+	[DOMHelperUtilities privateGetElementsByName:data inNamespace:nil childrenOfElement:self.documentElement addToList:accumulator];
+	
+	return accumulator;
+}
+
+// Introduced in DOM Level 2:
+-(Node*) importNode:(Node*) importedNode deep:(BOOL) deep
+{
+	NSAssert( FALSE, @"Not implemented." );
+	return nil;
+}
+
+// Introduced in DOM Level 2:
+-(Element*) createElementNS:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName
+{
+	Element* newElement = [[Element alloc] initWithQualifiedName:qualifiedName inNameSpaceURI:namespaceURI attributes:nil];
+	
+	SVGKitLogVerbose( @"[%@] WARNING: SVG Spec, missing feature: if there are known attributes with default values, Attr nodes representing them SHOULD BE automatically created and attached to the element.", [self class] );
+	
+	return newElement;
+}
+
+// Introduced in DOM Level 2:
+-(Attr*) createAttributeNS:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName
+{
+	NSAssert( FALSE, @"This should be re-implemented to share code with createElementNS: method above" );
+	Attr* newAttr = [[Attr alloc] initWithNamespace:namespaceURI qualifiedName:qualifiedName value:@""];
+	return newAttr;
+}
+
+// Introduced in DOM Level 2:
+-(NodeList*) getElementsByTagNameNS:(NSString*) namespaceURI localName:(NSString*) localName
+{
+	NodeList* accumulator = [[NodeList alloc] init];
+	[DOMHelperUtilities privateGetElementsByName:localName inNamespace:namespaceURI childrenOfElement:self.documentElement addToList:accumulator];
+	
+	return accumulator;
+}
+
+// Introduced in DOM Level 2:
+-(Element*) getElementById:(NSString*) elementId
+{
+	return [DOMHelperUtilities privateGetElementById:elementId childrenOfElement:self.documentElement];
+}
+
+@end

+ 19 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentCSS.h

@@ -0,0 +1,19 @@
+/**
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/css.html#CSS-DocumentCSS
+ 
+ interface DocumentCSS : stylesheets::DocumentStyle {
+ CSSStyleDeclaration getOverrideStyle(in Element elt,
+ in DOMString pseudoElt);
+ */
+#import <Foundation/Foundation.h>
+#import "DocumentStyle.h"
+
+#import "CSSStyleDeclaration.h"
+
+@class Element;
+
+@protocol DocumentCSS <DocumentStyle>
+
+-(CSSStyleDeclaration *)getOverrideStyle:(Element *)element pseudoElt:(NSString *)pseudoElt;
+
+@end

+ 17 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentFragment.h

@@ -0,0 +1,17 @@
+/*
+ From SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-B63ED1A3
+ 
+ interface DocumentFragment : Node {
+ };
+*/
+
+#import <Foundation/Foundation.h>
+
+/** objc won't allow this: @class Node;*/
+#import "Node.h"
+
+@interface DocumentFragment : Node
+
+@end

+ 21 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentFragment.m

@@ -0,0 +1,21 @@
+//
+//  DocumentFragment.m
+//  SVGKit
+//
+//  Created by adam on 22/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "DocumentFragment.h"
+
+@implementation DocumentFragment
+
+- (id)init
+{
+    self = [super initType:DOMNodeType_DOCUMENT_FRAGMENT_NODE name:nil];
+    if (self) {
+		
+    }
+    return self;
+}
+@end

+ 17 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentStyle.h

@@ -0,0 +1,17 @@
+/**
+ 
+ http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle
+ 
+ interface DocumentStyle {
+ readonly attribute StyleSheetList   styleSheets;
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "StyleSheetList.h"
+
+@protocol DocumentStyle <NSObject>
+
+@property(nonatomic,retain) StyleSheetList* styleSheets;
+
+@end

+ 39 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentType.h

@@ -0,0 +1,39 @@
+/*
+ From SVG-DOM, via Core DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-412266927
+ 
+ interface DocumentType : Node {
+ readonly attribute DOMString        name;
+ readonly attribute NamedNodeMap     entities;
+ readonly attribute NamedNodeMap     notations;
+ // Introduced in DOM Level 2:
+ readonly attribute DOMString        publicId;
+ // Introduced in DOM Level 2:
+ readonly attribute DOMString        systemId;
+ // Introduced in DOM Level 2:
+ readonly attribute DOMString        internalSubset;
+ };
+*/
+#import <Foundation/Foundation.h>
+
+#import "Node.h"
+#import "NamedNodeMap.h"
+
+@interface DocumentType : Node
+
+@property(nonatomic,strong,readonly) NSString* name;
+@property(nonatomic,strong,readonly) NamedNodeMap* entities;
+@property(nonatomic,strong,readonly) NamedNodeMap* notations;
+
+// Introduced in DOM Level 2:
+@property(nonatomic,strong,readonly) NSString* publicId;
+
+// Introduced in DOM Level 2:
+@property(nonatomic,strong,readonly) NSString* systemId;
+
+// Introduced in DOM Level 2:
+@property(nonatomic,strong,readonly) NSString* internalSubset;
+
+
+@end

+ 44 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/DocumentType.m

@@ -0,0 +1,44 @@
+//
+//  DocumentType.m
+//  SVGKit
+//
+//  Created by adam on 23/05/2012.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#import "DocumentType.h"
+
+/*
+ in case we need to redeclare them readwrite:
+ @property(nonatomic,retain,readonly) NSString* name;
+ @property(nonatomic,retain,readonly) NamedNodeMap* entities;
+ @property(nonatomic,retain,readonly) NamedNodeMap* notations;
+ 
+ // Introduced in DOM Level 2:
+ @property(nonatomic,retain,readonly) NSString* publicId;
+ 
+ // Introduced in DOM Level 2:
+ @property(nonatomic,retain,readonly) NSString* systemId;
+ 
+ // Introduced in DOM Level 2:
+ @property(nonatomic,retain,readonly) NSString* internalSubset;
+
+ */
+
+@implementation DocumentType
+
+@synthesize name;
+@synthesize entities;
+@synthesize notations;
+
+// Introduced in DOM Level 2:
+@synthesize publicId;
+
+// Introduced in DOM Level 2:
+@synthesize systemId;
+
+// Introduced in DOM Level 2:
+@synthesize internalSubset;
+
+
+@end

+ 99 - 0
Pods/SVGKit/Source/DOM classes/Core DOM/Element.h

@@ -0,0 +1,99 @@
+/*
+ From SVG-DOM, via Core-DOM:
+ 
+ http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-745549614
+ 
+ interface Element : Node {
+ readonly attribute DOMString        tagName;
+ DOMString          getAttribute(in DOMString name);
+ void               setAttribute(in DOMString name, 
+ in DOMString value)
+ raises(DOMException);
+ void               removeAttribute(in DOMString name)
+ raises(DOMException);
+ Attr               getAttributeNode(in DOMString name);
+ Attr               setAttributeNode(in Attr newAttr)
+ raises(DOMException);
+ Attr               removeAttributeNode(in Attr oldAttr)
+ raises(DOMException);
+ NodeList           getElementsByTagName(in DOMString name);
+ // Introduced in DOM Level 2:
+ DOMString          getAttributeNS(in DOMString namespaceURI, 
+ in DOMString localName);
+ // Introduced in DOM Level 2:
+ void               setAttributeNS(in DOMString namespaceURI, 
+ in DOMString qualifiedName, 
+ in DOMString value)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ void               removeAttributeNS(in DOMString namespaceURI, 
+ in DOMString localName)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ Attr               getAttributeNodeNS(in DOMString namespaceURI, 
+ in DOMString localName);
+ // Introduced in DOM Level 2:
+ Attr               setAttributeNodeNS(in Attr newAttr)
+ raises(DOMException);
+ // Introduced in DOM Level 2:
+ NodeList           getElementsByTagNameNS(in DOMString namespaceURI, 
+ in DOMString localName);
+ // Introduced in DOM Level 2:
+ boolean            hasAttribute(in DOMString name);
+ // Introduced in DOM Level 2:
+ boolean            hasAttributeNS(in DOMString namespaceURI, 
+ in DOMString localName);
+ };
+ */
+
+#import <Foundation/Foundation.h>
+
+/** objc won't allow this: @class Node;*/
+#import "Node.h"
+@class Attr;
+#import "Attr.h"
+@class NodeList;
+#import "NodeList.h"
+
+@interface Element : Node
+
+@property(nonatomic,strong,readonly) NSString* tagName;
+
+-(NSString*) getAttribute:(NSString*) name;
+-(void) setAttribute:(NSString*) name value:(NSString*) value;
+-(void) removeAttribute:(NSString*) name;
+-(Attr*) getAttributeNode:(NSString*) name;
+-(Attr*) setAttributeNode:(Attr*) newAttr;
+-(Attr*) removeAttributeNode:(Attr*) oldAttr;
+-(NodeList*) getElementsByTagName:(NSString*) name;
+
+// Introduced in DOM Level 2:
+-(NSString*) getAttributeNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+// Introduced in DOM Level 2:
+-(void) setAttributeNS:(NSString*) namespaceURI qualifiedName:(NSString*) qualifiedName value:(NSString*) value;
+
+// Introduced in DOM Level 2:
+-(void) removeAttributeNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+// Introduced in DOM Level 2:
+-(Attr*) getAttributeNodeNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+// Introduced in DOM Level 2:
+-(Attr*) setAttributeNodeNS:(Attr*) newAttr;
+
+// Introduced in DOM Level 2:
+-(NodeList*) getElementsByTagNameNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+// Introduced in DOM Level 2:
+-(BOOL) hasAttribute:(NSString*) name;
+
+// Introduced in DOM Level 2:
+-(BOOL) hasAttributeNS:(NSString*) namespaceURI localName:(NSString*) localName;
+
+#pragma mark - Objective-C init methods (not in SVG Spec - you're supposed to use SVGDocument's createXXX methods instead)
+
+- (id)initWithLocalName:(NSString*) n attributes:(NSMutableDictionary*) attributes;
+- (id)initWithQualifiedName:(NSString*) n inNameSpaceURI:(NSString*) nsURI attributes:(NSMutableDictionary*) attributes;
+	
+@end

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است