Update Cocoapods.

This commit is contained in:
Matthew Chen 2018-11-30 09:30:20 -05:00
parent 24b0c88968
commit 71a3d8c660
43 changed files with 4976 additions and 4496 deletions

View File

@ -49,16 +49,16 @@ PODS:
- Mantle (2.1.0):
- Mantle/extobjc (= 2.1.0)
- Mantle/extobjc (2.1.0)
- PromiseKit (6.5.2):
- PromiseKit/CorePromise (= 6.5.2)
- PromiseKit/Foundation (= 6.5.2)
- PromiseKit/UIKit (= 6.5.2)
- PromiseKit/CorePromise (6.5.2)
- PromiseKit/Foundation (6.5.2):
- PromiseKit (6.5.3):
- PromiseKit/CorePromise (= 6.5.3)
- PromiseKit/Foundation (= 6.5.3)
- PromiseKit/UIKit (= 6.5.3)
- PromiseKit/CorePromise (6.5.3)
- PromiseKit/Foundation (6.5.3):
- PromiseKit/CorePromise
- PromiseKit/UIKit (6.5.2):
- PromiseKit/UIKit (6.5.3):
- PromiseKit/CorePromise
- PureLayout (3.0.2)
- PureLayout (3.1.4)
- Reachability (3.2)
- SAMKeychain (1.5.3)
- SignalCoreKit (1.0.0):
@ -119,8 +119,8 @@ PODS:
- SQLCipher/common (3.4.2)
- SQLCipher/standard (3.4.2):
- SQLCipher/common
- SSZipArchive (2.1.3)
- SwiftProtobuf (1.1.2)
- SSZipArchive (2.1.4)
- SwiftProtobuf (1.2.0)
- YapDatabase/SQLCipher (3.1.1):
- YapDatabase/SQLCipher/Core (= 3.1.1)
- YapDatabase/SQLCipher/Extensions (= 3.1.1)
@ -265,10 +265,10 @@ CHECKOUT OPTIONS:
:commit: 3e0c2371d125f2d3db26daa498d5d436961b1795
:git: https://github.com/signalapp/HKDFKit.git
SignalCoreKit:
:commit: b60dc7d58dfc93ca6eafbb3ea5300c6d67ebc69a
:commit: a84ec7ed6c13b079a7e03cb09c79b5452086d1e7
:git: https://github.com/signalapp/SignalCoreKit.git
SignalMetadataKit:
:commit: 8a586363921b4546bea99b07c06bf5c93eab7973
:commit: 56f28fc3a6e35d548d034ef7d0009f233ca0aa62
:git: https://github.com/signalapp/SignalMetadataKit
SocketRocket:
:commit: 9f9563a83cd8960503074aa8de72206f83fb7a69
@ -289,8 +289,8 @@ SPEC CHECKSUMS:
HKDFKit: 3b6dbbb9d59c221cc6c52c3aa915700cbf24e376
libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa
Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b
PromiseKit: 27c1601bfb73405871b805bcb8cf7e55c4dad3db
PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd
PromiseKit: c609029bdd801f792551a504c695c7d3098b42cd
PureLayout: f08c01b8dec00bb14a1fefa3de4c7d9c265df85e
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SignalCoreKit: c2d8132cdedb95d35eb2f8ae7eac0957695d0a8b
@ -298,8 +298,8 @@ SPEC CHECKSUMS:
SignalServiceKit: 80d774c32b22567682f63c36bf9da265d82083bb
SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
SQLCipher: f9fcf29b2e59ced7defc2a2bdd0ebe79b40d4990
SSZipArchive: 51a800ebb77f95a8329b6ced1faaff394b47f509
SwiftProtobuf: 7147b8ec19c8c0694a45155d989ea9e6dedaf51f
SSZipArchive: 41455d4b8d2b6ab93990820b50dc697c2554a322
SwiftProtobuf: 91a9856079044ef4ec762b2344c763cd9e5a73c1
YapDatabase: b418a4baa6906e8028748938f9159807fd039af4
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,11 @@ For Carthage, SwiftPM, etc., or for instructions when using older Swifts or
Xcodes, see our [Installation Guide]. We
recommend [Carthage](https://github.com/Carthage/Carthage).
# PromiseKit is Thousands of Hours of Work
This project is a labor of love: to the pursuit of making quality, reusable, compartmentalized software that has general use
to the wider development community. It is a widely used project where every change, fix and plan can have serious repercussions to its users and thus must be carefully considered. If you use PromiseKit in your apps, please consider supporting its development with a donation to [my patreon](https://www.patreon.com/mxcl). Thank you.
# Documentation
* Handbook

View File

@ -5,7 +5,7 @@ enum Sealant<R> {
case resolved(R)
}
class Handlers<R> {
final class Handlers<R> {
var bodies: [(R) -> Void] = []
func append(_ item: @escaping(R) -> Void) { bodies.append(item) }
}
@ -17,7 +17,7 @@ class Box<T> {
func seal(_: T) {}
}
class SealedBox<T>: Box<T> {
final class SealedBox<T>: Box<T> {
let value: T
init(value: T) {
@ -29,7 +29,7 @@ class SealedBox<T>: Box<T> {
}
}
class EmptyBox<T>: Box<T> {
final class EmptyBox<T>: Box<T> {
private var sealant = Sealant<T>.pending(.init())
private let barrier = DispatchQueue(label: "org.promisekit.barrier", attributes: .concurrent)

View File

@ -5,7 +5,7 @@ import Dispatch
A `Guarantee` is a functional abstraction around an asynchronous operation that cannot error.
- See: `Thenable`
*/
public class Guarantee<T>: Thenable {
public final class Guarantee<T>: Thenable {
let box: Box<T>
fileprivate init(box: SealedBox<T>) {

View File

@ -5,7 +5,7 @@ import Dispatch
A `Promise` is a functional abstraction around a failable asynchronous operation.
- See: `Thenable`
*/
public class Promise<T>: Thenable, CatchMixin {
public final class Promise<T>: Thenable, CatchMixin {
let box: Box<Result<T>>
fileprivate init(box: SealedBox<Result<T>>) {

View File

@ -1,5 +1,5 @@
/// An object for resolving promises
public class Resolver<T> {
public final class Resolver<T> {
let box: Box<Result<T>>
init(_ box: Box<Result<T>>) {

View File

@ -69,6 +69,29 @@ PL__ASSUME_NONNULL_BEGIN
#endif /* PL__PureLayout_MinBaseSDK_iOS_8_0 */
#if TARGET_OS_IPHONE
#pragma mark Pin Edges to SafeArea
/** Pins the given edge of the view to the same edge of its superview anchor/edge. */
- (NSLayoutConstraint *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgeToSuperviewSafeArea:(ALEdge)edge;
/** Pins the given edge of the view to the same edge of its superview anchor/edge with an inset. */
- (NSLayoutConstraint *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgeToSuperviewSafeArea:(ALEdge)edge withInset:(CGFloat)inset;
/** Pins the given edge of the view to the same edge of its superview anchor/edge with an inset as a maximum or minimum. */
- (NSLayoutConstraint *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgeToSuperviewSafeArea:(ALEdge)edge withInset:(CGFloat)inset relation:(NSLayoutRelation)relation;
/** Pins the edges of the view to the edges of its superview anchors/edge. */
- (PL__NSArray_of(NSLayoutConstraint *) *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgesToSuperviewSafeArea;
/** Pins the edges of the view to the edges of its superview anchors/edges with the given edge insets. */
- (PL__NSArray_of(NSLayoutConstraint *) *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgesToSuperviewSafeAreaWithInsets:(ALEdgeInsets)insets;
/** Pins 3 of the 4 edges of the view to the edges of its superview anchor/edge with the given edge insets, excluding one edge. */
- (PL__NSArray_of(NSLayoutConstraint *) *)API_AVAILABLE(ios(9.0), tvos(9.0))autoPinEdgesToSuperviewSafeAreaWithInsets:(ALEdgeInsets)insets excludingEdge:(ALEdge)edge;
#endif /* TARGET_OS_IPHONE */
#pragma mark Pin Edges to Superview
@ -95,12 +118,18 @@ PL__ASSUME_NONNULL_BEGIN
/** Pins the given edge of the view to the corresponding margin of its superview. Available in iOS 8.0 and later. */
- (NSLayoutConstraint *)autoPinEdgeToSuperviewMargin:(ALEdge)edge;
/** Pins the given edge of a view to the corresponding margin of its superview with an inset.*/
- (NSLayoutConstraint *)autoPinEdgeToSuperviewMargin:(ALEdge)edge withInset:(CGFloat)inset;
/** Pins the given edge of the view to the corresponding margin of its superview as a maximum or minimum. Available in iOS 8.0 and later. */
- (NSLayoutConstraint *)autoPinEdgeToSuperviewMargin:(ALEdge)edge relation:(NSLayoutRelation)relation;
/** Pins the edges of the view to the margins of its superview. Available in iOS 8.0 and later. */
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMargins;
/** Pins the edges of the view to the margins of its superview with the given edge insets. Available in iOS 8.0 and later.*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMarginsWithInsets:(ALEdgeInsets)insets;
/** Pins 3 of the 4 edges of the view to the margins of its superview excluding one edge. Available in iOS 8.0 and later. */
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMarginsExcludingEdge:(ALEdge)edge;

View File

@ -80,7 +80,7 @@
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoCenterInSuperview
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoAlignAxisToSuperviewAxis:ALAxisHorizontal]];
[constraints addObject:[self autoAlignAxisToSuperviewAxis:ALAxisVertical]];
return constraints;
@ -109,7 +109,7 @@
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoCenterInSuperviewMargins
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoAlignAxisToSuperviewMarginAxis:ALAxisHorizontal]];
[constraints addObject:[self autoAlignAxisToSuperviewMarginAxis:ALAxisVertical]];
return constraints;
@ -133,6 +133,234 @@
#endif /* PL__PureLayout_MinBaseSDK_iOS_8_0 */
#pragma mark Pin Edges to SafeArea
#if TARGET_OS_IPHONE
/**
Pins the given edge of the view to the same edge of its superview anchor.
@param edge The edge of this view and its superview to pin.
@return The constraint added.
*/
- (NSLayoutConstraint *)autoPinEdgeToSuperviewSafeArea:(ALEdge)edge
{
return [self autoPinEdgeToSuperviewSafeArea:edge withInset:0.0];
}
/**
Pins the given edge of the view to the same edge of its superview anchor with an inset.
@param edge The edge of this view and its superview to pin.
@param inset The amount to inset this view's edge from the superview's edge.
@return The constraint added.
*/
- (NSLayoutConstraint *)autoPinEdgeToSuperviewSafeArea:(ALEdge)edge withInset:(CGFloat)inset
{
return [self autoPinEdgeToSuperviewSafeArea:edge withInset:inset relation:NSLayoutRelationEqual];
}
/**
Pins the given edge of the view to the same edge of its superview anchor/edge with an inset as a maximum or minimum.
@param edge The edge of this view and its superview to pin.
@param inset The amount to inset this view's edge from the superview's edge.
@param relation Whether the inset should be at least, at most, or exactly equal to the given value.
@return The constraint added.
*/
- (NSLayoutConstraint *)autoPinEdgeToSuperviewSafeArea:(ALEdge)edge withInset:(CGFloat)inset relation:(NSLayoutRelation)relation
{
#if PL__PureLayout_MinBaseSDK_iOS_9_0
self.translatesAutoresizingMaskIntoConstraints = NO;
ALView *superview = self.superview;
NSAssert(superview, @"View's superview must not be nil.\nView: %@", self);
NSLayoutConstraint *constraint = nil;
NSLayoutYAxisAnchor *topAnchor;
NSLayoutYAxisAnchor *bottomAnchor;
NSLayoutXAxisAnchor *leftAnchor;
NSLayoutXAxisAnchor *rightAnchor;
NSLayoutXAxisAnchor *leadingAnchor;
NSLayoutXAxisAnchor *trailingAnchor;
#if PL__PureLayout_MinBaseSDK_iOS_11_0 // only iOS/tvOS SDK 11.0 has @available syntax introduced
if (@available(iOS 11.0, tvOS 11.0, *)) {
topAnchor = superview.safeAreaLayoutGuide.topAnchor;
bottomAnchor = superview.safeAreaLayoutGuide.bottomAnchor;
leftAnchor = superview.safeAreaLayoutGuide.leftAnchor;
rightAnchor = superview.safeAreaLayoutGuide.rightAnchor;
leadingAnchor = superview.safeAreaLayoutGuide.leadingAnchor;
trailingAnchor = superview.safeAreaLayoutGuide.trailingAnchor;
} else if (@available(iOS 9.0, *)) {
topAnchor = superview.topAnchor;
bottomAnchor = superview.bottomAnchor;
leftAnchor = superview.leftAnchor;
rightAnchor = superview.rightAnchor;
leadingAnchor = superview.leadingAnchor;
trailingAnchor = superview.trailingAnchor;
} else { // for targeting iOS 8 or below without anchor system
return [self autoPinEdgeToSuperviewEdge:edge withInset:inset relation:relation];
}
#elif PL__PureLayout_MinBaseSDK_iOS_9_0 // fallback to older SDKs, when using Xcode 8.0, which only has iOS SDK 10.0
if (PL__PureLayout_MinSysVer_iOS_9_0) {
topAnchor = superview.topAnchor;
bottomAnchor = superview.bottomAnchor;
leftAnchor = superview.leftAnchor;
rightAnchor = superview.rightAnchor;
leadingAnchor = superview.leadingAnchor;
trailingAnchor = superview.trailingAnchor;
} else { // for targeting iOS 8 or below without anchor system
return [self autoPinEdgeToSuperviewEdge:edge withInset:inset relation:relation];
}
#endif
if (edge == ALEdgeBottom || edge == ALEdgeRight || edge == ALEdgeTrailing) {
// The bottom, right, and trailing insets (and relations, if an inequality) are inverted to become offsets
inset = -inset;
}
switch (edge) {
case ALEdgeLeft:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self leftAnchor] constraintEqualToAnchor:leftAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self leftAnchor] constraintLessThanOrEqualToAnchor:leftAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self leftAnchor] constraintGreaterThanOrEqualToAnchor:leftAnchor constant:inset];
break;
}
break;
case ALEdgeRight:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self rightAnchor] constraintEqualToAnchor:rightAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self rightAnchor] constraintGreaterThanOrEqualToAnchor:rightAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self rightAnchor] constraintLessThanOrEqualToAnchor:rightAnchor constant:inset];
break;
}
break;
case ALEdgeTop:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self topAnchor] constraintEqualToAnchor:topAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self topAnchor] constraintLessThanOrEqualToAnchor:topAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self topAnchor] constraintGreaterThanOrEqualToAnchor:topAnchor constant:inset];
break;
}
break;
case ALEdgeBottom:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self bottomAnchor] constraintEqualToAnchor:bottomAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self bottomAnchor] constraintGreaterThanOrEqualToAnchor:bottomAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self bottomAnchor] constraintLessThanOrEqualToAnchor:bottomAnchor constant:inset];
break;
}
break;
case ALEdgeLeading:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self leadingAnchor] constraintEqualToAnchor:leadingAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self leadingAnchor] constraintLessThanOrEqualToAnchor:leadingAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self leadingAnchor] constraintGreaterThanOrEqualToAnchor:leadingAnchor constant:inset];
break;
}
break;
case ALEdgeTrailing:
switch (relation) {
case NSLayoutRelationEqual:
constraint = [[self trailingAnchor] constraintEqualToAnchor:trailingAnchor constant:inset];
break;
case NSLayoutRelationLessThanOrEqual:
constraint = [[self trailingAnchor] constraintGreaterThanOrEqualToAnchor:trailingAnchor constant:inset];
break;
case NSLayoutRelationGreaterThanOrEqual:
constraint = [[self trailingAnchor] constraintLessThanOrEqualToAnchor:trailingAnchor constant:inset];
break;
}
break;
}
constraint.active = YES;
return constraint;
#else
return [self autoPinEdgeToSuperviewEdge:edge withInset:inset relation:relation];
#endif /* PL__PureLayout_MinBaseSDK_iOS_9_0 */
}
/**
Pins the edges of the view to the edges of its superview anchor.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewSafeArea
{
return [self autoPinEdgesToSuperviewSafeAreaWithInsets:ALEdgeInsetsZero];
}
/**
Pins the edges of the view to the edges of its superview anchor with the given edge insets.
The insets.left corresponds to a leading edge constraint, and insets.right corresponds to a trailing edge constraint.
@param insets The insets for this view's edges from its superview's edges.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewSafeAreaWithInsets:(ALEdgeInsets)insets
{
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeBottom withInset:insets.bottom]];
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing withInset:insets.right]];
return constraints;
}
/**
Pins 3 of the 4 edges of the view to the edges of its superview anchor with the given edge insets, excluding one edge.
The insets.left corresponds to a leading edge constraint, and insets.right corresponds to a trailing edge constraint.
@param insets The insets for this view's edges from its superview's edges. The inset corresponding to the excluded edge
will be ignored.
@param edge The edge of this view to exclude in pinning to its superview anchor; this method will not apply any constraint to it.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewSafeAreaWithInsets:(ALEdgeInsets)insets excludingEdge:(ALEdge)edge
{
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
if (edge != ALEdgeTop) {
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeTop withInset:insets.top]];
}
if (edge != ALEdgeLeading && edge != ALEdgeLeft) {
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeLeading withInset:insets.left]];
}
if (edge != ALEdgeBottom) {
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeBottom withInset:insets.bottom]];
}
if (edge != ALEdgeTrailing && edge != ALEdgeRight) {
[constraints addObject:[self autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing withInset:insets.right]];
}
return constraints;
}
#endif /* TARGET_OS_IPHONE */
#pragma mark Pin Edges to Superview
/**
@ -186,7 +414,7 @@
/**
Pins the edges of the view to the edges of its superview.
@return An array of constraints added.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewEdges
{
@ -198,11 +426,11 @@
The insets.left corresponds to a leading edge constraint, and insets.right corresponds to a trailing edge constraint.
@param insets The insets for this view's edges from its superview's edges.
@return An array of constraints added.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewEdgesWithInsets:(ALEdgeInsets)insets
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
@ -217,22 +445,42 @@
@param insets The insets for this view's edges from its superview's edges. The inset corresponding to the excluded edge
will be ignored.
@param edge The edge of this view to exclude in pinning to its superview; this method will not apply any constraint to it.
@return An array of constraints added.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewEdgesWithInsets:(ALEdgeInsets)insets excludingEdge:(ALEdge)edge
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
if (edge != ALEdgeTop) {
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
}
if (edge != ALEdgeLeading && edge != ALEdgeLeft) {
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:insets.left]];
}
if (edge != ALEdgeBottom) {
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
}
if (edge != ALEdgeTrailing && edge != ALEdgeRight) {
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:insets.right]];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
switch (edge) {
case ALEdgeLeft:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:insets.right]];
break;
case ALEdgeRight:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
break;
case ALEdgeTop:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:insets.right]];
break;
case ALEdgeBottom:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:insets.right]];
break;
case ALEdgeLeading:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:insets.right]];
break;
case ALEdgeTrailing:
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:insets.bottom]];
break;
}
return constraints;
}
@ -250,6 +498,26 @@
return [self autoPinEdgeToSuperviewMargin:edge relation:NSLayoutRelationEqual];
}
/**
Pins the given edge of the view to the corresponding margin of its superview with an inset.
@param edge The edge of this view to pin to the corresponding margin of its superview.
@param @param inset The amount to inset this view's edge from the corresponding margin of its superview edge.
@return The constraint added.
*/
- (NSLayoutConstraint *)autoPinEdgeToSuperviewMargin:(ALEdge)edge withInset:(CGFloat)inset
{
self.translatesAutoresizingMaskIntoConstraints = NO;
ALView *superview = self.superview;
NSAssert(superview, @"View's superview must not be nil.\nView: %@", self);
if (edge == ALEdgeBottom || edge == ALEdgeRight || edge == ALEdgeTrailing) {
// The bottom, right, and trailing insets (and relations, if an inequality) are inverted to become offsets
inset = -inset;
}
ALMargin margin = [NSLayoutConstraint al_marginForEdge:edge];
return [self autoConstrainAttribute:(ALAttribute)edge toAttribute:(ALAttribute)margin ofView:superview withOffset:inset];
}
/**
Pins the given edge of the view to the corresponding margin of its superview as a maximum or minimum.
@ -273,19 +541,31 @@
ALMargin margin = [NSLayoutConstraint al_marginForEdge:edge];
return [self autoConstrainAttribute:(ALAttribute)edge toAttribute:(ALAttribute)margin ofView:superview withOffset:0.0 relation:relation];
}
/**
Pins the edges of the view to the margins of its superview.
@return An array of constraints added.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMargins
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing]];
return [self autoPinEdgesToSuperviewMarginsWithInsets:ALEdgeInsetsZero];
}
/**
Pins the edges of the view to the edges of its corresponding margins of its superview with the given edge insets.
The insets.left corresponds to a leading edge constraint, and insets.right corresponds to a trailing edge constraint.
@param insets The insets for this view's edges from its corresponding margin of its superview.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMarginsWithInsets:(ALEdgeInsets)insets
{
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop withInset:insets.top]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading withInset:insets.left]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom withInset:insets.bottom]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing withInset:insets.right]];
return constraints;
}
@ -293,22 +573,42 @@
Pins 3 of the 4 edges of the view to the margins of its superview, excluding one edge.
@param edge The edge of this view to exclude in pinning to its superview; this method will not apply any constraint to it.
@return An array of constraints added.
@return An array of constraints added, ordered counterclockwise from top.
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoPinEdgesToSuperviewMarginsExcludingEdge:(ALEdge)edge
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
if (edge != ALEdgeTop) {
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
}
if (edge != ALEdgeLeading && edge != ALEdgeLeft) {
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading]];
}
if (edge != ALEdgeBottom) {
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
}
if (edge != ALEdgeTrailing && edge != ALEdgeRight) {
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing]];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
switch (edge) {
case ALEdgeLeft:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeRight]];
break;
case ALEdgeRight:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeft]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
break;
case ALEdgeTop:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing]];
break;
case ALEdgeBottom:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing]];
break;
case ALEdgeLeading:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTrailing]];
break;
case ALEdgeTrailing:
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeTop]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeLeading]];
[constraints addObject:[self autoPinEdgeToSuperviewMargin:ALEdgeBottom]];
break;
}
return constraints;
}
@ -360,7 +660,6 @@
return [self autoConstrainAttribute:(ALAttribute)edge toAttribute:(ALAttribute)toEdge ofView:otherView withOffset:offset relation:relation];
}
#pragma mark Align Axes
/**
@ -486,7 +785,7 @@
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoSetDimensionsToSize:(CGSize)size
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObject:[self autoSetDimension:ALDimensionWidth toSize:size.width]];
[constraints addObject:[self autoSetDimension:ALDimensionHeight toSize:size.height]];
return constraints;
@ -721,7 +1020,6 @@
#endif /* TARGET_OS_IPHONE */
#pragma mark Internal Methods
/**

View File

@ -121,7 +121,7 @@
- (PL__NSArray_of(NSLayoutConstraint *) *)autoAlignViewsToEdge:(ALEdge)edge
{
NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
ALView *previousView = nil;
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
@ -146,7 +146,7 @@
- (PL__NSArray_of(NSLayoutConstraint *) *)autoAlignViewsToAxis:(ALAxis)axis
{
NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
ALView *previousView = nil;
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
@ -171,7 +171,7 @@
- (PL__NSArray_of(NSLayoutConstraint *) *)autoMatchViewsDimension:(ALDimension)dimension
{
NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
ALView *previousView = nil;
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
@ -197,7 +197,7 @@
- (PL__NSArray_of(NSLayoutConstraint *) *)autoSetViewsDimension:(ALDimension)dimension toSize:(CGFloat)size
{
NSAssert([self al_containsMinimumNumberOfViews:1], @"This array must contain at least 1 view.");
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
ALView *view = (ALView *)object;
@ -217,7 +217,7 @@
*/
- (PL__NSArray_of(NSLayoutConstraint *) *)autoSetViewsDimensionsToSize:(CGSize)size
{
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
[constraints addObjectsFromArray:[self autoSetViewsDimension:ALDimensionWidth toSize:size.width]];
[constraints addObjectsFromArray:[self autoSetViewsDimension:ALDimensionHeight toSize:size.height]];
return constraints;
@ -311,7 +311,7 @@
CGFloat leadingSpacing = shouldSpaceInsets ? spacing : 0.0;
CGFloat trailingSpacing = shouldSpaceInsets ? spacing : 0.0;
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
ALView *previousView = nil;
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
@ -407,7 +407,7 @@
#endif /* TARGET_OS_IPHONE */
BOOL shouldFlipOrder = isRightToLeftLayout && (axis != ALAxisVertical); // imitate the effect of leading/trailing when distributing horizontally
__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSMutableArray_of(NSLayoutConstraint *) *constraints = [NSMutableArray new];
PL__NSArray_of(ALView *) *views = [self al_copyViewsOnly];
NSUInteger numberOfViews = [views count];
ALView *commonSuperview = [views al_commonSuperviewOfViews];
@ -493,7 +493,7 @@
*/
- (PL__NSArray_of(ALView *) *)al_copyViewsOnly
{
__NSMutableArray_of(ALView *) *viewsOnlyArray = [NSMutableArray arrayWithCapacity:[self count]];
PL__NSMutableArray_of(ALView *) *viewsOnlyArray = [NSMutableArray arrayWithCapacity:[self count]];
for (id object in self) {
if ([object isKindOfClass:[ALView class]]) {
[viewsOnlyArray addObject:object];

View File

@ -46,7 +46,7 @@
NOTE: Access to this variable is not synchronized (and should only be done on the main thread).
*/
static __NSMutableArray_of(__NSMutableArray_of(NSLayoutConstraint *) *) *_al_arraysOfCreatedConstraints = nil;
static PL__NSMutableArray_of(PL__NSMutableArray_of(NSLayoutConstraint *) *) *_al_arraysOfCreatedConstraints = nil;
/**
A global variable that is set to YES when installing a batch of constraints collected from a call to +[autoCreateAndInstallConstraints].
@ -59,7 +59,7 @@ static BOOL _al_isInstallingCreatedConstraints = NO;
/**
Accessor for the global state that stores arrays of constraints created without being installed.
*/
+ (__NSMutableArray_of(__NSMutableArray_of(NSLayoutConstraint *) *) *)al_arraysOfCreatedConstraints
+ (PL__NSMutableArray_of(PL__NSMutableArray_of(NSLayoutConstraint *) *) *)al_arraysOfCreatedConstraints
{
NSAssert([NSThread isMainThread], @"PureLayout is not thread safe, and must be used exclusively from the main thread.");
if (!_al_arraysOfCreatedConstraints) {
@ -71,7 +71,7 @@ static BOOL _al_isInstallingCreatedConstraints = NO;
/**
Accessor for the current mutable array of constraints created without being immediately installed.
*/
+ (__NSMutableArray_of(NSLayoutConstraint *) *)al_currentArrayOfCreatedConstraints
+ (PL__NSMutableArray_of(NSLayoutConstraint *) *)al_currentArrayOfCreatedConstraints
{
return [[self al_arraysOfCreatedConstraints] lastObject];
}
@ -138,12 +138,12 @@ static BOOL _al_isInstallingCreatedConstraints = NO;
constraints created by this library (even if automatic constraint installation is being prevented).
NOTE: Access to this variable is not synchronized (and should only be done on the main thread).
*/
static __NSMutableArray_of(NSNumber *) *_al_globalConstraintPriorities = nil;
static PL__NSMutableArray_of(NSNumber *) *_al_globalConstraintPriorities = nil;
/**
Accessor for the global stack of layout priorities.
*/
+ (__NSMutableArray_of(NSNumber *) *)al_globalConstraintPriorities
+ (PL__NSMutableArray_of(NSNumber *) *)al_globalConstraintPriorities
{
NSAssert([NSThread isMainThread], @"PureLayout is not thread safe, and must be used exclusively from the main thread.");
if (!_al_globalConstraintPriorities) {
@ -159,7 +159,7 @@ static __NSMutableArray_of(NSNumber *) *_al_globalConstraintPriorities = nil;
*/
+ (ALLayoutPriority)al_currentGlobalConstraintPriority
{
__NSMutableArray_of(NSNumber *) *globalConstraintPriorities = [self al_globalConstraintPriorities];
PL__NSMutableArray_of(NSNumber *) *globalConstraintPriorities = [self al_globalConstraintPriorities];
if ([globalConstraintPriorities count] == 0) {
return ALLayoutPriorityRequired;
}
@ -207,12 +207,12 @@ static __NSMutableArray_of(NSNumber *) *_al_globalConstraintPriorities = nil;
constraints created by this library (even if automatic constraint installation is being prevented).
NOTE: Access to this variable is not synchronized (and should only be done on the main thread).
*/
static __NSMutableArray_of(NSString *) *_al_globalConstraintIdentifiers = nil;
static PL__NSMutableArray_of(NSString *) *_al_globalConstraintIdentifiers = nil;
/**
Accessor for the global state of constraint identifiers.
*/
+ (__NSMutableArray_of(NSString *) *)al_globalConstraintIdentifiers
+ (PL__NSMutableArray_of(NSString *) *)al_globalConstraintIdentifiers
{
NSAssert([NSThread isMainThread], @"PureLayout is not thread safe, and must be used exclusively from the main thread.");
if (!_al_globalConstraintIdentifiers) {
@ -228,7 +228,7 @@ static __NSMutableArray_of(NSString *) *_al_globalConstraintIdentifiers = nil;
*/
+ (NSString *)al_currentGlobalConstraintIdentifier
{
__NSMutableArray_of(NSString *) *globalConstraintIdentifiers = [self al_globalConstraintIdentifiers];
PL__NSMutableArray_of(NSString *) *globalConstraintIdentifiers = [self al_globalConstraintIdentifiers];
if ([globalConstraintIdentifiers count] == 0) {
return nil;
}

View File

@ -29,7 +29,7 @@
// Using generics with NSMutableArray is so common in the internal implementation of PureLayout that it gets a dedicated preprocessor macro for better readability.
#define __NSMutableArray_of(type) PL__GENERICS(NSMutableArray, type)
#define PL__NSMutableArray_of(type) PL__GENERICS(NSMutableArray, type)
PL__ASSUME_NONNULL_BEGIN
@ -68,11 +68,11 @@ static const CGFloat kMULTIPLIER_MIN_VALUE = (CGFloat)0.00001; // very small flo
@interface NSLayoutConstraint (PureLayoutInternal)
+ (BOOL)al_preventAutomaticConstraintInstallation;
+ (__NSMutableArray_of(NSLayoutConstraint *) *)al_currentArrayOfCreatedConstraints;
+ (PL__NSMutableArray_of(NSLayoutConstraint *) *)al_currentArrayOfCreatedConstraints;
+ (BOOL)al_isExecutingPriorityConstraintsBlock;
+ (ALLayoutPriority)al_currentGlobalConstraintPriority;
#if PL__PureLayout_MinBaseSDK_iOS_8_0 || PL__PureLayout_MinBaseSDK_OSX_10_10
+ (NSString *)al_currentGlobalConstraintIdentifier;
+ (nullable NSString *)al_currentGlobalConstraintIdentifier;
#endif /* PL__PureLayout_MinBaseSDK_iOS_8_0 || PL__PureLayout_MinBaseSDK_OSX_10_10 */
+ (void)al_applyGlobalStateToConstraint:(NSLayoutConstraint *)constraint;
+ (NSLayoutAttribute)al_layoutAttributeForAttribute:(ALAttribute)attribute;

View File

@ -29,14 +29,18 @@
#define PureLayoutDefines_h
#import <Foundation/Foundation.h>
// check the code in <Foundation/NSObjCRuntime.h> - <Availability.h>
// Define some preprocessor macros to check for a minimum Base SDK. These are used to prevent compile-time errors in older versions of Xcode.
#define PL__PureLayout_MinBaseSDK_iOS_8_0 (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1)
#define PL__PureLayout_MinBaseSDK_iOS_9_0 (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_8_4)
#define PL__PureLayout_MinBaseSDK_iOS_11_0 (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3)
#define PL__PureLayout_MinBaseSDK_OSX_10_10 (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9)
// Define some preprocessor macros to check for a minimum System Version. These are used to prevent runtime crashes on older versions of iOS/OS X.
#define PL__PureLayout_MinSysVer_iOS_7_0 (TARGET_OS_IPHONE && floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)
#define PL__PureLayout_MinSysVer_iOS_8_0 (TARGET_OS_IPHONE && floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1)
#define PL__PureLayout_MinSysVer_iOS_9_0 (TARGET_OS_IPHONE && floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_x_Max)
#define PL__PureLayout_MinSysVer_iOS_10_0 (TARGET_OS_IPHONE && floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max)
#define PL__PureLayout_MinSysVer_OSX_10_9 (!TARGET_OS_IPHONE && floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4)
// Define some preprocessor macros that allow nullability annotations to be adopted in a backwards-compatible manner.

View File

@ -1,5 +1,5 @@
# [![PureLayout](https://github.com/PureLayout/PureLayout/blob/master/Images/PureLayout.png?raw=true)](#)
[![Build Status](http://img.shields.io/travis/PureLayout/PureLayout.svg?style=flat)](https://travis-ci.org/PureLayout/PureLayout) [![Test Coverage](http://img.shields.io/coveralls/PureLayout/PureLayout.svg?style=flat)](https://coveralls.io/r/PureLayout/PureLayout) [![Version](http://img.shields.io/cocoapods/v/PureLayout.svg?style=flat)](http://cocoapods.org/pods/PureLayout) [![Platform](http://img.shields.io/cocoapods/p/PureLayout.svg?style=flat)](http://cocoapods.org/pods/PureLayout) [![License](http://img.shields.io/cocoapods/l/PureLayout.svg?style=flat)](LICENSE)
[![Build Status](https://travis-ci.org/PureLayout/PureLayout.svg?branch=master)](https://travis-ci.org/PureLayout/PureLayout) [![Version](http://img.shields.io/cocoapods/v/PureLayout.svg?style=flat)](http://cocoapods.org/pods/PureLayout) [![Platform](http://img.shields.io/cocoapods/p/PureLayout.svg?style=flat)](http://cocoapods.org/pods/PureLayout) [![License](http://img.shields.io/cocoapods/l/PureLayout.svg?style=flat)](LICENSE)
The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. PureLayout extends `UIView`/`NSView`, `NSArray`, and `NSLayoutConstraint` with a comprehensive Auto Layout API that is modeled after Apple's own frameworks. PureLayout is a cross-platform Objective-C library that works (and looks!) great in Swift. It is fully backwards-compatible with all versions of iOS and OS X that support Auto Layout.
@ -110,6 +110,8 @@ Additionally, there is one generic attribute type, `ALAttribute`, which is effec
- autoSetDimension(s)ToSize:
- autoConstrainAttribute:toAttribute:ofView:(withOffset:|withMultiplier:)
- autoPinTo(Top|Bottom)LayoutGuideOfViewController:withInset: // iOS only
- autoPinEdgeToSuperviewSafeArea: // iOS 11.0+ only
- autoPinEdgeToSuperviewSafeArea:withInset: // iOS 11.0+ only
```
### [`NSArray`](PureLayout/PureLayout/NSArray%2BPureLayout.h)
@ -144,16 +146,28 @@ Additionally, there is one generic attribute type, `ALAttribute`, which is effec
### Sample Code (Swift)
PureLayout dramatically simplifies writing Auto Layout code. Let's take a quick look at some examples, using PureLayout from Swift.
Initialize the view using PureLayout initializer:
```swift
let view1 = UIView(forAutoLayout: ())
```
If you need to use a different initializer (e.g. in `UIView` subclass), you can also use `configureForAutoLayout`:
```
view1.configureForAutoLayout() // alternative to UIView.init(forAutoLayout: ())
```
Here's a constraint between two views created (and automatically activated) using PureLayout:
```swift
view1.autoPinEdge(.Top, toEdge: .Bottom, ofView: view2)
view1.autoPinEdge(.top, toEdge: .bottom, ofView: view2)
```
Without PureLayout, here's the equivalent code you'd have to write using Apple's Foundation API directly:
```swift
NSLayoutConstraint(item: view1, attribute: .Top, relatedBy: .Equal, toItem: view2, attribute: .Bottom, multiplier: 1.0, constant: 0.0).active = true
NSLayoutConstraint(item: view1, attribute: .top, relatedBy: .equal, toItem: view2, attribute: .bottom, multiplier: 1.0, constant: 0.0).active = true
```
Many APIs of PureLayout create multiple constraints for you under the hood, letting you write highly readable layout code:
@ -163,13 +177,19 @@ Many APIs of PureLayout create multiple constraints for you under the hood, lett
logoImageView.autoCenterInSuperview()
// 4 constraints created & activated in one line!
textContentView.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsets(top: 20.0, left: 5.0, bottom: 10.0, right: 5.0))
textContentView.autoPinEdgesToSuperviewEdges(with insets: UIEdgeInsets(top: 20.0, left: 5.0, bottom: 10.0, right: 5.0))
```
PureLayout always returns the constraints it creates so you have full control:
```swift
let constraint = skinnyView.autoMatchDimension(.Height, toDimension: .Width, ofView: tallView)
let constraint = skinnyView.autoMatchDimension(.height, toDimension: .width, ofView: tallView)
```
PureLayout supports safearea with iOS 11.0+:
```swift
view2.autoPinEdge(toSuperviewSafeArea: .top)
```
PureLayout supports all Auto Layout features including inequalities, priorities, layout margins, identifiers, and much more. It's a comprehensive, developer-friendly way to use Auto Layout.

View File

@ -412,7 +412,7 @@ BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
} else {
[fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err];
}
if (nil != err) {
if (err != nil) {
if ([err.domain isEqualToString:NSCocoaErrorDomain] &&
err.code == 640) {
unzippingError = err;
@ -430,7 +430,9 @@ BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
continue;
}
if (!fileIsSymbolicLink) {
if (isDirectory && !fileIsSymbolicLink) {
// nothing to read/write for a directory
} else if (!fileIsSymbolicLink) {
// ensure we are not creating stale file entries
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
if (readBytes >= 0) {
@ -720,29 +722,31 @@ BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath];
NSArray<NSString *> *allObjects = dirEnumerator.allObjects;
NSUInteger total = allObjects.count, complete = 0;
NSString *fileName;
for (fileName in allObjects) {
BOOL isDir;
if (keepParentDirectory && !total) {
allObjects = @[@""];
total = 1;
}
for (__strong NSString *fileName in allObjects) {
NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
if (keepParentDirectory)
{
if (keepParentDirectory) {
fileName = [directoryPath.lastPathComponent stringByAppendingPathComponent:fileName];
}
BOOL isDir;
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
if (!isDir) {
// file
success &= [zipArchive writeFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes];
}
else
{
if ([[NSFileManager defaultManager] subpathsOfDirectoryAtPath:fullFilePath error:nil].count == 0)
{
} else {
// directory
if (![fileManager enumeratorAtPath:fullFilePath].nextObject) {
// empty directory
success &= [zipArchive writeFolderAtPath:fullFilePath withFolderName:fileName withPassword:password];
}
}
complete++;
if (progressHandler) {
complete++;
progressHandler(complete, total);
}
}
@ -866,7 +870,7 @@ BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened");
int error = zipClose(_zip, NULL);
_zip = nil;
return error == UNZ_OK;
return error == ZIP_OK;
}
#pragma mark - Private

View File

@ -84,6 +84,7 @@ void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab)
/***************************************************************************/
#ifndef NOCRYPT
int cryptrand(unsigned char *buf, unsigned int len)
{
#ifdef _WIN32
@ -140,5 +141,6 @@ int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys,
buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify2, t);
return n;
}
#endif
/***************************************************************************/

View File

@ -48,12 +48,14 @@ uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c);
/* Initialize the encryption keys and the random header according to the given password. */
void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab);
#ifndef NOCRYPT
/* Generate cryptographically secure random numbers */
int cryptrand(unsigned char *buf, unsigned int len);
/* Create encryption header */
int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys,
const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2);
#endif
/***************************************************************************/

View File

@ -1269,6 +1269,13 @@ extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, in
for (i = 0; i < 12; i++)
zdecode(s->keys, s->pcrc_32_tab, source[i]);
uint8_t expected = (s->cur_file_info.flag & (1 << 3)) ?
s->cur_file_info.dos_date >> 8 :
s->cur_file_info.crc >> 24;
uint8_t actual = (uint8_t)source[11];
if (expected != actual) {
return UNZ_BADPASSWORD;
}
s->pfile_in_zip_read->rest_read_compressed -= 12;
s->pfile_in_zip_read->pos_in_zipfile += 12;
@ -1344,7 +1351,7 @@ extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len)
s->pfile_in_zip_read->stream.next_in);
bytes_to_read -= bytes_not_read;
if (bytes_not_read > 0)
memcpy(s->pfile_in_zip_read->read_buffer, s->pfile_in_zip_read->stream.next_in, bytes_not_read);
memmove(s->pfile_in_zip_read->read_buffer, s->pfile_in_zip_read->stream.next_in, bytes_not_read);
if (s->pfile_in_zip_read->rest_read_compressed < bytes_to_read)
bytes_to_read = (uint16_t)s->pfile_in_zip_read->rest_read_compressed;

View File

@ -1,24 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface SCKExampleTest : XCTestCase
@end
#pragma mark -
@implementation SCKExampleTest
- (void)testExample
{
XCTAssertTrue(YES);
}
@end
NS_ASSUME_NONNULL_END

View File

@ -4,6 +4,33 @@
import Foundation
@objc
public class SecretSessionKnownSenderError: NSObject, CustomNSError {
@objc
public static let kSenderRecipientIdKey = "kSenderRecipientIdKey"
@objc
public static let kSenderDeviceIdKey = "kSenderDeviceIdKey"
public let senderRecipientId: String
public let senderDeviceId: UInt32
public let underlyingError: Error
init(senderRecipientId: String, senderDeviceId: UInt32, underlyingError: Error) {
self.senderRecipientId = senderRecipientId
self.senderDeviceId = senderDeviceId
self.underlyingError = underlyingError
}
public var errorUserInfo: [String: Any] {
return [
type(of: self).kSenderRecipientIdKey: self.senderRecipientId,
type(of: self).kSenderDeviceIdKey: self.senderDeviceId,
NSUnderlyingErrorKey: (underlyingError as NSError)
]
}
}
@objc
public enum SMKSecretSessionCipherError: Int, Error {
case selfSentMessage
@ -245,97 +272,113 @@ public class SMKDecryptResult: NSObject {
localDeviceId: Int32,
protocolContext: Any?) throws -> SMKDecryptResult {
guard timestamp > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid timestamp")
}
guard timestamp > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid timestamp")
}
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
}
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
}
// UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext);
let wrapper = try SMKUnidentifiedSenderMessage.parse(dataAndPrefix: cipherTextData)
// UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext);
let wrapper = try SMKUnidentifiedSenderMessage.parse(dataAndPrefix: cipherTextData)
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(),
// ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize());
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
}
let ephemeralSalt = NSData.join([
prefixData,
try ourIdentityKeyPair.ecPublicKey().serialized,
wrapper.ephemeralKey.serialized
])
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(),
// ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize());
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
}
let ephemeralSalt = NSData.join([
prefixData,
try ourIdentityKeyPair.ecPublicKey().serialized,
wrapper.ephemeralKey.serialized
])
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(),
// ephemeralSalt);
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: wrapper.ephemeralKey,
ephemeralPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: ephemeralSalt)
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(),
// ephemeralSalt);
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: wrapper.ephemeralKey,
ephemeralPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: ephemeralSalt)
// byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic());
let staticKeyBytes = try decrypt(cipherKey: ephemeralKeys.cipherKey,
macKey: ephemeralKeys.macKey,
cipherTextWithMac: wrapper.encryptedStatic)
// byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic());
let staticKeyBytes = try decrypt(cipherKey: ephemeralKeys.cipherKey,
macKey: ephemeralKeys.macKey,
cipherTextWithMac: wrapper.encryptedStatic)
// ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0);
let staticKey = try ECPublicKey(serializedKeyData: staticKeyBytes)
// ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0);
let staticKey = try ECPublicKey(serializedKeyData: staticKeyBytes)
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic());
let staticSalt = NSData.join([
ephemeralKeys.chainKey,
wrapper.encryptedStatic
])
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic());
let staticSalt = NSData.join([
ephemeralKeys.chainKey,
wrapper.encryptedStatic
])
// StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt);
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: staticKey,
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: staticSalt)
// StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt);
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: staticKey,
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: staticSalt)
// byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage());
let messageBytes = try decrypt(cipherKey: staticKeys.cipherKey,
macKey: staticKeys.macKey,
cipherTextWithMac: wrapper.encryptedMessage)
// byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage());
let messageBytes = try decrypt(cipherKey: staticKeys.cipherKey,
macKey: staticKeys.macKey,
cipherTextWithMac: wrapper.encryptedMessage)
// content = new UnidentifiedSenderMessageContent(messageBytes);
let messageContent = try SMKUnidentifiedSenderMessageContent.parse(data: messageBytes)
// content = new UnidentifiedSenderMessageContent(messageBytes);
let messageContent = try SMKUnidentifiedSenderMessageContent.parse(data: messageBytes)
guard messageContent.senderCertificate.senderRecipientId != localRecipientId ||
messageContent.senderCertificate.senderDeviceId != localDeviceId else {
let senderRecipientId = messageContent.senderCertificate.senderRecipientId
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
guard senderRecipientId != localRecipientId || senderDeviceId != localDeviceId else {
Logger.info("Discarding self-sent message")
throw SMKSecretSessionCipherError.selfSentMessage
}
}
// validator.validate(content.getSenderCertificate(), timestamp);
// validator.validate(content.getSenderCertificate(), timestamp);
let wrapAsKnownSenderError = { (underlyingError: Error) in
return SecretSessionKnownSenderError(senderRecipientId: senderRecipientId, senderDeviceId: senderDeviceId, underlyingError: underlyingError)
}
do {
try certificateValidator.throwswrapped_validate(senderCertificate: messageContent.senderCertificate,
validationTime: timestamp)
validationTime: timestamp)
} catch {
throw wrapAsKnownSenderError(error)
}
// if (!MessageDigest.isEqual(content.getSenderCertificate().getKey().serialize(), staticKeyBytes)) {
// throw new InvalidKeyException("Sender's certificate key does not match key used in message");
// }
//
// NOTE: Constant time comparison.
guard messageContent.senderCertificate.key.serialized.ows_constantTimeIsEqual(to: staticKeyBytes) else {
throw SMKError.assertionError(description: "\(logTag) Sender's certificate key does not match key used in message.")
}
// if (!MessageDigest.isEqual(content.getSenderCertificate().getKey().serialize(), staticKeyBytes)) {
// throw new InvalidKeyException("Sender's certificate key does not match key used in message");
// }
//
// NOTE: Constant time comparison.
guard messageContent.senderCertificate.key.serialized.ows_constantTimeIsEqual(to: staticKeyBytes) else {
let underlyingError = SMKError.assertionError(description: "\(logTag) Sender's certificate key does not match key used in message.")
throw wrapAsKnownSenderError(underlyingError)
}
let paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent, protocolContext: protocolContext)
let paddedMessagePlaintext: Data
do {
paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent, protocolContext: protocolContext)
} catch {
throw wrapAsKnownSenderError(error)
}
// return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(),
// content.getSenderCertificate().getSenderDeviceId()),
// decrypt(content));
//
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
let senderRecipientId = messageContent.senderCertificate.senderRecipientId
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
guard senderDeviceId >= 0 && senderDeviceId <= INT_MAX else {
throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
}
return SMKDecryptResult(senderRecipientId: senderRecipientId,
senderDeviceId: Int(senderDeviceId),
paddedPayload: paddedMessagePlaintext,
messageType: messageContent.messageType)
// return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(),
// content.getSenderCertificate().getSenderDeviceId()),
// decrypt(content));
//
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
guard senderDeviceId >= 0 && senderDeviceId <= INT_MAX else {
let underlyingError = SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
throw wrapAsKnownSenderError(underlyingError)
}
return SMKDecryptResult(senderRecipientId: senderRecipientId,
senderDeviceId: Int(senderDeviceId),
paddedPayload: paddedMessagePlaintext,
messageType: messageContent.messageType)
}
// MARK: - Encrypt

View File

@ -128,8 +128,9 @@ class SMKSecretSessionCipherTest: XCTestCase {
localDeviceId: bobMockClient.deviceId,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch _ as SMKCertificateError {
} catch let knownSenderError as SecretSessionKnownSenderError {
// Decryption is expected to fail.
XCTAssert(knownSenderError.underlyingError is SMKCertificateError )
} catch {
XCTFail("Unexpected error: \(error)")
}
@ -187,8 +188,9 @@ class SMKSecretSessionCipherTest: XCTestCase {
localDeviceId: bobMockClient.deviceId,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch _ as SMKCertificateError {
} catch let knownSenderError as SecretSessionKnownSenderError {
// Decryption is expected to fail.
XCTAssert(knownSenderError.underlyingError is SMKCertificateError )
} catch {
XCTFail("Unexpected error: \(error)")
}
@ -240,16 +242,21 @@ class SMKSecretSessionCipherTest: XCTestCase {
// }
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
do {
try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
cipherTextData: ciphertext,
timestamp: 31335,
localRecipientId: bobMockClient.recipientId,
localDeviceId: bobMockClient.deviceId,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch {
} catch let knownSenderError as SecretSessionKnownSenderError {
// Decryption is expected to fail.
XCTAssertTrue(error is SMKError)
guard case SMKError.assertionError = knownSenderError.underlyingError else {
XCTFail("unexpected error: \(knownSenderError.underlyingError)")
return
}
} catch {
XCTFail("unexpected error: \(error)")
}
}
@ -287,7 +294,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
key: try! serverKey.ecPublicKey(),
signatureData: serverCertificateSignature)
XCTAssertEqual(try! signedServerCertificate.toProto().certificate, unsignedServerCertificateData)
let signedServerCertificateData = try! signedServerCertificate.serialized()
_ = try! signedServerCertificate.serialized()
// byte[] senderCertificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
// .setSender(sender)

View File

@ -20,12 +20,16 @@ private let i_2166136261 = Int(bitPattern: 2166136261)
private let i_16777619 = Int(16777619)
#endif
fileprivate func serializeAnyJSON(for message: Message, typeURL: String) throws -> String {
var visitor = try JSONEncodingVisitor(message: message)
fileprivate func serializeAnyJSON(
for message: Message,
typeURL: String,
options: JSONEncodingOptions
) throws -> String {
var visitor = try JSONEncodingVisitor(message: message, options: options)
visitor.startObject()
visitor.encodeField(name: "@type", stringValue: typeURL)
if let m = message as? _CustomJSONCodable {
let value = try m.encodedJSONString()
let value = try m.encodedJSONString(options: options)
visitor.encodeField(name: "value", jsonText: value)
} else {
try message.traverse(visitor: &visitor)
@ -381,7 +385,7 @@ extension AnyMessageStorage {
// * The protobuf field we were deserialized from.
// The last case requires locating the type, deserializing
// into an object, then reserializing back to JSON.
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
switch state {
case .binary(let valueData):
// Transcode by decoding the binary data to a message object
@ -394,13 +398,13 @@ extension AnyMessageStorage {
throw JSONEncodingError.anyTranscodeFailure
}
let m = try messageType.init(serializedData: valueData, partial: true)
return try serializeAnyJSON(for: m, typeURL: _typeURL)
return try serializeAnyJSON(for: m, typeURL: _typeURL, options: options)
case .message(let msg):
// We should have been initialized with a typeURL, but
// ensure it wasn't cleared.
let url = !_typeURL.isEmpty ? _typeURL : buildTypeURL(forMessage: msg, typePrefix: defaultAnyTypeURLPrefix)
return try serializeAnyJSON(for: msg, typeURL: url)
return try serializeAnyJSON(for: msg, typeURL: url, options: options)
case .contentJSON(let contentJSON, _):
var jsonEncoder = JSONEncoder()
@ -409,6 +413,8 @@ extension AnyMessageStorage {
jsonEncoder.putStringValue(value: _typeURL)
if !contentJSON.isEmpty {
jsonEncoder.append(staticText: ",")
// NOTE: This doesn't really take `options` into account since it is
// just reflecting out what was taken in originally.
jsonEncoder.append(utf8Data: contentJSON)
}
jsonEncoder.endObject()

View File

@ -14,7 +14,7 @@
/// Allows WKTs to provide their custom JSON encodings.
internal protocol _CustomJSONCodable {
func encodedJSONString() throws -> String
func encodedJSONString(options: JSONEncodingOptions) throws -> String
mutating func decodeJSON(from: inout JSONDecoder) throws
/// Called when the JSON `null` literal is encountered in a position where

View File

@ -131,8 +131,8 @@ extension Google_Protobuf_Any: _CustomJSONCodable {
}
}
internal func encodedJSONString() throws -> String {
return try _storage.encodedJSONString()
internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
return try _storage.encodedJSONString(options: options)
}
internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {

View File

@ -133,7 +133,7 @@ extension Google_Protobuf_Duration: _CustomJSONCodable {
let s = try decoder.scanner.nextQuotedString()
(seconds, nanos) = try parseDuration(text: s)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
if let formatted = formatDuration(seconds: seconds, nanos: nanos) {
return "\"\(formatted)\""
} else {

View File

@ -159,7 +159,7 @@ extension Google_Protobuf_FieldMask: _CustomJSONCodable {
}
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
// Note: Proto requires alphanumeric field names, so there
// cannot be a ',' or '"' character to mess up this formatting.
var jsonPaths = [String]()

View File

@ -26,13 +26,13 @@ extension Google_Protobuf_ListValue: ExpressibleByArrayLiteral {
}
extension Google_Protobuf_ListValue: _CustomJSONCodable {
internal func encodedJSONString() throws -> String {
internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var jsonEncoder = JSONEncoder()
jsonEncoder.append(text: "[")
var separator: StaticString = ""
for v in values {
jsonEncoder.append(staticText: separator)
try v.serializeJSONValue(to: &jsonEncoder)
try v.serializeJSONValue(to: &jsonEncoder, options: options)
separator = ","
}
jsonEncoder.append(text: "]")

View File

@ -28,10 +28,10 @@ extension Google_Protobuf_Struct: ExpressibleByDictionaryLiteral {
}
extension Google_Protobuf_Struct: _CustomJSONCodable {
internal func encodedJSONString() throws -> String {
internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var jsonEncoder = JSONEncoder()
jsonEncoder.startObject()
var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder)
var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder, options: options)
for (k,v) in fields {
try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1)
try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2)

View File

@ -225,7 +225,7 @@ extension Google_Protobuf_Timestamp: _CustomJSONCodable {
(seconds, nanos) = try parseTimestamp(s: s)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
if let formatted = formatTimestamp(seconds: seconds, nanos: nanos) {
return "\"\(formatted)\""
} else {

View File

@ -69,9 +69,9 @@ extension Google_Protobuf_Value: ExpressibleByNilLiteral {
}
extension Google_Protobuf_Value: _CustomJSONCodable {
internal func encodedJSONString() throws -> String {
internal func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var jsonEncoder = JSONEncoder()
try serializeJSONValue(to: &jsonEncoder)
try serializeJSONValue(to: &jsonEncoder, options: options)
return jsonEncoder.stringResult
}
@ -146,14 +146,17 @@ extension Google_Protobuf_Value {
}
/// Writes out the JSON representation of the value to the given encoder.
internal func serializeJSONValue(to encoder: inout JSONEncoder) throws {
internal func serializeJSONValue(
to encoder: inout JSONEncoder,
options: JSONEncodingOptions
) throws {
switch kind {
case .nullValue?: encoder.putNullValue()
case .numberValue(let v)?: encoder.putDoubleValue(value: v)
case .stringValue(let v)?: encoder.putStringValue(value: v)
case .boolValue(let v)?: encoder.putBoolValue(value: v)
case .structValue(let v)?: encoder.append(text: try v.jsonString())
case .listValue(let v)?: encoder.append(text: try v.jsonString())
case .structValue(let v)?: encoder.append(text: try v.jsonString(options: options))
case .listValue(let v)?: encoder.append(text: try v.jsonString(options: options))
case nil: throw JSONEncodingError.missingValue
}
}

View File

@ -47,7 +47,7 @@ extension Google_Protobuf_DoubleValue:
self.init(floatLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putDoubleValue(value: value)
return encoder.stringResult
@ -75,7 +75,7 @@ extension Google_Protobuf_FloatValue:
self.init(floatLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putFloatValue(value: value)
return encoder.stringResult
@ -103,7 +103,7 @@ extension Google_Protobuf_Int64Value:
self.init(integerLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putInt64(value: value)
return encoder.stringResult
@ -131,7 +131,7 @@ extension Google_Protobuf_UInt64Value:
self.init(integerLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putUInt64(value: value)
return encoder.stringResult
@ -159,7 +159,7 @@ extension Google_Protobuf_Int32Value:
self.init(integerLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
return String(value)
}
@ -185,7 +185,7 @@ extension Google_Protobuf_UInt32Value:
self.init(integerLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
return String(value)
}
@ -211,7 +211,7 @@ extension Google_Protobuf_BoolValue:
self.init(booleanLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
return value ? "true" : "false"
}
@ -247,7 +247,7 @@ extension Google_Protobuf_StringValue:
self.init(unicodeScalarLiteral)
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putStringValue(value: value)
return encoder.stringResult
@ -269,7 +269,7 @@ extension Google_Protobuf_BytesValue: ProtobufWrapper, _CustomJSONCodable {
self.value = value
}
func encodedJSONString() throws -> String {
func encodedJSONString(options: JSONEncodingOptions) throws -> String {
var encoder = JSONEncoder()
encoder.putBytesValue(value: value)
return encoder.stringResult

View File

@ -0,0 +1,22 @@
// Sources/SwiftProtobuf/JSONEncodingOptions.swift - JSON encoding options
//
// Copyright (c) 2014 - 2018 Apple Inc. and the project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See LICENSE.txt for license information:
// https://github.com/apple/swift-protobuf/blob/master/LICENSE.txt
//
// -----------------------------------------------------------------------------
///
/// JSON encoding options
///
// -----------------------------------------------------------------------------
/// Options for JSONEncoding.
public struct JSONEncodingOptions {
/// Always print enums as ints. By default they are printed as strings.
public var alwaysPrintEnumsAsInts: Bool = false
public init() {}
}

View File

@ -19,6 +19,7 @@ internal struct JSONEncodingVisitor: Visitor {
private var encoder = JSONEncoder()
private var nameMap: _NameMap
private let options: JSONEncodingOptions
/// The JSON text produced by the visitor, as raw UTF8 bytes.
var dataResult: Data {
@ -32,21 +33,23 @@ internal struct JSONEncodingVisitor: Visitor {
/// Creates a new visitor for serializing a message of the given type to JSON
/// format.
init(type: Message.Type) throws {
init(type: Message.Type, options: JSONEncodingOptions) throws {
if let nameProviding = type as? _ProtoNameProviding.Type {
self.nameMap = nameProviding._protobuf_nameMap
} else {
throw JSONEncodingError.missingFieldNames
}
self.options = options
}
/// Creates a new visitor that serializes the given message to JSON format.
init(message: Message) throws {
init(message: Message, options: JSONEncodingOptions) throws {
if let nameProviding = message as? _ProtoNameProviding {
self.nameMap = type(of: nameProviding)._protobuf_nameMap
} else {
throw JSONEncodingError.missingFieldNames
}
self.options = options
}
mutating func startArray() {
@ -154,7 +157,7 @@ internal struct JSONEncodingVisitor: Visitor {
mutating func visitSingularEnumField<E: Enum>(value: E, fieldNumber: Int) throws {
try startField(for: fieldNumber)
if let n = value.name {
if !options.alwaysPrintEnumsAsInts, let n = value.name {
encoder.appendQuoted(name: n)
} else {
encoder.putEnumInt(value: value.rawValue)
@ -163,7 +166,7 @@ internal struct JSONEncodingVisitor: Visitor {
mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
try startField(for: fieldNumber)
let json = try value.jsonUTF8Data()
let json = try value.jsonUTF8Data(options: options)
encoder.append(utf8Data: json)
}
@ -259,9 +262,10 @@ internal struct JSONEncodingVisitor: Visitor {
}
mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts
try _visitRepeated(value: value, fieldNumber: fieldNumber) {
(encoder: inout JSONEncoder, v: E) throws in
if let n = v.name {
if !alwaysPrintEnumsAsInts, let n = v.name {
encoder.appendQuoted(name: n)
} else {
encoder.putEnumInt(value: v.rawValue)
@ -270,9 +274,10 @@ internal struct JSONEncodingVisitor: Visitor {
}
mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
let localOptions = options
try _visitRepeated(value: value, fieldNumber: fieldNumber) {
(encoder: inout JSONEncoder, v: M) throws in
let json = try v.jsonUTF8Data()
let json = try v.jsonUTF8Data(options: localOptions)
encoder.append(utf8Data: json)
}
}
@ -289,7 +294,7 @@ internal struct JSONEncodingVisitor: Visitor {
mutating func visitMapField<KeyType, ValueType: MapValueType>(fieldType: _ProtobufMap<KeyType, ValueType>.Type, value: _ProtobufMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws {
try startField(for: fieldNumber)
encoder.append(text: "{")
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder)
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options)
for (k,v) in value {
try KeyType.visitSingular(value: k, fieldNumber: 1, with: &mapVisitor)
try ValueType.visitSingular(value: v, fieldNumber: 2, with: &mapVisitor)
@ -301,7 +306,7 @@ internal struct JSONEncodingVisitor: Visitor {
mutating func visitMapField<KeyType, ValueType>(fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type, value: _ProtobufEnumMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws where ValueType.RawValue == Int {
try startField(for: fieldNumber)
encoder.append(text: "{")
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder)
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options)
for (k, v) in value {
try KeyType.visitSingular(value: k, fieldNumber: 1, with: &mapVisitor)
try mapVisitor.visitSingularEnumField(value: v, fieldNumber: 2)
@ -313,7 +318,7 @@ internal struct JSONEncodingVisitor: Visitor {
mutating func visitMapField<KeyType, ValueType>(fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type, value: _ProtobufMessageMap<KeyType, ValueType>.BaseType, fieldNumber: Int) throws {
try startField(for: fieldNumber)
encoder.append(text: "{")
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder)
var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options)
for (k,v) in value {
try KeyType.visitSingular(value: k, fieldNumber: 1, with: &mapVisitor)
try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2)

View File

@ -23,9 +23,11 @@ import Foundation
internal struct JSONMapEncodingVisitor: SelectiveVisitor {
private var separator: StaticString?
internal var encoder: JSONEncoder
private let options: JSONEncodingOptions
init(encoder: JSONEncoder) {
init(encoder: JSONEncoder, options: JSONEncodingOptions) {
self.encoder = encoder
self.options = options
}
private mutating func startKey() {
@ -40,6 +42,13 @@ internal struct JSONMapEncodingVisitor: SelectiveVisitor {
encoder.append(staticText: ":")
}
mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
// Doubles/Floats can never be map keys, only values
assert(fieldNumber == 2)
startValue()
encoder.putFloatValue(value: value)
}
mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
// Doubles/Floats can never be map keys, only values
assert(fieldNumber == 2)
@ -86,6 +95,30 @@ internal struct JSONMapEncodingVisitor: SelectiveVisitor {
encoder.putUInt64(value: value)
}
mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
try visitSingularInt32Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
try visitSingularInt64Field(value: value, fieldNumber: fieldNumber)
}
mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
if fieldNumber == 1 {
startKey()
@ -116,7 +149,7 @@ internal struct JSONMapEncodingVisitor: SelectiveVisitor {
// Enums can only be map values, never keys
assert(fieldNumber == 2)
startValue()
if let n = value.name {
if !options.alwaysPrintEnumsAsInts, let n = value.name {
encoder.putStringValue(value: String(describing: n))
} else {
encoder.putEnumInt(value: value.rawValue)
@ -127,7 +160,7 @@ internal struct JSONMapEncodingVisitor: SelectiveVisitor {
// Messages can only be map values, never keys
assert(fieldNumber == 2)
startValue()
let json = try value.jsonString()
let json = try value.jsonString(options: options)
encoder.append(text: json)
}

View File

@ -22,9 +22,13 @@ public extension Message {
/// serializing to JSON.
///
/// - Returns: A string containing the JSON serialization of the message.
/// - Parameters:
/// - options: The JSONEncodingOptions to use.
/// - Throws: `JSONEncodingError` if encoding fails.
func jsonString() throws -> String {
let data = try jsonUTF8Data()
func jsonString(
options: JSONEncodingOptions = JSONEncodingOptions()
) throws -> String {
let data = try jsonUTF8Data(options: options)
return String(data: data, encoding: String.Encoding.utf8)!
}
@ -34,14 +38,18 @@ public extension Message {
/// serializing to JSON.
///
/// - Returns: A Data containing the JSON serialization of the message.
/// - Parameters:
/// - options: The JSONEncodingOptions to use.
/// - Throws: `JSONEncodingError` if encoding fails.
func jsonUTF8Data() throws -> Data {
func jsonUTF8Data(
options: JSONEncodingOptions = JSONEncodingOptions()
) throws -> Data {
if let m = self as? _CustomJSONCodable {
let string = try m.encodedJSONString()
let string = try m.encodedJSONString(options: options)
let data = string.data(using: String.Encoding.utf8)! // Cannot fail!
return data
}
var visitor = try JSONEncodingVisitor(message: self)
var visitor = try JSONEncodingVisitor(message: self, options: options)
visitor.startObject()
try traverse(visitor: &visitor)
visitor.endObject()

View File

@ -22,10 +22,15 @@ public extension Message {
/// serializing to JSON.
///
/// - Returns: A string containing the JSON serialization of the messages.
/// - Parameter collection: The list of messages to encode.
/// - Parameters:
/// - collection: The list of messages to encode.
/// - options: The JSONEncodingOptions to use.
/// - Throws: `JSONEncodingError` if encoding fails.
public static func jsonString<C: Collection>(from collection: C) throws -> String where C.Iterator.Element == Self {
let data = try jsonUTF8Data(from: collection)
public static func jsonString<C: Collection>(
from collection: C,
options: JSONEncodingOptions = JSONEncodingOptions()
) throws -> String where C.Iterator.Element == Self {
let data = try jsonUTF8Data(from: collection, options: options)
return String(data: data, encoding: String.Encoding.utf8)!
}
@ -35,10 +40,15 @@ public extension Message {
/// serializing to JSON.
///
/// - Returns: A Data containing the JSON serialization of the messages.
/// - Parameter collection: The list of messages to encode.
/// - Parameters:
/// - collection: The list of messages to encode.
/// - options: The JSONEncodingOptions to use.
/// - Throws: `JSONEncodingError` if encoding fails.
public static func jsonUTF8Data<C: Collection>(from collection: C) throws -> Data where C.Iterator.Element == Self {
var visitor = try JSONEncodingVisitor(type: Self.self)
public static func jsonUTF8Data<C: Collection>(
from collection: C,
options: JSONEncodingOptions = JSONEncodingOptions()
) throws -> Data where C.Iterator.Element == Self {
var visitor = try JSONEncodingVisitor(type: Self.self, options: options)
visitor.startArray()
for message in collection {
visitor.startObject()

View File

@ -19,9 +19,9 @@ public struct Version {
/// Major version.
static public let major = 1
/// Minor version.
static public let minor = 1
static public let minor = 2
/// Revision number.
static public let revision = 2
static public let revision = 0
/// String form of the version number.
static public let versionString = "\(major).\(minor).\(revision)"

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>6.5.2</string>
<string>6.5.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.0.2</string>
<string>3.1.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.1.3</string>
<string>2.1.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,4 +1,5 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive
DEFINES_MODULE = YES
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = -l"z"
PODS_BUILD_DIR = ${BUILD_DIR}

View File

@ -39,7 +39,6 @@
#import "OWSRecordTranscriptJob.h"
#import "OWSVerificationStateSyncMessage.h"
#import "OWSAttachmentDownloads.h"
#import "OWSAttachmentsProcessor.h"
#import "TSAttachment.h"
#import "TSAttachmentPointer.h"
#import "TSAttachmentStream.h"

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.1.2</string>
<string>1.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>