chore(deps): update

This commit is contained in:
yicheng 2020-03-04 17:45:36 +08:00
parent 1540f2c1d7
commit 01f3e3da2e
312 changed files with 3644 additions and 4208 deletions

View File

@ -13,7 +13,9 @@ class Logger {
var fileLogger: DDFileLogger = DDFileLogger()
private init() {
DDLog.add(DDTTYLogger.sharedInstance) // TTY = Xcode console
if let tty = DDTTYLogger.sharedInstance {
DDLog.add(tty) // TTY = Xcode console
}
fileLogger.rollingFrequency = TimeInterval(60 * 60 * 24) // 24 hours
fileLogger.logFileManager.maximumNumberOfLogFiles = 3
DDLog.add(fileLogger)

View File

@ -14,7 +14,7 @@ target 'ClashX' do
inhibit_all_warnings!
use_frameworks!
pod 'LetsMove'
pod 'Alamofire', '~> 5.0.0-rc.3'
pod 'Alamofire', '~> 5.0'
pod 'SwiftyJSON'
pod 'RxSwift'
pod 'RxCocoa'

View File

@ -1,25 +1,25 @@
PODS:
- Alamofire (5.0.0-rc.3)
- CocoaLumberjack/Core (3.6.0)
- CocoaLumberjack/Swift (3.6.0):
- Alamofire (5.0.2)
- CocoaLumberjack/Core (3.6.1)
- CocoaLumberjack/Swift (3.6.1):
- CocoaLumberjack/Core
- Crashlytics (3.14.0):
- Fabric (~> 1.10.2)
- Fabric (1.10.2)
- LetsMove (1.24)
- RxCocoa (5.0.1):
- RxCocoa (5.1.0):
- RxRelay (~> 5)
- RxSwift (~> 5)
- RxRelay (5.0.1):
- RxRelay (5.1.0):
- RxSwift (~> 5)
- RxSwift (5.0.1)
- Sparkle (1.22.0)
- RxSwift (5.1.0)
- Sparkle (1.23.0)
- Starscream (3.1.1)
- SwiftyJSON (5.0.0)
- WebViewJavascriptBridge (6.0.3)
DEPENDENCIES:
- Alamofire (~> 5.0.0-rc.3)
- Alamofire (~> 5.0)
- CocoaLumberjack/Swift
- Crashlytics
- Fabric
@ -36,31 +36,30 @@ SPEC REPOS:
- Alamofire
- CocoaLumberjack
- Crashlytics
- Fabric
- LetsMove
- RxCocoa
- RxRelay
- RxSwift
- Sparkle
- Starscream
- SwiftyJSON
- WebViewJavascriptBridge
https://github.com/cocoapods/specs.git:
- Fabric
- RxSwift
SPEC CHECKSUMS:
Alamofire: ca8c0de6906873be89d6deec5c8de279e00bf872
CocoaLumberjack: 78b0c238666f4f58db069738ec176f4519557516
Alamofire: 3ba7a4db18b4f62c4a1c0e1cb39d7f3d52e10ada
CocoaLumberjack: b17ae15142558d08bbacf69775fa10c4abbebcc9
Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
RxCocoa: e741b9749968e8a143e2b787f1dfbff2b63d0a5c
RxRelay: 89d54507f4fd4d969e6ec1d4bd7f3673640b4640
RxSwift: e2dc62b366a3adf6a0be44ba9f405efd4c94e0c4
Sparkle: 593ac2e677c07bcb6c3b22d621240e7cbedaab57
RxCocoa: 13d2a4d7546a34b8ececae8c281e4ea1dbb94f2b
RxRelay: a168bd6caf712d00c676ac344e9295afc93b418e
RxSwift: ad5874f24bb0dbffd1e9bb8443604e3578796c7a
Sparkle: 55b1a87ba69d56913375a281546b7c82dec95bb0
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7
WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29
PODFILE CHECKSUM: 8dd9c30013fa97b5c735e427620c41790488a2a1
PODFILE CHECKSUM: b104b5e59f0be7cdf17282b19ab5917132171e6f
COCOAPODS: 1.7.5

View File

@ -1,4 +1,4 @@
Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png)
[![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire)
[![Build Status](https://github.com/Alamofire/Alamofire/workflows/Alamofire%20CI/badge.svg?branch=master)](https://github.com/Alamofire/Alamofire/actions)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](https://alamofire.github.io/Alamofire)
@ -10,8 +10,6 @@
Alamofire is an HTTP networking library written in Swift.
**⚠️⚠️⚠️ WARNING ⚠️⚠️⚠️** This documentation is out of date during the Alamofire 5 beta process.
- [Features](#features)
- [Component Libraries](#component-libraries)
- [Requirements](#requirements)
@ -24,7 +22,7 @@ Alamofire is an HTTP networking library written in Swift.
- **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server)
- **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output)
- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md)
- **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-manager), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-delegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request)
- **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request)
- **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests)
- **Model Objects -** [Custom Response Serialization](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#custom-response-serialization)
- **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability)
@ -37,7 +35,7 @@ Alamofire is an HTTP networking library written in Swift.
## Features
- [x] Chainable Request / Response Methods
- [x] URL / JSON / plist Parameter Encoding
- [x] URL / JSON Parameter Encoding
- [x] Upload File / Data / Stream / MultipartFormData
- [x] Download File using Request or Resume Data
- [x] Authentication with URLCredential
@ -65,7 +63,7 @@ In order to keep Alamofire focused specifically on core networking implementatio
## Migration Guides
- Alamofire 5.0 Migration Guide: To be written!
- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md)
- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md)
- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md)
- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md)
@ -77,7 +75,7 @@ In order to keep Alamofire focused specifically on core networking implementatio
- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better!
- If you **want to contribute**, submit a pull request.
- If you **want to contribute**, submit a pull request!
## Installation
@ -86,7 +84,7 @@ In order to keep Alamofire focused specifically on core networking implementatio
[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
pod 'Alamofire', '~> 5.0.0-rc.3'
pod 'Alamofire', '~> 5.0'
```
### Carthage
@ -94,7 +92,7 @@ pod 'Alamofire', '~> 5.0.0-rc.3'
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
```ogdl
github "Alamofire/Alamofire" "5.0.0-rc.3"
github "Alamofire/Alamofire" "5.0"
```
### Swift Package Manager
@ -105,7 +103,7 @@ Once you have your Swift package set up, adding Alamofire as a dependency is as
```swift
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0-rc.3")
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0")
]
```
@ -181,7 +179,7 @@ If you believe you have identified a security vulnerability with Alamofire, you
## Donations
The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization.
Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free.
Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax-free.
Donating to the ASF will enable us to:
- Pay our yearly legal fees to keep the non-profit in good status
@ -190,7 +188,7 @@ Donating to the ASF will enable us to:
- Potentially fund developers to work on one of our projects full-time
The community adoption of the ASF libraries has been amazing.
We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward.
We are greatly humbled by your enthusiasm around the projects and want to continue to do everything we can to move the needle forward.
With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members.
If you use any of our libraries for work, see if your employers would be interested in donating.
Any amount you can donate today to help us reach our goal would be greatly appreciated.

View File

@ -22,499 +22,8 @@
// THE SOFTWARE.
//
import Foundation
/// Reference to `Session.default` for quick bootstrapping and examples.
public let AF = Session.default
/// Global namespace containing API for the `default` `Session` instance.
public enum AF {
/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
static let version = "5.0.0-rc.3"
// MARK: - Data Request
/// Creates a `DataRequest` using `Session.default` to retrieve the contents of the specified `url` using the
/// `method`, `parameters`, `encoding`, and `headers` provided.
///
/// - Parameters:
/// - url: The `URLConvertible` value.
/// - method: The `HTTPMethod`, `.get` by default.
/// - parameters: The `Parameters`, `nil` by default.
/// - encoding: The `ParameterEncoding`, `URLEncoding.default` by default.
/// - headers: The `HTTPHeaders`, `nil` by default.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
///
/// - Returns: The created `DataRequest`.
public static func request(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil) -> DataRequest {
return Session.default.request(url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers,
interceptor: interceptor)
}
/// Creates a `DataRequest` using `Session.default` to retrieve the contents of the specified `url` using the
/// `method`, `parameters`, `encoding`, and `headers` provided.
///
/// - Parameters:
/// - url: The `URLConvertible` value.
/// - method: The `HTTPMethod`, `.get` by default.
/// - parameters: The `Encodable` parameters, `nil` by default.
/// - encoding: The `ParameterEncoder`, `URLEncodedFormParameterEncoder.default` by default.
/// - headers: The `HTTPHeaders`, `nil` by default.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
///
/// - Returns: The created `DataRequest`.
public static func request<Parameters: Encodable>(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil) -> DataRequest {
return Session.default.request(url,
method: method,
parameters: parameters,
encoder: encoder,
headers: headers,
interceptor: interceptor)
}
/// Creates a `DataRequest` using `Session.default` to execute the specified `urlRequest`.
///
/// - Parameters:
/// - urlRequest: The `URLRequestConvertible` value.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
///
/// - Returns: The created `DataRequest`.
public static func request(_ urlRequest: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest {
return Session.default.request(urlRequest, interceptor: interceptor)
}
// MARK: - Download Request
/// Creates a `DownloadRequest` using `Session.default` to download the contents of the specified `url` to
/// the provided `destination` using the `method`, `parameters`, `encoding`, and `headers` provided.
///
/// If `destination` is not specified, the download will be moved to a temporary location determined by Alamofire.
///
/// - Parameters:
/// - url: The `URLConvertible` value.
/// - method: The `HTTPMethod`, `.get` by default.
/// - parameters: The `Parameters`, `nil` by default.
/// - encoding: The `ParameterEncoding`, `URLEncoding.default` by default.
/// - headers: The `HTTPHeaders`, `nil` by default.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
/// - destination: The `DownloadRequest.Destination` closure used the determine the destination of the
/// downloaded file. `nil` by default.
///
/// - Returns: The created `DownloadRequest`.
public static func download(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
to destination: DownloadRequest.Destination? = nil) -> DownloadRequest {
return Session.default.download(url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers,
interceptor: interceptor,
to: destination)
}
/// Creates a `DownloadRequest` using `Session.default` to download the contents of the specified `url` to the
/// provided `destination` using the `method`, encodable `parameters`, `encoder`, and `headers` provided.
///
/// - Note: If `destination` is not specified, the download will be moved to a temporary location determined by
/// Alamofire.
///
/// - Parameters:
/// - url: The `URLConvertible` value.
/// - method: The `HTTPMethod`, `.get` by default.
/// - parameters: The `Encodable` parameters, `nil` by default.
/// - encoder: The `ParameterEncoder`, `URLEncodedFormParameterEncoder.default` by default.
/// - headers: The `HTTPHeaders`, `nil` by default.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
/// - destination: The `DownloadRequest.Destination` closure used the determine the destination of the
/// downloaded file. `nil` by default.
///
/// - Returns: The created `DownloadRequest`.
public static func download<Parameters: Encodable>(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
to destination: DownloadRequest.Destination? = nil) -> DownloadRequest {
return Session.default.download(url,
method: method,
parameters: parameters,
encoder: encoder,
headers: headers,
interceptor: interceptor,
to: destination)
}
// MARK: URLRequest
/// Creates a `DownloadRequest` using `Session.default` to execute the specified `urlRequest` and download
/// the result to the provided `destination`.
///
/// - Parameters:
/// - urlRequest: The `URLRequestConvertible` value.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
/// - destination: The `DownloadRequest.Destination` closure used the determine the destination of the
/// downloaded file. `nil` by default.
///
/// - Returns: The created `DownloadRequest`.
public static func download(_ urlRequest: URLRequestConvertible,
interceptor: RequestInterceptor? = nil,
to destination: DownloadRequest.Destination? = nil) -> DownloadRequest {
return Session.default.download(urlRequest, interceptor: interceptor, to: destination)
}
// MARK: Resume Data
/// Creates a `DownloadRequest` using the `Session.default` from the `resumeData` produced from a previous
/// `DownloadRequest` cancellation to retrieve the contents of the original request and save them to the `destination`.
///
/// - Note: If `destination` is not specified, the download will be moved to a temporary location determined by
/// Alamofire.
///
/// - Note: On some versions of all Apple platforms (iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1),
/// `resumeData` is broken on background URL session configurations. There's an underlying bug in the `resumeData`
/// generation logic where the data is written incorrectly and will always fail to resume the download. For more
/// information about the bug and possible workarounds, please refer to the [this Stack Overflow post](http://stackoverflow.com/a/39347461/1342462).
///
/// - Parameters:
/// - resumeData: The resume `Data`. This is an opaque blob produced by `URLSessionDownloadTask` when a task is
/// cancelled. See [Apple's documentation](https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel)
/// for more information.
/// - interceptor: The `RequestInterceptor`, `nil` by default.
/// - destination: The `DownloadRequest.Destination` closure used to determine the destination of the downloaded
/// file. `nil` by default.
///
/// - Returns: The created `DownloadRequest`.
public static func download(resumingWith resumeData: Data,
interceptor: RequestInterceptor? = nil,
to destination: DownloadRequest.Destination? = nil) -> DownloadRequest {
return Session.default.download(resumingWith: resumeData, interceptor: interceptor, to: destination)
}
// MARK: - Upload Request
// MARK: Data
/// Creates an `UploadRequest` for the given `Data`, `URLRequest` components, and `RequestInterceptor`.
///
/// - Parameters:
/// - data: The `Data` to upload.
/// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ data: Data,
to convertible: URLConvertible,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(data,
to: convertible,
method: method,
headers: headers,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` for the given `Data` using the `URLRequestConvertible` value and `RequestInterceptor`.
///
/// - Parameters:
/// - data: The `Data` to upload.
/// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ data: Data,
with convertible: URLRequestConvertible,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager)
}
// MARK: File
/// Creates an `UploadRequest` for the file at the given file `URL`, using a `URLRequest` from the provided
/// components and `RequestInterceptor`.
///
/// - Parameters:
/// - fileURL: The `URL` of the file to upload.
/// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `UploadRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ fileURL: URL,
to convertible: URLConvertible,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(fileURL,
to: convertible,
method: method,
headers: headers,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` for the file at the given file `URL` using the `URLRequestConvertible` value and
/// `RequestInterceptor`.
///
/// - Parameters:
/// - fileURL: The `URL` of the file to upload.
/// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ fileURL: URL,
with convertible: URLRequestConvertible,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(fileURL, with: convertible, interceptor: interceptor, fileManager: fileManager)
}
// MARK: InputStream
/// Creates an `UploadRequest` from the `InputStream` provided using a `URLRequest` from the provided components and
/// `RequestInterceptor`.
///
/// - Parameters:
/// - stream: The `InputStream` that provides the data to upload.
/// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ stream: InputStream,
to convertible: URLConvertible,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(stream,
to: convertible,
method: method,
headers: headers,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` from the provided `InputStream` using the `URLRequestConvertible` value and
/// `RequestInterceptor`.
///
/// - Parameters:
/// - stream: The `InputStream` that provides the data to upload.
/// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(_ stream: InputStream,
with convertible: URLRequestConvertible,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(stream, with: convertible, interceptor: interceptor, fileManager: fileManager)
}
// MARK: MultipartFormData
/// Creates an `UploadRequest` for the multipart form data built using a closure and sent using the provided
/// `URLRequest` components and `RequestInterceptor`.
///
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
/// used for larger payloads such as video content.
///
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
/// technique was used.
///
/// - Parameters:
/// - multipartFormData: `MultipartFormData` building closure.
/// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or
/// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by
/// default.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is
/// written to disk before being uploaded. `.default` instance by default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(multipartFormData: @escaping (MultipartFormData) -> Void,
to url: URLConvertible,
usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(multipartFormData: multipartFormData,
to: url,
usingThreshold: encodingMemoryThreshold,
method: method,
headers: headers,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` using a `MultipartFormData` building closure, the provided `URLRequestConvertible`
/// value, and a `RequestInterceptor`.
///
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
/// used for larger payloads such as video content.
///
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
/// technique was used.
///
/// - Parameters:
/// - multipartFormData: `MultipartFormData` building closure.
/// - request: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or
/// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by
/// default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is
/// written to disk before being uploaded. `.default` instance by default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(multipartFormData: @escaping (MultipartFormData) -> Void,
with request: URLRequestConvertible,
usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(multipartFormData: multipartFormData,
with: request,
usingThreshold: encodingMemoryThreshold,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the provided `URLRequest` components
/// and `RequestInterceptor`.
///
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
/// used for larger payloads such as video content.
///
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
/// technique was used.
///
/// - Parameters:
/// - multipartFormData: `MultipartFormData` instance to upload.
/// - url: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or
/// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by
/// default.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` to be used if the form data exceeds the memory threshold and is
/// written to disk before being uploaded. `.default` instance by default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(multipartFormData: MultipartFormData,
to url: URLConvertible,
usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(multipartFormData: multipartFormData,
to: url,
usingThreshold: encodingMemoryThreshold,
method: method,
headers: headers,
interceptor: interceptor,
fileManager: fileManager)
}
/// Creates an `UploadRequest` for the prebuilt `MultipartFormData` value using the providing `URLRequestConvertible`
/// value and `RequestInterceptor`.
///
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cumulative
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
/// used for larger payloads such as video content.
///
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
/// technique was used.
///
/// - Parameters:
/// - multipartFormData: `MultipartFormData` instance to upload.
/// - request: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - encodingMemoryThreshold: Byte threshold used to determine whether the form data is encoded into memory or
/// onto disk before being uploaded. `MultipartFormData.encodingMemoryThreshold` by
/// default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
public static func upload(multipartFormData: MultipartFormData,
with request: URLRequestConvertible,
usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
return Session.default.upload(multipartFormData: multipartFormData,
with: request,
usingThreshold: encodingMemoryThreshold,
interceptor: interceptor,
fileManager: fileManager)
}
}
/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
let version = "5.0.2"

View File

@ -24,10 +24,13 @@
/// Type that acts as a generic extension point for all `AlamofireExtended` types.
public struct AlamofireExtension<ExtendedType> {
/// Stores the type or metatype of any extended type.
let type: ExtendedType
/// Stores the type or meta-type of any extended type.
public private(set) var type: ExtendedType
init(_ type: ExtendedType) {
/// Create an instance from the provided value.
///
/// - Parameter type: Instance being extended.
public init(_ type: ExtendedType) {
self.type = type
}
}

View File

@ -398,7 +398,7 @@ extension HTTPHeader {
return "\(osName) \(versionString)"
}()
let alamofireVersion = "Alamofire/\(AF.version)"
let alamofireVersion = "Alamofire/\(version)"
return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
}

View File

@ -240,11 +240,11 @@ extension SCNetworkReachabilityFlags {
var canConnectWithoutUserInteraction: Bool { return canConnectAutomatically && !contains(.interventionRequired) }
var isActuallyReachable: Bool { return isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) }
var isCellular: Bool {
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS)
return contains(.isWWAN)
#else
#else
return false
#endif
#endif
}
/// Human readable `String` for all states, to help with debugging.

View File

@ -35,10 +35,10 @@ public protocol RedirectHandler {
/// 3. A `nil` value to deny the redirect request and return the body of the redirect response.
///
/// - Parameters:
/// - task: The task whose request resulted in a redirect.
/// - request: The URL request object to the new location specified by the redirect response.
/// - response: The response containing the server's response to the original request.
/// - completion: The closure to execute containing the new request, a modified request, or `nil`.
/// - task: The `URLSessionTask` whose request resulted in a redirect.
/// - request: The `URLRequest` to the new location specified by the redirect response.
/// - response: The `HTTPURLResponse` containing the server's response to the original request.
/// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`.
func task(_ task: URLSessionTask,
willBeRedirectedTo request: URLRequest,
for response: HTTPURLResponse,

View File

@ -1045,7 +1045,6 @@ public class DownloadRequest: Request {
/// Specifies that any previous file at the destination `URL` should be removed.
public static let removePreviousFile = Options(rawValue: 1 << 1)
/// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
public let rawValue: Int
public init(rawValue: Int) {
@ -1055,15 +1054,13 @@ public class DownloadRequest: Request {
// MARK: Destination
/// A closure executed once a download request has successfully completed in order to determine where to move the
/// A closure executed once a `DownloadRequest` has successfully completed in order to determine where to move the
/// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
/// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and
/// the options defining how the file should be moved.
public typealias Destination = (_ temporaryURL: URL,
_ response: HTTPURLResponse) -> (destinationURL: URL, options: Options)
// MARK: Destination
/// Creates a download file destination closure which uses the default file manager to move the temporary file to a
/// file URL in the first available directory with the specified search path directory and search path domain mask.
///
@ -1095,6 +1092,8 @@ public class DownloadRequest: Request {
return (destination, [])
}
// MARK: Downloadable
/// Type describing the source used to create the underlying `URLSessionDownloadTask`.
public enum Downloadable {
/// Download should be started from the `URLRequest` produced by the associated `URLRequestConvertible` value.

View File

@ -45,7 +45,7 @@ public enum RetryResult {
case retryWithDelay(TimeInterval)
/// Do not retry.
case doNotRetry
/// Do not retry due to the associated `AFError`.
/// Do not retry due to the associated `Error`.
case doNotRetryWithError(Error)
}

View File

@ -323,14 +323,14 @@ extension DownloadRequest {
///
/// - Returns: The request.
@discardableResult
public func response<T: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: T,
completionHandler: @escaping (AFDownloadResponse<T.SerializedObject>) -> Void)
public func response<Serializer: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
-> Self {
appendResponseSerializer {
// Start work that should be on the serialization queue.
let start = CFAbsoluteTimeGetCurrent()
let result: AFResult<T.SerializedObject> = Result {
let result: AFResult<Serializer.SerializedObject> = Result {
try responseSerializer.serializeDownload(request: self.request,
response: self.response,
fileURL: self.fileURL,
@ -371,7 +371,7 @@ extension DownloadRequest {
didComplete = { completionHandler(response) }
case let .doNotRetryWithError(retryError):
let result: AFResult<T.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
let result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
let response = DownloadResponse(request: self.request,
response: self.response,
@ -777,3 +777,24 @@ extension DataRequest {
completionHandler: completionHandler)
}
}
extension DownloadRequest {
/// Adds a handler to be called once the request has finished.
///
/// - Parameters:
/// - type: `Decodable` type to decode from response data.
/// - queue: The queue on which the completion handler is dispatched. `.main` by default.
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
/// - completionHandler: A closure to be executed once the request has finished.
///
/// - Returns: The request.
@discardableResult
public func responseDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
decoder: DataDecoder = JSONDecoder(),
completionHandler: @escaping (AFDownloadResponse<T>) -> Void) -> Self {
return response(queue: queue,
responseSerializer: DecodableResponseSerializer(decoder: decoder),
completionHandler: completionHandler)
}
}

View File

@ -73,9 +73,9 @@ open class ServerTrustManager {
/// A protocol describing the API used to evaluate server trusts.
public protocol ServerTrustEvaluating {
#if os(Linux)
// Implement this once Linux has API for evaluating server trusts.
#else
#if os(Linux)
// Implement this once Linux has API for evaluating server trusts.
#else
/// Evaluates the given `SecTrust` value for the given `host`.
///
/// - Parameters:
@ -84,7 +84,7 @@ public protocol ServerTrustEvaluating {
///
/// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`.
func evaluate(_ trust: SecTrust, forHost host: String) throws
#endif
#endif
}
// MARK: - Server Trust Evaluators
@ -345,9 +345,9 @@ public final class DisabledEvaluator: ServerTrustEvaluating {
// MARK: - Extensions
public extension Array where Element == ServerTrustEvaluating {
#if os(Linux)
// Add this same convenience method for Linux.
#else
#if os(Linux)
// Add this same convenience method for Linux.
#else
/// Evaluates the given `SecTrust` value for the given `host`.
///
/// - Parameters:
@ -360,7 +360,7 @@ public extension Array where Element == ServerTrustEvaluating {
try evaluator.evaluate(trust, forHost: host)
}
}
#endif
#endif
}
extension Bundle: AlamofireExtended {}

View File

@ -38,6 +38,24 @@ open class SessionDelegate: NSObject {
public init(fileManager: FileManager = .default) {
self.fileManager = fileManager
}
/// Internal method to find and cast requests while maintaining some integrity checking.
///
/// - Parameters:
/// - task: The `URLSessionTask` for which to find the associated `Request`.
/// - type: The `Request` subclass type to cast any `Request` associate with `task`.
func request<R: Request>(for task: URLSessionTask, as type: R.Type) -> R? {
guard let provider = stateProvider else {
assertionFailure("StateProvider is nil.")
return nil
}
guard let request = provider.request(for: task) as? R else {
fatalError("Returned Request is not of expected type: \(R.self).")
}
return request
}
}
/// Type which provides various `Session` state values.
@ -79,10 +97,9 @@ extension SessionDelegate: URLSessionTaskDelegate {
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodServerTrust:
evaluation = attemptServerTrustAuthentication(with: challenge)
case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate:
case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM,
NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate:
evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
// case NSURLAuthenticationMethodClientCertificate:
// Alamofire doesn't currently support client certificate validation.
default:
evaluation = (.performDefaultHandling, nil, nil)
}
@ -161,8 +178,10 @@ extension SessionDelegate: URLSessionTaskDelegate {
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task)
guard let request = stateProvider?.request(for: task) as? UploadRequest else {
fatalError("needNewBodyStream for request that isn't UploadRequest.")
guard let request = request(for: task, as: UploadRequest.self) else {
assertionFailure("needNewBodyStream did not find UploadRequest.")
completionHandler(nil)
return
}
completionHandler(request.inputStream())
@ -210,8 +229,9 @@ extension SessionDelegate: URLSessionDataDelegate {
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data)
guard let request = stateProvider?.request(for: dataTask) as? DataRequest else {
fatalError("dataTask received data for incorrect Request subclass: \(String(describing: stateProvider?.request(for: dataTask)))")
guard let request = request(for: dataTask, as: DataRequest.self) else {
assertionFailure("dataTask did not find DataRequest.")
return
}
request.didReceive(data: data)
@ -242,9 +262,9 @@ extension SessionDelegate: URLSessionDownloadDelegate {
downloadTask: downloadTask,
didResumeAtOffset: fileOffset,
expectedTotalBytes: expectedTotalBytes)
guard let downloadRequest = stateProvider?.request(for: downloadTask) as? DownloadRequest else {
fatalError("No DownloadRequest found for downloadTask: \(downloadTask)")
guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
assertionFailure("downloadTask did not find DownloadRequest.")
return
}
downloadRequest.updateDownloadProgress(bytesWritten: fileOffset,
@ -261,9 +281,9 @@ extension SessionDelegate: URLSessionDownloadDelegate {
didWriteData: bytesWritten,
totalBytesWritten: totalBytesWritten,
totalBytesExpectedToWrite: totalBytesExpectedToWrite)
guard let downloadRequest = stateProvider?.request(for: downloadTask) as? DownloadRequest else {
fatalError("No DownloadRequest found for downloadTask: \(downloadTask)")
guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
assertionFailure("downloadTask did not find DownloadRequest.")
return
}
downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten,
@ -273,8 +293,9 @@ extension SessionDelegate: URLSessionDownloadDelegate {
open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
guard let request = stateProvider?.request(for: downloadTask) as? DownloadRequest else {
fatalError("Download finished but either no request found or request wasn't DownloadRequest")
guard let request = request(for: downloadTask, as: DownloadRequest.self) else {
assertionFailure("downloadTask did not find DownloadRequest.")
return
}
guard let response = request.response else {

View File

@ -411,11 +411,11 @@ final class _URLEncodedFormEncoder {
private let dataEncoding: URLEncodedFormEncoder.DataEncoding
private let dateEncoding: URLEncodedFormEncoder.DateEncoding
public init(context: URLEncodedFormContext,
codingPath: [CodingKey] = [],
boolEncoding: URLEncodedFormEncoder.BoolEncoding,
dataEncoding: URLEncodedFormEncoder.DataEncoding,
dateEncoding: URLEncodedFormEncoder.DateEncoding) {
init(context: URLEncodedFormContext,
codingPath: [CodingKey] = [],
boolEncoding: URLEncodedFormEncoder.BoolEncoding,
dataEncoding: URLEncodedFormEncoder.DataEncoding,
dateEncoding: URLEncodedFormEncoder.DateEncoding) {
self.context = context
self.codingPath = codingPath
self.boolEncoding = boolEncoding

View File

@ -97,7 +97,7 @@ extension Request {
data: Data?)
-> ValidationResult
where S.Iterator.Element == String {
guard let data = data, data.count > 0 else { return .success(Void()) }
guard let data = data, !data.isEmpty else { return .success(Void()) }
guard
let responseContentType = response.mimeType,

View File

@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2010-2019, Deusty, LLC
Copyright (c) 2010-2020, 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:

View File

@ -16,14 +16,15 @@ CocoaLumberjack
**CocoaLumberjack** is a fast & simple, yet powerful & flexible logging framework for Mac and iOS.
### How to get started
- install via [CocoaPods](http://cocoapods.org)
- use `DDOSLogger` for iOS 10 and later, `DDTTYLogger` and `DDASLLogger` for earlier versions.
##### Swift version via CocoaPods
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, '8.0'
# You need to set target when you use CocoaPods 1.0.0 or later.
target 'SampleTarget' do
use_frameworks!
pod 'CocoaLumberjack/Swift'
@ -32,13 +33,44 @@ 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).
##### Swift Usage
For Objective-C use the following:
```ruby
platform :ios, '8.0'
If you installed using CocoaPods or manually:
```swift
import CocoaLumberjackSwift
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.6.0"),
```
#### 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
@ -56,16 +88,9 @@ DDLogWarn("Warn")
DDLogError("Error")
```
##### Obj-C version via CocoaPods
#### Obj-C usage
```ruby
platform :ios, '8.0'
pod 'CocoaLumberjack'
```
##### Obj-C usage
If you're using Lumberjack as a framework, you can `@import CocoaLumberjack;`.
Otherwise, `#import <CocoaLumberjack/CocoaLumberjack.h>`
```objc
@ -85,22 +110,13 @@ DDLogWarn(@"Warn");
DDLogError(@"Error");
```
##### Installation with Carthage (iOS 8+)
#### More information
[Carthage](https://github.com/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"
```
- or [install manually](https://raw.githubusercontent.com/CocoaLumberjack/CocoaLumberjack/master/Documentation/GettingStarted.md#manual-installation)
- 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
@ -151,16 +167,16 @@ Configure your logging however you want. Change log levels per file (perfect for
### Requirements
The current version of Lumberjack requires:
- Xcode 10.2 or later
- Xcode 11 or later
- Swift 5.0 or later
- iOS 8 or later
- OS X 10.10 or later
- WatchOS 3 or later
- TVOS 9 or later
- macOS 10.10 or later
- watchOS 3 or later
- tvOS 9 or later
#### Backwards compatibility
- for Xcode 10 and Swift 4.2, use the 3.5.2 version
- for iOS 6, iOS 7, OS X 10.8, OS 10.9 and Xcode 9, use the 3.4.2 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

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -26,9 +26,8 @@
@implementation CLIColor
+ (CLIColor *)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+ (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;
@ -40,15 +39,12 @@
if (red) {
*red = _red;
}
if (green) {
*green = _green;
}
if (blue) {
*blue = _blue;
}
if (alpha) {
*alpha = _alpha;
}

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -62,6 +62,10 @@ static DDASLLogger *sharedInstance;
return self;
}
- (DDLoggerName)loggerName {
return DDLoggerNameASL;
}
- (void)logMessage:(DDLogMessage *)logMessage {
// Skip captured log messages
if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
@ -116,10 +120,6 @@ static DDASLLogger *sharedInstance;
}
}
- (DDLoggerName)loggerName {
return DDLoggerNameASL;
}
@end
#endif

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -16,6 +16,7 @@
#import <CocoaLumberjack/DDFileLogger.h>
NS_ASSUME_NONNULL_BEGIN
@interface DDFileLogger (Internal)
- (void)logData:(NSData *)data;
@ -23,7 +24,8 @@ NS_ASSUME_NONNULL_BEGIN
// Will assert if used outside logger's queue.
- (void)lt_logData:(NSData *)data;
- (NSData *)lt_dataForMessage:(DDLogMessage *)message;
- (nullable NSData *)lt_dataForMessage:(DDLogMessage *)message;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -50,11 +50,14 @@ NSTimeInterval const kDDDefaultLogRollingFrequency = 60 * 60 * 24; // 24
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;
@ -70,15 +73,29 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
@synthesize maximumNumberOfLogFiles = _maximumNumberOfLogFiles;
@synthesize logFilesDiskQuota = _logFilesDiskQuota;
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
if ([theKey isEqualToString:@"maximumNumberOfLogFiles"] || [theKey isEqualToString:@"logFilesDiskQuota"]) {
return NO;
} else {
return [super automaticallyNotifiesObserversForKey:theKey];
}
}
- (instancetype)init {
return [self initWithLogsDirectory:nil];
}
- (instancetype)initWithLogsDirectory:(NSString * __nullable)aLogsDirectory {
- (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 setDateFormat: @"yyyy'-'MM'-'dd'--'HH'-'mm'-'ss'-'SSS'"];
[_fileDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
if (aLogsDirectory.length > 0) {
_logsDirectory = [aLogsDirectory copy];
} else {
@ -97,15 +114,6 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
return self;
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
if ([theKey isEqualToString:@"maximumNumberOfLogFiles"] || [theKey isEqualToString:@"logFilesDiskQuota"]) {
return NO;
} else {
return [super automaticallyNotifiesObserversForKey:theKey];
}
}
#if TARGET_OS_IPHONE
- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory
defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel {
@ -301,20 +309,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
// if you change formatter, then change sortedLogFileInfos method also accordingly
- (NSDateFormatter *)logFileDateFormatter {
NSMutableDictionary *dictionary = [[NSThread currentThread] threadDictionary];
NSString *dateFormat = @"yyyy'-'MM'-'dd'--'HH'-'mm'-'ss'-'SSS'";
NSString *key = [NSString stringWithFormat:@"logFileDateFormatter.%@", dateFormat];
NSDateFormatter *dateFormatter = dictionary[key];
if (dateFormatter == nil) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setDateFormat:dateFormat];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
dictionary[key] = dateFormatter;
}
return dateFormatter;
return _fileDateFormatter;
}
- (NSArray *)unsortedLogFilePaths {
@ -326,19 +321,19 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
for (NSString *fileName in fileNames) {
// Filter out any files that aren't log files. (Just for extra safety)
#if TARGET_IPHONE_SIMULATOR
#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.
//
// See full explanation in the header file.
NSString *theFileName = [fileName stringByReplacingOccurrencesOfString:@".archived"
withString:@""];
if ([self isLogFile:theFileName])
#else
#else
if ([self isLogFile:fileName])
#endif
#endif
{
NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName];
@ -409,7 +404,10 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
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];
}
@ -417,7 +415,10 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
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];
}
@ -440,13 +441,13 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
return [NSString stringWithFormat:@"%@ %@.log", appName, formattedDate];
}
- (NSString * __nullable)logFileHeader {
- (nullable NSString *)logFileHeader {
return nil;
}
- (NSData *)logFileHeaderData {
NSString *fileHeaderStr = [self logFileHeader];
if (fileHeaderStr.length == 0) {
return nil;
}
@ -458,7 +459,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
return [fileHeaderStr dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSString *)createNewLogFile {
- (NSString *)createNewLogFileWithError:(NSError *__autoreleasing _Nullable *)error {
static NSUInteger MAX_ALLOWED_ERROR = 5;
NSString *fileName = [self newLogFileName];
@ -470,16 +471,17 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
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];
@ -493,8 +495,8 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
NSString *filePath = [logsDirectory stringByAppendingPathComponent:actualFileName];
NSError *error = nil;
BOOL success = [fileHeader writeToFile:filePath options:NSAtomicWrite error:&error];
NSError *currentError = nil;
BOOL success = [fileHeader writeToFile:filePath options:NSDataWritingAtomic error:&currentError];
#if TARGET_OS_IPHONE
if (success) {
@ -506,23 +508,24 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
NSDictionary *attributes = @{NSFileProtectionKey: [self logFileProtection]};
success = [[NSFileManager defaultManager] setAttributes:attributes
ofItemAtPath:filePath
error:&error];
error:&currentError];
}
#endif
if (success) {
NSLogVerbose(@"PURLogFileManagerDefault: Created new log file: %@", actualFileName);
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 (error.code == NSFileWriteFileExistsError) {
} else if (currentError.code == NSFileWriteFileExistsError) {
attempt++;
continue;
} else {
NSLogError(@"PURLogFileManagerDefault: Critical error while creating log file: %@", error);
NSLogError(@"DDLogFileManagerDefault: Critical error while creating log file: %@", currentError);
criticalErrors++;
lastCriticalError = currentError;
continue;
}
@ -571,7 +574,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
return [self initWithDateFormatter:nil];
}
- (instancetype)initWithDateFormatter:(NSDateFormatter * __nullable)aDateFormatter {
- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)aDateFormatter {
if ((self = [super init])) {
if (aDateFormatter) {
_dateFormatter = aDateFormatter;
@ -630,7 +633,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
}
- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)aLogFileManager
completionQueue:(dispatch_queue_t __nullable)dispatchQueue {
completionQueue:(nullable dispatch_queue_t)dispatchQueue {
if ((self = [super init])) {
_completionQueue = dispatchQueue ?: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
@ -833,7 +836,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
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, 1ull * NSEC_PER_SEC);
dispatch_source_set_timer(_rollingTimer, fireTime, DISPATCH_TIME_FOREVER, (uint64_t)kDDRollingLeeway * NSEC_PER_SEC);
dispatch_resume(_rollingTimer);
}
@ -841,7 +844,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
[self rollLogFileWithCompletionBlock:nil];
}
- (void)rollLogFileWithCompletionBlock:(void (^ __nullable)(void))completionBlock {
- (void)rollLogFileWithCompletionBlock:(nullable void (^)(void))completionBlock {
// This method is public.
// We need to execute the rolling on our logging thread/queue.
@ -885,10 +888,11 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
_currentLogFileHandle = nil;
_currentLogFileInfo.isArchived = YES;
NSString *archivedFilePath = [_currentLogFileInfo.filePath copy];
BOOL logFileManagerRespondsToSelector = [_logFileManager respondsToSelector:@selector(didRollAndArchiveLogFile:)];
NSString *archivedFilePath = (logFileManagerRespondsToSelector) ? [_currentLogFileInfo.filePath copy] : nil;
_currentLogFileInfo = nil;
if ([_logFileManager respondsToSelector:@selector(didRollAndArchiveLogFile:)]) {
if (logFileManagerRespondsToSelector) {
dispatch_async(_completionQueue, ^{
[self->_logFileManager didRollAndArchiveLogFile:archivedFilePath];
});
@ -908,7 +912,7 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
- (void)lt_maybeRollLogFileDueToAge {
NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue.");
if (_rollingFrequency > 0.0 && _currentLogFileInfo.age >= _rollingFrequency) {
if (_rollingFrequency > 0.0 && (_currentLogFileInfo.age + kDDRollingLeeway) >= _rollingFrequency) {
NSLogVerbose(@"DDFileLogger: Rolling log file due to age...");
[self lt_rollLogFileNow];
} else {
@ -1027,8 +1031,23 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
}
_currentLogFileInfo = newCurrentLogFile;
} else {
NSString *currentLogFilePath = [_logFileManager createNewLogFile];
_currentLogFileInfo = [[DDLogFileInfo alloc] initWithFilePath:currentLogFilePath];
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;
@ -1046,11 +1065,11 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
// 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;
NSString *archivedLogFilePath = [logFileInfo.fileName copy];
if ([_logFileManager respondsToSelector:@selector(didArchiveLogFile:)]) {
NSString *archivedFilePath = [logFileInfo.filePath copy];
dispatch_async(_completionQueue, ^{
[self->_logFileManager didArchiveLogFile:archivedLogFilePath];
[self->_logFileManager didArchiveLogFile:archivedFilePath];
});
}
@ -1111,26 +1130,14 @@ unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20
static int exception_count = 0;
- (void)logMessage:(DDLogMessage *)logMessage {
NSAssert([self isOnInternalLoggerQueue], @"logMessage should only be executed on internal queue.");
// Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
NSData *data = [self lt_dataForMessage:logMessage];
NSString *message = logMessage->_message;
BOOL isFormatted = NO;
if (_logFormatter != nil) {
message = [_logFormatter formatLogMessage:logMessage];
isFormatted = message != logMessage->_message;
}
if (message.length == 0) {
if (data.length == 0) {
return;
}
BOOL shouldFormat = !isFormatted || _automaticallyAppendNewlineForCustomFormatters;
if (shouldFormat && ![message hasSuffix:@"\n"]) {
message = [message stringByAppendingString:@"\n"];
}
[self lt_logData:[message dataUsingEncoding:NSUTF8StringEncoding]];
[self lt_logData:data];
}
- (void)willLogMessage:(DDLogFileInfo *)logFileInfo {
@ -1293,7 +1300,7 @@ static int exception_count = 0;
}
if (message.length == 0) {
return [NSData new];
return nil;
}
BOOL shouldFormat = !isFormatted || _automaticallyAppendNewlineForCustomFormatters;
@ -1310,11 +1317,7 @@ static int exception_count = 0;
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if TARGET_IPHONE_SIMULATOR
static NSString * const kDDXAttrArchivedName = @"archived";
#else
static NSString * const kDDXAttrArchivedName = @"lumberjack.log.archived";
#endif
static NSString * const kDDXAttrArchivedName = @"lumberjack.log.archived";
@interface DDLogFileInfo () {
__strong NSString *_filePath;
@ -1328,6 +1331,15 @@ static int exception_count = 0;
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
@ -1344,14 +1356,15 @@ static int exception_count = 0;
@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];
}
@ -1428,43 +1441,15 @@ static int exception_count = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)isArchived {
#if TARGET_IPHONE_SIMULATOR
// Extended attributes don't work properly on the simulator.
// So we have to use a less attractive alternative.
// See full explanation in the header file.
return [self hasExtensionAttributeWithName:kDDXAttrArchivedName];
#else
return [self hasExtendedAttributeWithName:kDDXAttrArchivedName];
#endif
}
- (void)setIsArchived:(BOOL)flag {
#if TARGET_IPHONE_SIMULATOR
// Extended attributes don't work properly on the simulator.
// So we have to use a less attractive alternative.
// See full explanation in the header file.
if (flag) {
[self addExtensionAttributeWithName:kDDXAttrArchivedName];
} else {
[self removeExtensionAttributeWithName:kDDXAttrArchivedName];
}
#else
if (flag) {
[self addExtendedAttributeWithName:kDDXAttrArchivedName];
} else {
[self removeExtendedAttributeWithName:kDDXAttrArchivedName];
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1483,6 +1468,7 @@ static int exception_count = 0;
// 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];
@ -1490,28 +1476,29 @@ static int exception_count = 0;
// (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;
[[NSFileManager defaultManager] fileExistsAtPath:fileDir isDirectory:&directory];
[fileManager fileExistsAtPath:fileDir isDirectory:&directory];
NSAssert(directory, @"Containing directory must exist.");
#endif
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:newFilePath error:&error];
BOOL success = [fileManager removeItemAtPath:newFilePath error:&error];
if (!success && error.code != NSFileNoSuchFileError) {
NSLogError(@"DDLogFileInfo: Error deleting archive (%@): %@", self.fileName, error);
}
success = [[NSFileManager defaultManager] moveItemAtPath:filePath toPath:newFilePath error:&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) {
if (!success && error.code != NSFileNoSuchFileError)
#else
if (!success) {
if (!success)
#endif
{
NSLogError(@"DDLogFileInfo: Error renaming file (%@): %@", self.fileName, error);
}
@ -1526,13 +1513,26 @@ static int exception_count = 0;
#if TARGET_IPHONE_SIMULATOR
// Extended attributes don't work properly on the simulator.
// So we have to use a less attractive alternative.
// See full explanation in the header file.
// Old implementation of extended attributes on the simulator.
- (BOOL)hasExtensionAttributeWithName:(NSString *)attrName {
// This method is only used on the iPhone simulator, where normal extended attributes are broken.
// See full explanation in the header file.
// 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:
@ -1543,7 +1543,7 @@ static int exception_count = 0;
//
// So we want to search for the attrName in the components (ignoring the first array index).
NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator];
// Watch out for file names without an extension
@ -1558,66 +1558,8 @@ static int exception_count = 0;
return NO;
}
- (void)addExtensionAttributeWithName:(NSString *)attrName {
// This method is only used on the iPhone simulator, where normal extended attributes are broken.
// See full explanation in the header file.
if ([attrName length] == 0) {
return;
}
// Example:
// attrName = "archived"
//
// "mylog.txt" -> "mylog.archived.txt"
// "mylog" -> "mylog.archived"
NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
NSUInteger count = [components count];
NSUInteger estimatedNewLength = [[self fileName] length] + [attrName length] + 1;
NSMutableString *newFileName = [NSMutableString stringWithCapacity:estimatedNewLength];
if (count > 0) {
[newFileName appendString:components.firstObject];
}
NSString *lastExt = @"";
NSUInteger i;
for (i = 1; i < count; i++) {
NSString *attr = components[i];
if ([attr length] == 0) {
continue;
}
if ([attrName isEqualToString:attr]) {
// Extension attribute already exists in file name
return;
}
if ([lastExt length] > 0) {
[newFileName appendFormat:@".%@", lastExt];
}
lastExt = attr;
}
[newFileName appendFormat:@".%@", attrName];
if ([lastExt length] > 0) {
[newFileName appendFormat:@".%@", lastExt];
}
[self renameFile:newFileName];
}
- (void)removeExtensionAttributeWithName:(NSString *)attrName {
// This method is only used on the iPhone simulator, where normal extended attributes are broken.
// See full explanation in the header file.
- (void)_removeExtensionAttributeWithName:(NSString *)attrName {
// This method is only used on the iPhone simulator for backward compatibility reason.
if ([attrName length] == 0) {
return;
@ -1629,7 +1571,7 @@ static int exception_count = 0;
// "mylog.archived.txt" -> "mylog.txt"
// "mylog.archived" -> "mylog"
NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator];
NSUInteger count = [components count];
@ -1650,7 +1592,8 @@ static int exception_count = 0;
if ([attrName isEqualToString:attr]) {
found = YES;
} else {
[newFileName appendFormat:@".%@", attr];
[newFileName appendString:kDDExtensionSeparator];
[newFileName appendString:attr];
}
}
@ -1659,22 +1602,42 @@ static int exception_count = 0;
}
}
#else /* if TARGET_IPHONE_SIMULATOR */
#endif /* if TARGET_IPHONE_SIMULATOR */
- (BOOL)hasExtendedAttributeWithName:(NSString *)attrName {
const char *path = [filePath UTF8String];
const char *path = [filePath fileSystemRepresentation];
const char *name = [attrName UTF8String];
BOOL hasExtendedAttribute = NO;
char buffer[1];
ssize_t result = getxattr(path, name, NULL, 0, 0, 0);
ssize_t result = getxattr(path, name, buffer, 1, 0, 0);
return (result >= 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 UTF8String];
const char *path = [filePath fileSystemRepresentation];
const char *name = [attrName UTF8String];
int result = setxattr(path, name, NULL, 0, 0, 0);
int result = setxattr(path, name, "\1", 1, 0, 0);
if (result < 0) {
NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %s",
@ -1682,10 +1645,15 @@ static int exception_count = 0;
filePath,
strerror(errno));
}
#if TARGET_IPHONE_SIMULATOR
else {
[self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)];
}
#endif
}
- (void)removeExtendedAttributeWithName:(NSString *)attrName {
const char *path = [filePath UTF8String];
const char *path = [filePath fileSystemRepresentation];
const char *name = [attrName UTF8String];
int result = removexattr(path, name, 0);
@ -1696,9 +1664,11 @@ static int exception_count = 0;
self.fileName,
strerror(errno));
}
}
#endif /* if TARGET_IPHONE_SIMULATOR */
#if TARGET_IPHONE_SIMULATOR
[self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)];
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Comparisons

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,6 +22,7 @@
#import <pthread.h>
#import <objc/runtime.h>
#import <sys/qos.h>
#if TARGET_OS_IOS
#import <UIKit/UIDevice.h>
@ -84,9 +85,9 @@ static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueId
@property (nonatomic, readonly) DDLogLevel level;
@property (nonatomic, readonly) dispatch_queue_t loggerQueue;
+ (DDLoggerNode *)nodeWithLogger:(id <DDLogger>)logger
loggerQueue:(dispatch_queue_t)loggerQueue
level:(DDLogLevel)level;
+ (instancetype)nodeWithLogger:(id <DDLogger>)logger
loggerQueue:(dispatch_queue_t)loggerQueue
level:(DDLogLevel)level;
@end
@ -173,7 +174,7 @@ static NSUInteger _numProcessors;
*
* @return An initialized `DDLog` instance.
*/
- (id)init {
- (instancetype)init {
self = [super init];
if (self) {
@ -508,13 +509,11 @@ static NSUInteger _numProcessors;
[self queueLogMessage:logMessage asynchronously:asynchronous];
}
+ (void)log:(BOOL)asynchronous
message:(DDLogMessage *)logMessage {
+ (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
[self.sharedInstance log:asynchronous message:logMessage];
}
- (void)log:(BOOL)asynchronous
message:(DDLogMessage *)logMessage {
- (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
[self queueLogMessage:logMessage asynchronously:asynchronous];
}
@ -693,7 +692,7 @@ static NSUInteger _numProcessors;
// Add to loggers array.
// Need to create loggerQueue if loggerNode doesn't provide one.
for (DDLoggerNode* node in self._loggers) {
for (DDLoggerNode *node in self._loggers) {
if (node->_logger == logger
&& node->_level == level) {
// Exactly same logger already added, exit
@ -705,21 +704,18 @@ static NSUInteger _numProcessors;
@"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];
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];
loggerQueueName = logger.loggerName.UTF8String;
}
loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
@ -1000,8 +996,8 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
return self;
}
+ (DDLoggerNode *)nodeWithLogger:(id <DDLogger>)logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level {
return [[DDLoggerNode alloc] initWithLogger:logger loggerQueue:loggerQueue level:level];
+ (instancetype)nodeWithLogger:(id <DDLogger>)logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level {
return [[self alloc] initWithLogger:logger loggerQueue:loggerQueue level:level];
}
- (void)dealloc {
@ -1071,6 +1067,9 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
// Try to get the current queue's label
_queueLabel = [[NSString alloc] initWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
if (@available(macOS 10.10, iOS 8.0, *))
_qos = (NSUInteger) qos_class_self();
}
return self;
}
@ -1092,6 +1091,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
newMessage->_threadID = _threadID;
newMessage->_threadName = _threadName;
newMessage->_queueLabel = _queueLabel;
newMessage->_qos = _qos;
return newMessage;
}
@ -1110,7 +1110,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
const char *loggerQueueName = NULL;
if ([self respondsToSelector:@selector(loggerName)]) {
loggerQueueName = [[self loggerName] UTF8String];
loggerQueueName = self.loggerName.UTF8String;
}
_loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
@ -1242,9 +1242,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
}
};
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
dispatch_async(globalLoggingQueue, ^{
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self->_loggerQueue, block);
});
}
@ -1293,8 +1291,8 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
return self;
}
+ (DDLoggerInformation *)informationWithLogger:(id <DDLogger>)logger andLevel:(DDLogLevel)level {
return [[DDLoggerInformation alloc] initWithLogger:logger andLevel:level];
+ (instancetype)informationWithLogger:(id <DDLogger>)logger andLevel:(DDLogLevel)level {
return [[self alloc] initWithLogger:logger andLevel:level];
}
@end

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -21,9 +21,11 @@
NSString *_subsystem;
NSString *_category;
}
@property (copy, nonatomic, readonly) NSString *subsystem;
@property (copy, nonatomic, readonly) NSString *category;
@property (strong, nonatomic, readwrite) os_log_t logger;
@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
@ -38,7 +40,7 @@
* Swift: (String, String)?
*/
- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category {
NSAssert((subsystem == nil) == (category == nil), @"Either both subsystem and category or neither can be nil.");
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];
@ -68,9 +70,7 @@ static DDOSLogger *sharedInstance;
if (self.subsystem == nil || self.category == nil) {
return OS_LOG_DEFAULT;
}
__auto_type subdomain = self.subsystem.UTF8String;
__auto_type category = self.category.UTF8String;
return os_log_create(subdomain, category);
return os_log_create(self.subsystem.UTF8String, self.category.UTF8String);
}
- (os_log_t)logger {
@ -82,39 +82,37 @@ static DDOSLogger *sharedInstance;
#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, *)) {
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 :
case DDLogFlagError :
os_log_error(logger, "%{public}s", msg);
break;
case DDLogFlagWarning :
case DDLogFlagInfo :
case DDLogFlagWarning:
case DDLogFlagInfo :
os_log_info(logger, "%{public}s", msg);
break;
case DDLogFlagDebug :
case DDLogFlagVerbose :
default :
case DDLogFlagDebug :
case DDLogFlagVerbose:
default :
os_log_debug(logger, "%{public}s", msg);
break;
}
}
}
}
- (DDLoggerName)loggerName {
return DDLoggerNameOS;
}
@end

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -113,7 +113,7 @@ typedef struct {
size_t resetCodeLen;
}
- (instancetype)initWithForegroundColor:(DDColor *)fgColor backgroundColor:(DDColor *)bgColor flag:(DDLogFlag)mask context:(NSInteger)ctxt;
- (nullable instancetype)initWithForegroundColor:(nullable DDColor *)fgColor backgroundColor:(nullable DDColor *)bgColor flag:(DDLogFlag)mask context:(NSInteger)ctxt;
@end
@ -821,7 +821,7 @@ static DDTTYLogger *sharedInstance;
NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO"));
NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO"));
sharedInstance = [[[self class] alloc] init];
sharedInstance = [[self alloc] init];
});
return sharedInstance;
@ -832,6 +832,10 @@ static DDTTYLogger *sharedInstance;
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 *)
@ -889,6 +893,10 @@ static DDTTYLogger *sharedInstance;
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];
@ -1366,10 +1374,6 @@ static DDTTYLogger *sharedInstance;
}
}
- (DDLoggerName)loggerName {
return DDLoggerNameTTY;
}
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,11 +22,11 @@
@interface DDLoggingContextSet : NSObject
@property (readonly, copy, nonnull) NSArray *currentSet;
- (void)addToSet:(NSInteger)loggingContext;
- (void)removeFromSet:(NSInteger)loggingContext;
@property (readonly, copy) NSArray *currentSet;
- (BOOL)isInSet:(NSInteger)loggingContext;
@end

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -15,22 +15,38 @@
#import <CocoaLumberjack/DDDispatchQueueLogFormatter.h>
#import <pthread/pthread.h>
#import <objc/runtime.h>
#import <stdatomic.h>
#import <sys/qos.h>
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
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 () {
DDDispatchQueueLogFormatterMode _mode;
NSString *_dateFormatterKey;
DDAtomicCounter *_atomicLoggerCounter;
NSDateFormatter *_threadUnsafeDateFormatter; // Use [self stringFromDate]
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
@ -43,31 +59,12 @@
- (instancetype)init {
if ((self = [super init])) {
_mode = DDDispatchQueueLogFormatterModeShareble;
_dateFormatter = [self createDateFormatter];
// We need to carefully pick the name for storing in thread dictionary to not
// use a formatter configured by subclass and avoid surprises.
Class cls = [self class];
Class superClass = class_getSuperclass(cls);
SEL configMethodName = @selector(configureDateFormatter:);
Method configMethod = class_getInstanceMethod(cls, configMethodName);
while (class_getInstanceMethod(superClass, configMethodName) == configMethod) {
cls = superClass;
superClass = class_getSuperclass(cls);
}
// now `cls` is the class that provides implementation for `configureDateFormatter:`
_dateFormatterKey = [NSString stringWithFormat:@"%s_NSDateFormatter", class_getName(cls)];
_atomicLoggerCounter = [[DDAtomicCounter alloc] initWithDefaultValue:0];
_threadUnsafeDateFormatter = nil;
_minQueueLength = 0;
_maxQueueLength = 0;
pthread_mutex_init(&_mutex, NULL);
_replacements = [[NSMutableDictionary alloc] init];
// Set default replacements:
_replacements[@"com.apple.main-thread"] = @"main";
}
@ -75,10 +72,7 @@
}
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode {
if ((self = [self init])) {
_mode = mode;
}
return self;
return [self init];
}
- (void)dealloc {
@ -130,39 +124,11 @@
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
NSString *calendarIdentifier = NSCalendarIdentifierGregorian;
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]];
}
- (NSString *)stringFromDate:(NSDate *)date {
NSDateFormatter *dateFormatter = nil;
if (_mode == DDDispatchQueueLogFormatterModeNonShareble) {
// Single-threaded mode.
dateFormatter = _threadUnsafeDateFormatter;
if (dateFormatter == nil) {
dateFormatter = [self createDateFormatter];
_threadUnsafeDateFormatter = dateFormatter;
}
} else {
// Multi-threaded mode.
// NSDateFormatter is NOT thread-safe.
NSString *key = _dateFormatterKey;
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
dateFormatter = threadDictionary[key];
if (dateFormatter == nil) {
dateFormatter = [self createDateFormatter];
threadDictionary[key] = dateFormatter;
}
}
return [dateFormatter stringFromDate:date];
return [_dateFormatter stringFromDate:date];
}
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage {
@ -262,69 +228,44 @@
NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)];
NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
if (@available(macOS 10.10, iOS 8.0, *))
return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message];
return [NSString stringWithFormat:@"%@ [%@] %@", timestamp, queueThreadLabel, logMessage->_message];
}
- (void)didAddToLogger:(id <DDLogger> __attribute__((unused)))logger {
NSAssert([_atomicLoggerCounter increment] <= 1 || _mode == DDDispatchQueueLogFormatterModeShareble, @"Can't reuse formatter with multiple loggers in non-shareable mode.");
}
- (void)willRemoveFromLogger:(id <DDLogger> __attribute__((unused)))logger {
[_atomicLoggerCounter decrement];
}
@end
#pragma mark - DDAtomicCounter
#define DD_OSATOMIC_API_DEPRECATED (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (TARGET_OS_WATCH && __WATCH_OS_VERSION_MIN_REQUIRED >= 30000) || (TARGET_OS_TV && __TV_OS_VERSION_MIN_REQUIRED >= 100000)
#if DD_OSATOMIC_API_DEPRECATED
#import <stdatomic.h>
#else
#import <libkern/OSAtomic.h>
#endif
@interface DDAtomicCounter() {
#if DD_OSATOMIC_API_DEPRECATED
_Atomic(int32_t) _value;
#else
int32_t _value;
#endif
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])) {
_value = defaultValue;
atomic_init(&_value, defaultValue);
}
return self;
}
- (int32_t)value {
return _value;
return atomic_load_explicit(&_value, memory_order_relaxed);
}
#if DD_OSATOMIC_API_DEPRECATED
- (int32_t)increment {
atomic_fetch_add_explicit(&_value, 1, memory_order_relaxed);
return _value;
int32_t old = atomic_fetch_add_explicit(&_value, 1, memory_order_relaxed);
return (old + 1);
}
- (int32_t)decrement {
atomic_fetch_sub_explicit(&_value, 1, memory_order_relaxed);
return _value;
int32_t old = atomic_fetch_sub_explicit(&_value, 1, memory_order_relaxed);
return (old - 1);
}
#else
- (int32_t)increment {
return OSAtomicIncrement32(&_value);
}
- (int32_t)decrement {
return OSAtomicDecrement32(&_value);
}
#endif
@end

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -110,23 +110,27 @@ static NSUInteger DDGetDefaultBufferSizeBytes() {
#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];
NSUInteger length = data.length;
if (length == 0) {
if (data.length == 0) {
return;
}
#ifndef DEBUG
__unused
[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:[data bytes] maxLength:length];
NSAssert(written == (NSInteger)length, @"Failed to write to memory buffer.");
NSInteger written = [_buffer write:bytes maxLength:bytesLength];
NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
_currentBufferSizeBytes += length;
_currentBufferSizeBytes += bytesLength;
if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
[self lt_sendBufferedDataToFileLogger];
}
if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
[self lt_sendBufferedDataToFileLogger];
}
}];
}
- (void)flush {

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -18,6 +18,8 @@
#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
**/
@ -31,7 +33,7 @@
* @param blue blue channel, between 0 and 1
* @param alpha alpha channel, between 0 and 1
*/
+ (CLIColor *)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
+ (instancetype)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
/**
* Get the RGBA components from a `CLIColor`
@ -41,8 +43,10 @@
* @param blue blue channel, between 0 and 1
* @param alpha alpha channel, between 0 and 1
*/
- (void)getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha NS_SWIFT_NAME(get(red:green:blue:alpha:));
- (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

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -17,6 +17,8 @@
@protocol DDLogger;
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides the ability to capture the ASL (Apple System Logs)
*/
@ -40,3 +42,5 @@ API_DEPRECATED("Use DDOSLogger instead", macosx(10.4,10.12), ios(2.0,10.0), watc
@property (class) DDLogLevel captureLevel;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,6 +22,8 @@
#import <CocoaLumberjack/DDLog.h>
NS_ASSUME_NONNULL_BEGIN
// Custom key set on messages sent to ASL
extern const char* const kDDASLKeyDDLog;
@ -49,7 +51,7 @@ API_DEPRECATED("Use DDOSLogger instead", macosx(10.4,10.12), ios(2.0,10.0), watc
*
* @return the shared instance
*/
@property (class, readonly, strong) DDASLLogger *sharedInstance;
@property (nonatomic, class, readonly, strong) DDASLLogger *sharedInstance;
// Inherited from DDAbstractLogger
@ -57,3 +59,5 @@ API_DEPRECATED("Use DDOSLogger instead", macosx(10.4,10.12), ios(2.0,10.0), watc
// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -20,6 +20,8 @@
#import <CocoaLumberjack/DDLog.h>
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides an abstract implementation of a database logger.
*
@ -121,3 +123,5 @@
- (void)deleteOldLogEntries;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -23,4 +23,3 @@
NSAssert(NO, @"%@", description); \
}
#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,6 +22,8 @@
#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 whitelist.
*
@ -65,7 +67,7 @@
/**
* Return the whitelist
*/
@property (readonly, copy) NSArray<NSNumber *> *whitelist;
@property (nonatomic, readonly, copy) NSArray<NSNumber *> *whitelist;
/**
* Check if a context is on the whitelist
@ -115,3 +117,5 @@
- (BOOL)isOnBlacklist:(NSInteger)loggingContext;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,9 +22,12 @@
#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.
@ -38,6 +41,36 @@ typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
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_EXTENSIBLE_STRING_ENUM;
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUserInteractive API_AVAILABLE(macos(10.10), ios(8.0));
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUserInitiated API_AVAILABLE(macos(10.10), ios(8.0));
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceDefault API_AVAILABLE(macos(10.10), ios(8.0));
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUtility API_AVAILABLE(macos(10.10), ios(8.0));
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceBackground API_AVAILABLE(macos(10.10), ios(8.0));
FOUNDATION_EXPORT DDQualityOfServiceName const DDQualityOfServiceUnspecified 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.
@ -89,7 +122,7 @@ typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
*
* @param mode choose between DDDispatchQueueLogFormatterModeShareble and DDDispatchQueueLogFormatterModeNonShareble, depending if the formatter is shared between several loggers or not
*/
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode;
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode __attribute__((deprecated("DDDispatchQueueLogFormatter is always shareable")));
/**
* The minQueueLength restricts the minimum size of the [detail box].
@ -140,12 +173,12 @@ typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
*
* To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter.
**/
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
- (nullable NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
/**
* See the `replacementStringForQueueLabel:` description
*/
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
- (void)setReplacementString:(nullable NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
@end
@ -176,8 +209,9 @@ typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
@end
#pragma mark - DDAtomicCounter
#pragma mark - DDAtomicCountable
__attribute__((deprecated("DDAtomicCountable is useless since DDDispatchQueueLogFormatter is always shareable now")))
@protocol DDAtomicCountable <NSObject>
- (instancetype)initWithDefaultValue:(int32_t)defaultValue;
@ -187,5 +221,8 @@ typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
@end
__attribute__((deprecated("DDAtomicCountable is deprecated")))
@interface DDAtomicCounter: NSObject<DDAtomicCountable>
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -20,10 +20,10 @@
#import <CocoaLumberjack/DDLog.h>
NS_ASSUME_NONNULL_BEGIN
@class DDLogFileInfo;
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides a logger to write log statements to a file.
**/
@ -148,10 +148,17 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* This method is executed directly on the file logger's internal queue.
* The file has to exist by the time the method returns.
**/
- (NSString *)createNewLogFile;
- (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
/**
@ -195,7 +202,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* 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:(NSString * __nullable)logsDirectory NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithLogsDirectory:(nullable NSString *)logsDirectory NS_DESIGNATED_INITIALIZER;
#if TARGET_OS_IPHONE
/*
@ -209,7 +216,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* null
* cy#
**/
- (instancetype)initWithLogsDirectory:(NSString * __nullable)logsDirectory
- (instancetype)initWithLogsDirectory:(nullable NSString *)logsDirectory
defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel;
#endif
@ -306,7 +313,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
/**
* Designated initializer, requires a date formatter
*/
- (instancetype)initWithDateFormatter:(NSDateFormatter * __nullable)dateFormatter NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
@end
@ -329,15 +336,15 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* A global queue w/ default priority is used to run callbacks.
* If needed, specify queue using `initWithLogFileManager:completionQueue:`.
*/
- (instancetype)initWithLogFileManager:(id <DDLogFileManager> __nullable)logFileManager;
- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager;
/**
* Designated initializer, requires a `DDLogFileManager` instance.
* The completionQueue is used to execute `didArchiveLogFile`, `didRollAndArchiveLogFile`,
* and the callback in `rollLog`. If nil, a global queue w/ default priority is used.
*/
- (instancetype)initWithLogFileManager:(id <DDLogFileManager> __nullable)logFileManager
completionQueue:(dispatch_queue_t __nullable)dispatchQueue NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager
completionQueue:(nullable dispatch_queue_t)dispatchQueue NS_DESIGNATED_INITIALIZER;
/**
* Deprecated. Use `willLogMessage:`
@ -437,14 +444,14 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* You can optionally force the current log file to be rolled with this method.
* CompletionBlock will be called on main queue.
*/
- (void)rollLogFileWithCompletionBlock:(void (^ __nullable)(void))completionBlock
- (void)rollLogFileWithCompletionBlock:(nullable void (^)(void))completionBlock
NS_SWIFT_NAME(rollLogFile(withCompletion:));
/**
* Method is deprecated.
* @deprecated Use `rollLogFileWithCompletionBlock:` method instead.
*/
- (void)rollLogFile __attribute((deprecated));
- (void)rollLogFile __attribute__((deprecated("Use -rollLogFileWithCompletionBlock:")));
// Inherited from DDAbstractLogger
@ -456,9 +463,9 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
* 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.
* Otherwise a new file is created and returned. If this failes, `NULL` is returned.
**/
@property (nonatomic, readonly, strong) DDLogFileInfo *currentLogFileInfo;
@property (nonatomic, nullable, readonly, strong) DDLogFileInfo *currentLogFileInfo;
@end
@ -485,14 +492,10 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
@property (strong, nonatomic, readonly) NSString *filePath;
@property (strong, nonatomic, readonly) NSString *fileName;
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (strong, nonatomic, readonly) NSDictionary<NSFileAttributeKey, id> *fileAttributes;
#else
@property (strong, nonatomic, readonly) NSDictionary<NSString *, id> *fileAttributes;
#endif
@property (strong, nonatomic, readonly) NSDate *creationDate;
@property (strong, nonatomic, readonly) NSDate *modificationDate;
@property (strong, nonatomic, nullable, readonly) NSDate *creationDate;
@property (strong, nonatomic, nullable, readonly) NSDate *modificationDate;
@property (nonatomic, readonly) unsigned long long fileSize;
@ -500,7 +503,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
@property (nonatomic, readwrite) BOOL isArchived;
+ (instancetype)logFileWithPath:(NSString *)filePath NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
+ (nullable instancetype)logFileWithPath:(nullable NSString *)filePath NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFilePath:(NSString *)filePath NS_DESIGNATED_INITIALIZER;
@ -508,45 +511,11 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
- (void)reset;
- (void)renameFile:(NSString *)newFileName NS_SWIFT_NAME(renameFile(to:));
#if TARGET_IPHONE_SIMULATOR
// So here's the situation.
// Extended attributes are perfect for what we're trying to do here (marking files as archived).
// This is exactly what extended attributes were designed for.
//
// But Apple screws us over on the simulator.
// Everytime you build-and-go, they copy the application into a new folder on the hard drive,
// and as part of the process they strip extended attributes from our log files.
// Normally, a copy of a file preserves extended attributes.
// So obviously Apple has gone to great lengths to piss us off.
//
// Thus we use a slightly different tactic for marking log files as archived in the simulator.
// That way it "just works" and there's no confusion when testing.
//
// The difference in method names is indicative of the difference in functionality.
// On the simulator we add an attribute by appending a filename extension.
//
// For example:
// "mylog.txt" -> "mylog.archived.txt"
// "mylog" -> "mylog.archived"
- (BOOL)hasExtensionAttributeWithName:(NSString *)attrName;
- (void)addExtensionAttributeWithName:(NSString *)attrName;
- (void)removeExtensionAttributeWithName:(NSString *)attrName;
#else /* if TARGET_IPHONE_SIMULATOR */
// Normal use of extended attributes used everywhere else,
// such as on Macs and on iPhone devices.
- (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
- (void)addExtendedAttributeWithName:(NSString *)attrName;
- (void)removeExtendedAttributeWithName:(NSString *)attrName;
#endif /* if TARGET_IPHONE_SIMULATOR */
- (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
- (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -80,4 +80,3 @@
#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)

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -39,6 +39,8 @@
@protocol DDLogger;
@protocol DDLogFormatter;
NS_ASSUME_NONNULL_BEGIN
/**
* Define the standard options.
*
@ -111,22 +113,22 @@ 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
*/
@ -141,40 +143,38 @@ 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
};
NS_ASSUME_NONNULL_BEGIN
/**
* Extracts just the file name, no path or extension
*
@ -247,9 +247,9 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
function:(nullable const char *)function
line:(NSUInteger)line
tag:(id __nullable)tag
tag:(nullable id)tag
format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
/**
@ -273,9 +273,9 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
function:(nullable const char *)function
line:(NSUInteger)line
tag:(id __nullable)tag
tag:(nullable id)tag
format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
/**
@ -300,9 +300,9 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
function:(nullable const char *)function
line:(NSUInteger)line
tag:(id __nullable)tag
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:));
@ -328,9 +328,9 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
function:(nullable const char *)function
line:(NSUInteger)line
tag:(id __nullable)tag
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:));
@ -581,7 +581,7 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
* 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) id <DDLogFormatter> logFormatter;
@property (nonatomic, strong, nullable) id <DDLogFormatter> logFormatter;
@optional
@ -666,7 +666,7 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
* The formatter may also optionally filter the log message by returning nil,
* in which case the logger will not log the message.
**/
- (NSString * __nullable)formatLogMessage:(DDLogMessage *)logMessage NS_SWIFT_NAME(format(message:));
- (nullable NSString *)formatLogMessage:(DDLogMessage *)logMessage NS_SWIFT_NAME(format(message:));
@optional
@ -676,7 +676,7 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
*
* 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),
* 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;
@ -687,7 +687,7 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
*
* 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),
* 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.
**/
@ -780,10 +780,11 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
NSUInteger _line;
id _tag;
DDLogMessageOptions _options;
NSDate *_timestamp;
NSDate * _timestamp;
NSString *_threadID;
NSString *_threadName;
NSString *_queueLabel;
NSUInteger _qos;
}
/**
@ -823,11 +824,11 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(NSString * __nullable)function
function:(nullable NSString *)function
line:(NSUInteger)line
tag:(id __nullable)tag
tag:(nullable id)tag
options:(DDLogMessageOptions)options
timestamp:(NSDate * __nullable)timestamp NS_DESIGNATED_INITIALIZER;
timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
/**
* Read-only properties
@ -842,14 +843,15 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
@property (readonly, nonatomic) NSInteger context;
@property (readonly, nonatomic) NSString *file;
@property (readonly, nonatomic) NSString *fileName;
@property (readonly, nonatomic) NSString * __nullable function;
@property (readonly, nonatomic, nullable) NSString * function;
@property (readonly, nonatomic) NSUInteger line;
@property (readonly, nonatomic) id __nullable tag;
@property (readonly, nonatomic, nullable) id tag;
@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) NSString *threadName;
@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
@ -907,8 +909,8 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
@property (nonatomic, readonly) id <DDLogger> logger;
@property (nonatomic, readonly) DDLogLevel level;
+ (DDLoggerInformation *)informationWithLogger:(id <DDLogger>)logger
andLevel:(DDLogLevel)level;
+ (instancetype)informationWithLogger:(id <DDLogger>)logger
andLevel:(DDLogLevel)level;
@end

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -18,9 +18,13 @@
NS_ASSUME_NONNULL_BEGIN
typedef NSString *DDLoggerName NS_TYPED_EXTENSIBLE_ENUM;
FOUNDATION_EXPORT DDLoggerName const DDLoggerNameASL NS_SWIFT_NAME(DDLoggerName.asl); // DDASLLogger
FOUNDATION_EXPORT DDLoggerName const DDLoggerNameTTY NS_SWIFT_NAME(DDLoggerName.tty); // DDTTYLogger
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

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,6 +22,8 @@
#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.
@ -31,7 +33,7 @@
/**
* Array of chained formatters
*/
@property (readonly) NSArray<id<DDLogFormatter>> *formatters;
@property (nonatomic, readonly) NSArray<id<DDLogFormatter>> *formatters;
/**
* Add a new formatter
@ -54,3 +56,5 @@
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -22,6 +22,8 @@
#import <CocoaLumberjack/DDLog.h>
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides a logger for the Apple os_log facility.
**/
@ -33,18 +35,21 @@ API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
*
* @return the shared instance with OS_LOG_DEFAULT.
*/
@property (class, readonly, strong) DDOSLogger *sharedInstance;
@property (nonatomic, class, readonly, strong) DDOSLogger *sharedInstance;
/**
Designed initializer
Designated initializer
@param subsystem Desired subsystem in log. Consider "org.example"
@param category Desired category in log. Consider "Point of interests."
@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 accepts parameters of type (String, String)?
If both parameters are nil, this method will return logger wrapper for `OS_LOG_DEFAULT`.
If both parameters are not nil, it will return logger wrapper for `os_log_create(subsystem, category)`
@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:(NSString *)subsystem category:(NSString *)category NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithSubsystem:(nullable NSString *)subsystem category:(nullable NSString *)category NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -28,20 +28,21 @@
// iOS or tvOS or watchOS
#import <UIKit/UIColor.h>
typedef UIColor DDColor;
static inline DDColor* DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
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* DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
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* DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
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,
@ -60,9 +61,9 @@
@interface DDTTYLogger : DDAbstractLogger <DDLogger>
/**
* Singleton method
* Singleton instance. Returns `nil` if the initialization of the DDTTYLogger fails.
*/
@property (class, readonly, strong) DDTTYLogger *sharedInstance;
@property (nonatomic, class, readonly, strong, nullable) DDTTYLogger *sharedInstance;
/* Inherited from the DDLogger protocol:
*
@ -103,6 +104,11 @@
**/
@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:
*
@ -125,7 +131,7 @@
*
* This method invokes setForegroundColor:backgroundColor:forFlag:context: and applies it to `LOG_CONTEXT_ALL`.
**/
- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask;
- (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.
@ -138,7 +144,7 @@
* Logging context's are explained in further detail here:
* Documentation/CustomContext.md
**/
- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt;
- (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.
@ -163,7 +169,7 @@
*
* DDLogPurple(@"I'm a purple log message!");
**/
- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id <NSCopying>)tag;
- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forTag:(id <NSCopying>)tag;
/**
* Clearing color profiles.
@ -176,3 +182,5 @@
- (void)clearAllColors;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@ -14,7 +14,7 @@
// prior written permission of Deusty, LLC.
#if SWIFT_PACKAGE
import CocoaLumberjack
@_exported import CocoaLumberjack
import CocoaLumberjackSwiftSupport
#endif

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2019, Deusty, LLC
// Copyright (c) 2010-2020, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

35
Pods/Manifest.lock generated
View File

@ -1,25 +1,25 @@
PODS:
- Alamofire (5.0.0-rc.3)
- CocoaLumberjack/Core (3.6.0)
- CocoaLumberjack/Swift (3.6.0):
- Alamofire (5.0.2)
- CocoaLumberjack/Core (3.6.1)
- CocoaLumberjack/Swift (3.6.1):
- CocoaLumberjack/Core
- Crashlytics (3.14.0):
- Fabric (~> 1.10.2)
- Fabric (1.10.2)
- LetsMove (1.24)
- RxCocoa (5.0.1):
- RxCocoa (5.1.0):
- RxRelay (~> 5)
- RxSwift (~> 5)
- RxRelay (5.0.1):
- RxRelay (5.1.0):
- RxSwift (~> 5)
- RxSwift (5.0.1)
- Sparkle (1.22.0)
- RxSwift (5.1.0)
- Sparkle (1.23.0)
- Starscream (3.1.1)
- SwiftyJSON (5.0.0)
- WebViewJavascriptBridge (6.0.3)
DEPENDENCIES:
- Alamofire (~> 5.0.0-rc.3)
- Alamofire (~> 5.0)
- CocoaLumberjack/Swift
- Crashlytics
- Fabric
@ -36,31 +36,30 @@ SPEC REPOS:
- Alamofire
- CocoaLumberjack
- Crashlytics
- Fabric
- LetsMove
- RxCocoa
- RxRelay
- RxSwift
- Sparkle
- Starscream
- SwiftyJSON
- WebViewJavascriptBridge
https://github.com/cocoapods/specs.git:
- Fabric
- RxSwift
SPEC CHECKSUMS:
Alamofire: ca8c0de6906873be89d6deec5c8de279e00bf872
CocoaLumberjack: 78b0c238666f4f58db069738ec176f4519557516
Alamofire: 3ba7a4db18b4f62c4a1c0e1cb39d7f3d52e10ada
CocoaLumberjack: b17ae15142558d08bbacf69775fa10c4abbebcc9
Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5
RxCocoa: e741b9749968e8a143e2b787f1dfbff2b63d0a5c
RxRelay: 89d54507f4fd4d969e6ec1d4bd7f3673640b4640
RxSwift: e2dc62b366a3adf6a0be44ba9f405efd4c94e0c4
Sparkle: 593ac2e677c07bcb6c3b22d621240e7cbedaab57
RxCocoa: 13d2a4d7546a34b8ececae8c281e4ea1dbb94f2b
RxRelay: a168bd6caf712d00c676ac344e9295afc93b418e
RxSwift: ad5874f24bb0dbffd1e9bb8443604e3578796c7a
Sparkle: 55b1a87ba69d56913375a281546b7c82dec95bb0
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7
WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29
PODFILE CHECKSUM: 8dd9c30013fa97b5c735e427620c41790488a2a1
PODFILE CHECKSUM: b104b5e59f0be7cdf17282b19ab5917132171e6f
COCOAPODS: 1.7.5

File diff suppressed because it is too large Load Diff

View File

@ -35,7 +35,7 @@ struct Bag<T> : CustomDebugStringConvertible {
typealias Entry = (key: BagKey, value: T)
fileprivate var _nextKey: BagKey = BagKey(rawValue: 0)
private var _nextKey: BagKey = BagKey(rawValue: 0)
// data

View File

@ -10,7 +10,7 @@ struct PriorityQueue<Element> {
private let _hasHigherPriority: (Element, Element) -> Bool
private let _isEqual: (Element, Element) -> Bool
fileprivate var _elements = [Element]()
private var _elements = [Element]()
init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) {
_hasHigherPriority = hasHigherPriority

View File

@ -186,7 +186,7 @@ If you wish to build RxSwift as a Static Library using Carthage you may use the
```bash
carthage update RxSwift --platform iOS --no-build
sed -i -e 's/MACH_O_TYPE = mh_dylib/MACH_O_TYPE = staticlib/g' Carthage/Checkouts/RxSwift/Rx.xcodeproj/project.pbxproj
carthage build RxAlamofire --platform iOS
carthage build RxSwift --platform iOS
```
### [Swift Package Manager](https://github.com/apple/swift-package-manager)
@ -241,4 +241,4 @@ $ git submodule add git@github.com:ReactiveX/RxSwift.git
* [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc)
* [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf)
* [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/)
* [Haskell](https://www.haskell.org/)
* [Haskell](https://www.haskell.org/)

View File

@ -26,8 +26,8 @@
/// Parent object associated with delegate proxy.
private weak var _parentObject: ParentObject?
fileprivate let _currentDelegateFor: (ParentObject) -> AnyObject?
fileprivate let _setCurrentDelegateTo: (AnyObject?, ParentObject) -> Void
private let _currentDelegateFor: (ParentObject) -> AnyObject?
private let _setCurrentDelegateTo: (AnyObject?, ParentObject) -> Void
/// Initializes new instance.
///
@ -258,7 +258,7 @@
private let mainScheduler = MainScheduler()
fileprivate final class MessageDispatcher {
private final class MessageDispatcher {
private let dispatcher: PublishSubject<[Any]>
private let result: Observable<[Any]>

View File

@ -240,18 +240,18 @@ extension DelegateProxyType {
}
// fileprivate extensions
// private extensions
extension DelegateProxyType {
fileprivate static var factory: DelegateProxyFactory {
private static var factory: DelegateProxyFactory {
return DelegateProxyFactory.sharedFactory(for: self)
}
fileprivate static func assignedProxy(for object: ParentObject) -> AnyObject? {
private static func assignedProxy(for object: ParentObject) -> AnyObject? {
let maybeDelegate = objc_getAssociatedObject(object, self.identifier)
return castOptionalOrFatalError(maybeDelegate)
}
fileprivate static func assignProxy(_ proxy: AnyObject, toObject object: ParentObject) {
private static func assignProxy(_ proxy: AnyObject, toObject object: ParentObject) {
objc_setAssociatedObject(object, self.identifier, proxy, .OBJC_ASSOCIATION_RETAIN)
}
}

View File

@ -220,12 +220,6 @@ extension ObservableType {
fatalError()
}
}
extension UIWebView {
@available(*, unavailable, message: "createRxDelegateProxy is now unavailable, check DelegateProxyFactory")
public func createRxDelegateProxy() -> RxWebViewDelegateProxy {
fatalError()
}
}
#endif
#if os(macOS)
@ -407,8 +401,6 @@ extension Reactive where Base: UISegmentedControl {
}
#endif
import RxSwift
@available(*, deprecated, message: "Variable is deprecated. Please use `BehaviorRelay` as a replacement.")
extension Variable {
/// Converts `Variable` to `Driver` trait.

View File

@ -6,7 +6,11 @@
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if canImport(FoundationNetworking)
import struct FoundationNetworking.URLRequest
#else
import struct Foundation.URLRequest
#endif
/// Simple logging settings for RxCocoa library.
public struct Logging {

View File

@ -202,7 +202,7 @@ extension Reactive where Base: AnyObject {
}
}
fileprivate func registerMessageInterceptor<T: MessageInterceptorSubject>(_ selector: Selector) throws -> T {
private func registerMessageInterceptor<T: MessageInterceptorSubject>(_ selector: Selector) throws -> T {
let rxSelector = RX_selector(selector)
let selectorReference = RX_reference_from_selector(rxSelector)
@ -251,7 +251,7 @@ extension Reactive where Base: AnyObject {
var targetImplementation: IMP { get set }
}
fileprivate final class DeallocatingProxy
private final class DeallocatingProxy
: MessageInterceptorSubject
, RXDeallocatingObserver {
typealias Element = ()
@ -276,7 +276,7 @@ extension Reactive where Base: AnyObject {
}
}
fileprivate final class MessageSentProxy
private final class MessageSentProxy
: MessageInterceptorSubject
, RXMessageSentObserver {
typealias Element = [AnyObject]
@ -310,7 +310,7 @@ extension Reactive where Base: AnyObject {
#endif
fileprivate final class DeallocObservable {
private final class DeallocObservable {
let _subject = ReplaySubject<Void>.create(bufferSize:1)
init() {
@ -333,7 +333,7 @@ private protocol KVOObservableProtocol {
var options: KeyValueObservingOptions { get }
}
fileprivate final class KVOObserver
private final class KVOObserver
: _RXKVOObserver
, Disposable {
typealias Callback = (Any?) -> Void
@ -361,7 +361,7 @@ fileprivate final class KVOObserver
}
}
fileprivate final class KVOObservable<Element>
private final class KVOObservable<Element>
: ObservableType
, KVOObservableProtocol {
typealias Element = Element?
@ -397,7 +397,7 @@ fileprivate final class KVOObservable<Element>
}
fileprivate extension KeyValueObservingOptions {
private extension KeyValueObservingOptions {
var nsOptions: NSKeyValueObservingOptions {
var result: UInt = 0
if self.contains(.new) {
@ -438,7 +438,7 @@ fileprivate extension KeyValueObservingOptions {
return properyRuntimeInfo.range(of: ",W,") != nil
}
fileprivate extension ObservableType where Element == AnyObject? {
private extension ObservableType where Element == AnyObject? {
func finishWithNilWhenDealloc(_ target: NSObject)
-> Observable<AnyObject?> {
let deallocating = target.rx.deallocating

View File

@ -7,18 +7,26 @@
//
import struct Foundation.URL
import struct Foundation.URLRequest
import struct Foundation.Data
import struct Foundation.Date
import struct Foundation.TimeInterval
import class Foundation.HTTPURLResponse
import class Foundation.URLSession
import class Foundation.URLResponse
import class Foundation.JSONSerialization
import class Foundation.NSError
import var Foundation.NSURLErrorCancelled
import var Foundation.NSURLErrorDomain
#if canImport(FoundationNetworking)
import struct FoundationNetworking.URLRequest
import class FoundationNetworking.HTTPURLResponse
import class FoundationNetworking.URLSession
import class FoundationNetworking.URLResponse
#else
import struct Foundation.URLRequest
import class Foundation.HTTPURLResponse
import class Foundation.URLSession
import class Foundation.URLResponse
#endif
#if os(Linux)
// don't know why
import Foundation
@ -60,11 +68,11 @@ private func escapeTerminalString(_ value: String) -> String {
return value.replacingOccurrences(of: "\"", with: "\\\"", options:[], range: nil)
}
fileprivate func convertURLRequestToCurlCommand(_ request: URLRequest) -> String {
private func convertURLRequestToCurlCommand(_ request: URLRequest) -> String {
let method = request.httpMethod ?? "GET"
var returnValue = "curl -X \(method) "
if let httpBody = request.httpBody, request.httpMethod == "POST" {
if let httpBody = request.httpBody, request.httpMethod == "POST" || request.httpMethod == "PUT" {
let maybeBody = String(data: httpBody, encoding: String.Encoding.utf8)
if let body = maybeBody {
returnValue += "-d \"\(escapeTerminalString(body))\" "

View File

@ -20,10 +20,9 @@ public protocol ControlEventType : ObservableType {
Properties:
- it never fails,
- it doesnt send any initial value on subscription,
- it `Complete`s the sequence when the control deallocates,
- it never errors out, and
- it never errors out
- it delivers events on `MainScheduler.instance`.
**The implementation of `ControlEvent` will ensure that sequence of events is being subscribed on main scheduler
@ -33,7 +32,7 @@ public protocol ControlEventType : ObservableType {
**If they arent, using this trait will communicate wrong properties, and could potentially break someones code.**
**If the `events` observable sequence passed into thr initializer doesnt satisfy all enumerated
**If the `events` observable sequence passed into the initializer doesnt satisfy all enumerated
properties, dont use this trait.**
*/
public struct ControlEvent<PropertyType> : ControlEventType {

View File

@ -23,7 +23,6 @@ public protocol ControlPropertyType : ObservableType, ObserverType {
It's properties are:
- it never fails
- `shareReplay(1)` behavior
- it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
- it will `Complete` sequence on control being deallocated

View File

@ -25,6 +25,30 @@ extension SharedSequenceConvertibleType {
}
}
// MARK: compactMap
extension SharedSequenceConvertibleType {
/**
Projects each element of an observable sequence into an optional form and filters all optional results.
Equivalent to:
func compactMap<Result>(_ transform: @escaping (Self.E) -> Result?) -> SharedSequence<SharingStrategy, Result> {
return self.map(transform).filter { $0 != nil }.map { $0! }
}
- parameter transform: A transform function to apply to each source element and which returns an element or nil.
- returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source.
*/
public func compactMap<Result>(_ selector: @escaping (Element) -> Result?) -> SharedSequence<SharingStrategy, Result> {
let source = self
.asObservable()
.compactMap(selector)
return SharedSequence<SharingStrategy, Result>(source)
}
}
// MARK: filter
extension SharedSequenceConvertibleType {
/**

View File

@ -37,8 +37,8 @@ public struct SharedSequence<SharingStrategy: SharingStrategyProtocol, Element>
By defining `EXPANDABLE_SHARED_SEQUENCE` one agrees that it's up to him to ensure shared sequence
properties are preserved after extension.
*/
public static func createUnsafe<Source: ObservableType>(source: Source) -> SharedSequence<Sequence, Source.Element> {
return SharedSequence<Sequence, Source.Element>(raw: source.asObservable())
public static func createUnsafe<Source: ObservableType>(source: Source) -> SharedSequence<SharingStrategy, Source.Element> {
return SharedSequence<SharingStrategy, Source.Element>(raw: source.asObservable())
}
#endif

View File

@ -17,10 +17,10 @@ extension UICollectionView: HasPrefetchDataSource {
}
@available(iOS 10.0, tvOS 10.0, *)
fileprivate let collectionViewPrefetchDataSourceNotSet = CollectionViewPrefetchDataSourceNotSet()
private let collectionViewPrefetchDataSourceNotSet = CollectionViewPrefetchDataSourceNotSet()
@available(iOS 10.0, tvOS 10.0, *)
fileprivate final class CollectionViewPrefetchDataSourceNotSet
private final class CollectionViewPrefetchDataSourceNotSet
: NSObject
, UICollectionViewDataSourcePrefetching {
@ -48,7 +48,7 @@ open class RxCollectionViewDataSourcePrefetchingProxy
self.register { RxCollectionViewDataSourcePrefetchingProxy(collectionView: $0) }
}
fileprivate var _prefetchItemsPublishSubject: PublishSubject<[IndexPath]>?
private var _prefetchItemsPublishSubject: PublishSubject<[IndexPath]>?
/// Optimized version used for observing prefetch items callbacks.
internal var prefetchItemsPublishSubject: PublishSubject<[IndexPath]> {

View File

@ -15,9 +15,9 @@ extension UICollectionView: HasDataSource {
public typealias DataSource = UICollectionViewDataSource
}
fileprivate let collectionViewDataSourceNotSet = CollectionViewDataSourceNotSet()
private let collectionViewDataSourceNotSet = CollectionViewDataSourceNotSet()
fileprivate final class CollectionViewDataSourceNotSet
private final class CollectionViewDataSourceNotSet
: NSObject
, UICollectionViewDataSource {

View File

@ -38,8 +38,8 @@ open class RxScrollViewDelegateProxy
self.register { RxTextViewDelegateProxy(textView: $0) }
}
fileprivate var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
fileprivate var _contentOffsetPublishSubject: PublishSubject<()>?
private var _contentOffsetBehaviorSubject: BehaviorSubject<CGPoint>?
private var _contentOffsetPublishSubject: PublishSubject<()>?
/// Optimized version used for observing content offset changes.
internal var contentOffsetBehaviorSubject: BehaviorSubject<CGPoint> {

View File

@ -17,10 +17,10 @@ extension UITableView: HasPrefetchDataSource {
}
@available(iOS 10.0, tvOS 10.0, *)
fileprivate let tableViewPrefetchDataSourceNotSet = TableViewPrefetchDataSourceNotSet()
private let tableViewPrefetchDataSourceNotSet = TableViewPrefetchDataSourceNotSet()
@available(iOS 10.0, tvOS 10.0, *)
fileprivate final class TableViewPrefetchDataSourceNotSet
private final class TableViewPrefetchDataSourceNotSet
: NSObject
, UITableViewDataSourcePrefetching {
@ -48,7 +48,7 @@ open class RxTableViewDataSourcePrefetchingProxy
self.register { RxTableViewDataSourcePrefetchingProxy(tableView: $0) }
}
fileprivate var _prefetchRowsPublishSubject: PublishSubject<[IndexPath]>?
private var _prefetchRowsPublishSubject: PublishSubject<[IndexPath]>?
/// Optimized version used for observing prefetch rows callbacks.
internal var prefetchRowsPublishSubject: PublishSubject<[IndexPath]> {

View File

@ -15,9 +15,9 @@ extension UITableView: HasDataSource {
public typealias DataSource = UITableViewDataSource
}
fileprivate let tableViewDataSourceNotSet = TableViewDataSourceNotSet()
private let tableViewDataSourceNotSet = TableViewDataSourceNotSet()
fileprivate final class TableViewDataSourceNotSet
private final class TableViewDataSourceNotSet
: NSObject
, UITableViewDataSource {
@ -50,7 +50,7 @@ open class RxTableViewDataSourceProxy
self.register { RxTableViewDataSourceProxy(tableView: $0) }
}
fileprivate weak var _requiredMethodsDataSource: UITableViewDataSource? = tableViewDataSourceNotSet
private weak var _requiredMethodsDataSource: UITableViewDataSource? = tableViewDataSourceNotSet
// MARK: delegate

View File

@ -1,38 +0,0 @@
//
// RxWebViewDelegateProxy.swift
// RxCocoa
//
// Created by Andrew Breckenridge on 9/26/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension UIWebView: HasDelegate {
public typealias Delegate = UIWebViewDelegate
}
open class RxWebViewDelegateProxy
: DelegateProxy<UIWebView, UIWebViewDelegate>
, DelegateProxyType
, UIWebViewDelegate {
/// Typed parent object.
public weak private(set) var webView: UIWebView?
/// - parameter webView: Parent object for delegate proxy.
public init(webView: ParentObject) {
self.webView = webView
super.init(parentObject: webView, delegateProxy: RxWebViewDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxWebViewDelegateProxy(webView: $0) }
}
}
#endif

View File

@ -11,7 +11,7 @@
import UIKit
import RxSwift
fileprivate var rx_tap_key: UInt8 = 0
private var rx_tap_key: UInt8 = 0
extension Reactive where Base: UIBarButtonItem {

View File

@ -56,7 +56,7 @@ extension Reactive where Base: UICollectionView {
- parameter cellIdentifier: Identifier used to dequeue cells.
- parameter source: Observable sequence of items.
- parameter configureCell: Transform between sequence elements and view cells.
- parameter cellType: Type of table view cell.
- parameter cellType: Type of collection view cell.
- returns: Disposable object that can be used to unbind.
Example

View File

@ -50,6 +50,13 @@ extension Reactive where Base: UITextField {
}
)
}
/// Bindable sink for `isSecureTextEntry` property.
public var isSecureTextEntry: Binder<Bool> {
return Binder(self.base) { textField, isSecureTextEntry in
textField.isSecureTextEntry = isSecureTextEntry
}
}
}

View File

@ -1,46 +0,0 @@
//
// UIWebView+Rx.swift
// RxCocoa
//
// Created by Andrew Breckenridge on 8/30/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import UIKit
import RxSwift
extension Reactive where Base: UIWebView {
/// Reactive wrapper for `delegate`.
/// For more information take a look at `DelegateProxyType` protocol documentation.
public var delegate: DelegateProxy<UIWebView, UIWebViewDelegate> {
return RxWebViewDelegateProxy.proxy(for: base)
}
/// Reactive wrapper for `delegate` message.
public var didStartLoad: Observable<Void> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webViewDidStartLoad(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var didFinishLoad: Observable<Void> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webViewDidFinishLoad(_:)))
.map { _ in }
}
/// Reactive wrapper for `delegate` message.
public var didFailLoad: Observable<Error> {
return delegate
.methodInvoked(#selector(UIWebViewDelegate.webView(_:didFailLoadWithError:)))
.map { a in
return try castOrThrow(Error.self, a[1])
}
}
}
#endif

View File

@ -16,7 +16,7 @@ extension Reactive where Base: NSSlider {
/// Reactive wrapper for `value` property.
public var value: ControlProperty<Double> {
return self.base.rx.controlProperty(
getter: { control in
getter: { control -> Double in
return control.doubleValue
},
setter: { control, value in

View File

@ -186,7 +186,7 @@ If you wish to build RxSwift as a Static Library using Carthage you may use the
```bash
carthage update RxSwift --platform iOS --no-build
sed -i -e 's/MACH_O_TYPE = mh_dylib/MACH_O_TYPE = staticlib/g' Carthage/Checkouts/RxSwift/Rx.xcodeproj/project.pbxproj
carthage build RxAlamofire --platform iOS
carthage build RxSwift --platform iOS
```
### [Swift Package Manager](https://github.com/apple/swift-package-manager)
@ -241,4 +241,4 @@ $ git submodule add git@github.com:ReactiveX/RxSwift.git
* [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc)
* [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf)
* [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/)
* [Haskell](https://www.haskell.org/)
* [Haskell](https://www.haskell.org/)

View File

@ -35,7 +35,7 @@ struct Bag<T> : CustomDebugStringConvertible {
typealias Entry = (key: BagKey, value: T)
fileprivate var _nextKey: BagKey = BagKey(rawValue: 0)
private var _nextKey: BagKey = BagKey(rawValue: 0)
// data

View File

@ -10,7 +10,7 @@ struct PriorityQueue<Element> {
private let _hasHigherPriority: (Element, Element) -> Bool
private let _isEqual: (Element, Element) -> Bool
fileprivate var _elements = [Element]()
private var _elements = [Element]()
init(hasHigherPriority: @escaping (Element, Element) -> Bool, isEqual: @escaping (Element, Element) -> Bool) {
_hasHigherPriority = hasHigherPriority

View File

@ -186,7 +186,7 @@ If you wish to build RxSwift as a Static Library using Carthage you may use the
```bash
carthage update RxSwift --platform iOS --no-build
sed -i -e 's/MACH_O_TYPE = mh_dylib/MACH_O_TYPE = staticlib/g' Carthage/Checkouts/RxSwift/Rx.xcodeproj/project.pbxproj
carthage build RxAlamofire --platform iOS
carthage build RxSwift --platform iOS
```
### [Swift Package Manager](https://github.com/apple/swift-package-manager)
@ -241,4 +241,4 @@ $ git submodule add git@github.com:ReactiveX/RxSwift.git
* [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc)
* [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf)
* [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/)
* [Haskell](https://www.haskell.org/)
* [Haskell](https://www.haskell.org/)

View File

@ -179,7 +179,7 @@ public final class Variable<Element> {
private var _value: Element
#if DEBUG
fileprivate let _synchronizationTracker = SynchronizationTracker()
private let _synchronizationTracker = SynchronizationTracker()
#endif
/// Gets or sets current value of variable.

View File

@ -9,7 +9,7 @@
/// Represents an Action-based disposable.
///
/// When dispose method is called, disposal action will be dereferenced.
fileprivate final class AnonymousDisposable : DisposeBase, Cancelable {
private final class AnonymousDisposable : DisposeBase, Cancelable {
public typealias DisposeAction = () -> Void
private let _isDisposed = AtomicInt(0)
@ -23,7 +23,7 @@ fileprivate final class AnonymousDisposable : DisposeBase, Cancelable {
/// Constructs a new disposable with the given action used for disposal.
///
/// - parameter disposeAction: Disposal action which will be run upon calling `dispose`.
fileprivate init(_ disposeAction: @escaping DisposeAction) {
private init(_ disposeAction: @escaping DisposeAction) {
self._disposeAction = disposeAction
super.init()
}

View File

@ -32,8 +32,8 @@ public final class DisposeBag: DisposeBase {
private var _lock = SpinLock()
// state
fileprivate var _disposables = [Disposable]()
fileprivate var _isDisposed = false
private var _disposables = [Disposable]()
private var _isDisposed = false
/// Constructs new empty dispose bag.
public override init() {

View File

@ -9,11 +9,11 @@
/// Represents a disposable that does nothing on disposal.
///
/// Nop = No Operation
fileprivate struct NopDisposable : Disposable {
private struct NopDisposable : Disposable {
fileprivate static let noOp: Disposable = NopDisposable()
fileprivate init() {
private init() {
}

View File

@ -13,7 +13,7 @@ If an underlying disposable resource has already been set, future attempts to se
*/
public final class SingleAssignmentDisposable : DisposeBase, Cancelable {
fileprivate enum DisposeState: Int32 {
private enum DisposeState: Int32 {
case disposed = 1
case disposableSet = 2
}

View File

@ -17,7 +17,7 @@ public enum RxError
case unknown
/// Performing an action on disposed object.
case disposed(object: AnyObject)
/// Aritmetic overflow error.
/// Arithmetic overflow error.
case overflow
/// Argument out of range error.
case argumentOutOfRange

View File

@ -94,7 +94,7 @@ public protocol EventConvertible {
/// Type of element in event
associatedtype Element
@available(*, deprecated, message: "Use `Element` instead.")
@available(*, deprecated, renamed: "Element")
typealias ElementType = Element
/// Event representation of this instance

View File

@ -1,22 +0,0 @@
//
// String+Rx.swift
// RxSwift
//
// Created by Krunoslav Zaher on 12/25/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
extension String {
/// This is needed because on Linux Swift doesn't have `rangeOfString(..., options: .BackwardsSearch)`
func lastIndexOf(_ character: Character) -> Index? {
var index = self.endIndex
while index > self.startIndex {
index = self.index(before: index)
if self[index] == character {
return index
}
}
return nil
}
}

View File

@ -29,13 +29,5 @@ public class Observable<Element> : ObservableType {
_ = Resources.decrementTotal()
#endif
}
// this is kind of ugly I know :(
// Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_()_/¯
/// Optimizations for map operator
internal func composeMap<Result>(_ transform: @escaping (Element) throws -> Result) -> Observable<Result> {
return _map(source: self, transform: transform)
}
}

View File

@ -11,7 +11,7 @@ public protocol ObservableConvertibleType {
/// Type of elements in sequence.
associatedtype Element
@available(*, deprecated, message: "Use `Element` instead.")
@available(*, deprecated, renamed: "Element")
typealias E = Element
/// Converts `self` to `Observable` sequence.

View File

@ -89,14 +89,17 @@ extension Hooks {
public typealias DefaultErrorHandler = (_ subscriptionCallStack: [String], _ error: Error) -> Void
public typealias CustomCaptureSubscriptionCallstack = () -> [String]
fileprivate static let _lock = RecursiveLock()
fileprivate static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in
private static let _lock = RecursiveLock()
private static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in
#if DEBUG
let serializedCallStack = subscriptionCallStack.joined(separator: "\n")
print("Unhandled error happened: \(error)\n subscription called from:\n\(serializedCallStack)")
print("Unhandled error happened: \(error)")
if !serializedCallStack.isEmpty {
print("subscription called from:\n\(serializedCallStack)")
}
#endif
}
fileprivate static var _customCaptureSubscriptionCallstack: CustomCaptureSubscriptionCallstack = {
private static var _customCaptureSubscriptionCallstack: CustomCaptureSubscriptionCallstack = {
#if DEBUG
return Thread.callStackSymbols
#else

View File

@ -39,7 +39,7 @@ extension ObservableType {
}
}
fileprivate enum AmbState {
private enum AmbState {
case neither
case left
case right
@ -51,7 +51,7 @@ final private class AmbObserver<Observer: ObserverType>: ObserverType {
typealias This = AmbObserver<Observer>
typealias Sink = (This, Event<Element>) -> Void
fileprivate let _parent: Parent
private let _parent: Parent
fileprivate var _sink: Sink
fileprivate var _cancel: Disposable

View File

@ -6,7 +6,7 @@
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
fileprivate final class AsMaybeSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
private final class AsMaybeSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
typealias Element = Observer.Element
private var _element: Event<Element>?
@ -34,7 +34,7 @@ fileprivate final class AsMaybeSink<Observer: ObserverType> : Sink<Observer>, Ob
}
final class AsMaybe<Element>: Producer<Element> {
fileprivate let _source: Observable<Element>
private let _source: Observable<Element>
init(source: Observable<Element>) {
self._source = source

View File

@ -6,7 +6,7 @@
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
//
fileprivate final class AsSingleSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
private final class AsSingleSink<Observer: ObserverType> : Sink<Observer>, ObserverType {
typealias Element = Observer.Element
private var _element: Event<Element>?
@ -37,7 +37,7 @@ fileprivate final class AsSingleSink<Observer: ObserverType> : Sink<Observer>, O
}
final class AsSingle<Element>: Producer<Element> {
fileprivate let _source: Observable<Element>
private let _source: Observable<Element>
init(source: Observable<Element>) {
self._source = source

View File

@ -30,7 +30,7 @@ final private class AnonymousObservableSink<Observer: ObserverType>: Sink<Observ
private let _isStopped = AtomicInt(0)
#if DEBUG
fileprivate let _synchronizationTracker = SynchronizationTracker()
private let _synchronizationTracker = SynchronizationTracker()
#endif
override init(observer: Observer, cancel: Cancelable) {

View File

@ -26,9 +26,9 @@ extension ObservableType {
}
}
fileprivate let dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
private let dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
fileprivate func logEvent(_ identifier: String, dateFormat: DateFormatter, content: String) {
private func logEvent(_ identifier: String, dateFormat: DateFormatter, content: String) {
print("\(dateFormat.string(from: Date())): \(identifier) -> \(content)")
}
@ -75,7 +75,7 @@ final private class DebugSink<Source: ObservableType, Observer: ObserverType>: S
final private class Debug<Source: ObservableType>: Producer<Source.Element> {
fileprivate let _identifier: String
fileprivate let _trimOutput: Bool
fileprivate let _source: Source
private let _source: Source
init(source: Source, identifier: String?, trimOutput: Bool, file: String, line: UInt, function: String) {
self._trimOutput = trimOutput
@ -84,7 +84,7 @@ final private class Debug<Source: ObservableType>: Producer<Source.Element> {
}
else {
let trimmedFile: String
if let lastIndex = file.lastIndexOf("/") {
if let lastIndex = file.lastIndex(of: "/") {
trimmedFile = String(file[file.index(after: lastIndex) ..< file.endIndex])
}
else {

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