Initial clean commit

This commit is contained in:
2026-03-26 12:05:37 +08:00
commit 7c3c8dffee
1177 changed files with 147248 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
use_frameworks!
platform :ios, '10.0'
target 'SellyCloudSDK_Example' do
pod 'SellyCloudSDK', :path => '../'
target 'SellyCloudSDK_Tests' do
inherit! :search_paths
pod 'FBSnapshotTestCase'
end
end

View File

@@ -0,0 +1,27 @@
PODS:
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- SellyCloudSDK (0.1.0)
DEPENDENCIES:
- FBSnapshotTestCase
- SellyCloudSDK (from `../`)
SPEC REPOS:
trunk:
- FBSnapshotTestCase
EXTERNAL SOURCES:
SellyCloudSDK:
:path: "../"
SPEC CHECKSUMS:
FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
SellyCloudSDK: f71a5b97e56b934760a0c33267b5ca27b9d758db
PODFILE CHECKSUM: 9e422a4ddd0ac5aa3d5289a1a7feda4a45dcf75d
COCOAPODS: 1.16.2

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <UIKit/UIKit.h>
@interface UIApplication (StrictKeyWindow)
/**
@return The receiver's @c keyWindow. Raises an assertion if @c nil.
*/
- (UIWindow *)fb_strictKeyWindow;
@end

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
@implementation UIApplication (StrictKeyWindow)
- (UIWindow *)fb_strictKeyWindow
{
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
if (!keyWindow) {
[NSException raise:@"FBSnapshotTestCaseNilKeyWindowException"
format:@"Snapshot tests must be hosted by an application with a key window. Please ensure your test"
" host sets up a key window at launch (either via storyboards or programmatically) and doesn't"
" do anything to remove it while snapshot tests are running."];
}
return keyWindow;
}
@end

View File

@@ -0,0 +1,37 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import <UIKit/UIKit.h>
@interface UIImage (Compare)
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
@end

View File

@@ -0,0 +1,134 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import <FBSnapshotTestCase/UIImage+Compare.h>
// This makes debugging much more fun
typedef union {
uint32_t raw;
unsigned char bytes[4];
struct {
char red;
char green;
char blue;
char alpha;
} __attribute__ ((packed)) pixels;
} FBComparePixel;
@implementation UIImage (Compare)
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
{
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
// The images have the equal size, so we could use the smallest amount of bytes because of byte padding
size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
void *imagePixels = calloc(1, referenceImageSizeBytes);
if (!referenceImagePixels || !imagePixels) {
free(referenceImagePixels);
free(imagePixels);
return NO;
}
CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
referenceImageSize.width,
referenceImageSize.height,
CGImageGetBitsPerComponent(self.CGImage),
minBytesPerRow,
CGImageGetColorSpace(self.CGImage),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
);
CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(image.CGImage),
minBytesPerRow,
CGImageGetColorSpace(image.CGImage),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
);
if (!referenceImageContext || !imageContext) {
CGContextRelease(referenceImageContext);
CGContextRelease(imageContext);
free(referenceImagePixels);
free(imagePixels);
return NO;
}
CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
CGContextRelease(referenceImageContext);
CGContextRelease(imageContext);
BOOL imageEqual = YES;
// Do a fast compare if we can
if (tolerance == 0) {
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
} else {
// Go through each pixel in turn and see if it is different
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
FBComparePixel *p1 = referenceImagePixels;
FBComparePixel *p2 = imagePixels;
NSInteger numDiffPixels = 0;
for (int n = 0; n < pixelCount; ++n) {
// If this pixel is different, increment the pixel diff count and see
// if we have hit our limit.
if (p1->raw != p2->raw) {
numDiffPixels ++;
CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
if (percent > tolerance) {
imageEqual = NO;
break;
}
}
p1++;
p2++;
}
}
free(referenceImagePixels);
free(imagePixels);
return imageEqual;
}
@end

View File

@@ -0,0 +1,37 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import <UIKit/UIKit.h>
@interface UIImage (Diff)
- (UIImage *)fb_diffWithImage:(UIImage *)image;
@end

View File

@@ -0,0 +1,56 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#import <FBSnapshotTestCase/UIImage+Diff.h>
@implementation UIImage (Diff)
- (UIImage *)fb_diffWithImage:(UIImage *)image
{
if (!image) {
return nil;
}
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
CGContextSetAlpha(context, 0.5);
CGContextBeginTransparencyLayer(context, NULL);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextSetBlendMode(context, kCGBlendModeDifference);
CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
CGContextEndTransparencyLayer(context);
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImage;
}
@end

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <UIKit/UIKit.h>
@interface UIImage (Snapshot)
/// Uses renderInContext: to get a snapshot of the layer.
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
/// Uses renderInContext: to get a snapshot of the view layer.
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
+ (UIImage *)fb_imageForView:(UIView *)view;
@end

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
@implementation UIImage (Snapshot)
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
{
CGRect bounds = layer.bounds;
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
NSAssert1(context, @"Could not generate context for layer %@", layer);
CGContextSaveGState(context);
[layer layoutIfNeeded];
[layer renderInContext:context];
CGContextRestoreGState(context);
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
{
[view layoutIfNeeded];
return [self fb_imageForLayer:view.layer];
}
+ (UIImage *)fb_imageForView:(UIView *)view
{
CGRect bounds = view.bounds;
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
// If the input view is already a UIWindow, then just use that. Otherwise wrap in a window.
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window;
BOOL removeFromSuperview = NO;
if (!window) {
window = [[UIApplication sharedApplication] fb_strictKeyWindow];
}
if (!view.window && view != window) {
[window addSubview:view];
removeFromSuperview = YES;
}
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
[view layoutIfNeeded];
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (removeFromSuperview) {
[view removeFromSuperview];
}
return snapshot;
}
@end

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
/*
There are three ways of setting reference image directories.
1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
c-string with the path.
2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
takes precedence over the preprocessor macro to allow for run-time override.
3. Keep everything unset, which will cause the reference images to be looked up
inside the bundle holding the current test, in the
Resources/ReferenceImages_* directories.
*/
#ifndef FB_REFERENCE_IMAGE_DIR
#define FB_REFERENCE_IMAGE_DIR ""
#endif
/**
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
@param view The view to snapshot
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage of pixels that can differ and still count as an 'identical' view
*/
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
#define FBSnapshotVerifyView(view__, identifier__) \
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
/**
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
@param layer The layer to snapshot
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
*/
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
#define FBSnapshotVerifyLayer(layer__, identifier__) \
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
{ \
NSString *errorDescription = [self snapshotVerifyViewOrLayer:viewOrLayer__ identifier:identifier__ suffixes:suffixes__ tolerance:tolerance__]; \
BOOL noErrors = (errorDescription == nil); \
XCTAssertTrue(noErrors, @"%@", errorDescription); \
}
/**
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
and compare an image of the view to a reference image that write lots of complex layout-code tests.
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
@attention When recording, the reference image directory should be explicitly
set, otherwise the images may be written to somewhere inside the
simulator directory.
For example:
@code
- (void)setUp
{
[super setUp];
self.recordMode = YES;
}
@endcode
*/
@interface FBSnapshotTestCase : XCTestCase
/**
When YES, the test macros will save reference images, rather than performing an actual test.
*/
@property (readwrite, nonatomic, assign) BOOL recordMode;
/**
When @c YES appends the name of the device model and OS to the snapshot file name.
The default value is @c NO.
*/
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
/**
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
There are several things that do not work if renderInContext: is used.
- UIVisualEffect #70
- UIAppearance #91
- Size Classes #92
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
*/
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
- (void)setUp NS_REQUIRES_SUPER;
- (void)tearDown NS_REQUIRES_SUPER;
/**
Performs the comparison or records a snapshot of the layer if recordMode is YES.
@param viewOrLayer The UIView or CALayer to snapshot
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@returns nil if the comparison (or saving of the reference image) succeeded. Otherwise it contains an error description.
*/
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
identifier:(NSString *)identifier
suffixes:(NSOrderedSet *)suffixes
tolerance:(CGFloat)tolerance;
/**
Performs the comparison or records a snapshot of the layer if recordMode is YES.
@param layer The Layer to snapshot
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Performs the comparison or records a snapshot of the view if recordMode is YES.
@param view The view to snapshot
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfView:(UIView *)view
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Checks if reference image with identifier based name exists in the reference images directory.
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if reference image exists.
*/
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Returns the reference image directory.
Helper function used to implement the assert macros.
@param dir directory to use if environment variable not specified. Ignored if null or empty.
*/
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
@end

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/FBSnapshotTestCase.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
@implementation FBSnapshotTestCase
{
FBSnapshotTestController *_snapshotController;
}
#pragma mark - Overrides
- (void)setUp
{
[super setUp];
_snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
}
- (void)tearDown
{
_snapshotController = nil;
[super tearDown];
}
- (BOOL)recordMode
{
return _snapshotController.recordMode;
}
- (void)setRecordMode:(BOOL)recordMode
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.recordMode = recordMode;
}
- (BOOL)isDeviceAgnostic
{
return _snapshotController.deviceAgnostic;
}
- (void)setDeviceAgnostic:(BOOL)deviceAgnostic
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.deviceAgnostic = deviceAgnostic;
}
- (BOOL)usesDrawViewHierarchyInRect
{
return _snapshotController.usesDrawViewHierarchyInRect;
}
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
}
#pragma mark - Public API
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
identifier:(NSString *)identifier
suffixes:(NSOrderedSet *)suffixes
tolerance:(CGFloat)tolerance
{
if (nil == viewOrLayer) {
return @"Object to be snapshotted must not be nil";
}
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)];
if (referenceImageDirectory == nil) {
return @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.";
}
if (suffixes.count == 0) {
return [NSString stringWithFormat:@"Suffixes set cannot be empty %@", suffixes];
}
BOOL testSuccess = NO;
NSError *error = nil;
NSMutableArray *errors = [NSMutableArray array];
if (self.recordMode) {
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes.firstObject];
BOOL referenceImageSaved = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:(identifier) tolerance:tolerance error:&error];
if (!referenceImageSaved) {
[errors addObject:error];
}
} else {
for (NSString *suffix in suffixes) {
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix];
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory identifier:(identifier) error:&error];
if (referenceImageAvailable) {
BOOL comparisonSuccess = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:identifier tolerance:tolerance error:&error];
[errors removeAllObjects];
if (comparisonSuccess) {
testSuccess = YES;
break;
} else {
[errors addObject:error];
}
} else {
[errors addObject:error];
}
}
}
if (!testSuccess) {
return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject];
}
if (self.recordMode) {
return @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!";
}
return nil;
}
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
return [self _compareSnapshotOfViewOrLayer:layer
referenceImagesDirectory:referenceImagesDirectory
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
- (BOOL)compareSnapshotOfView:(UIView *)view
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
return [self _compareSnapshotOfViewOrLayer:view
referenceImagesDirectory:referenceImagesDirectory
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
identifier:identifier
error:errorPtr];
return (referenceImage != nil);
}
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
{
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
if (envReferenceImageDirectory) {
return envReferenceImageDirectory;
}
if (dir && dir.length > 0) {
return dir;
}
return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
}
#pragma mark - Private API
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
selector:self.invocation.selector
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
@end

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
This method is a convenience for creating the suffixes set based on the architecture
that the test is running.
@returns @c YES if the test is running in 64bit, otherwise @c NO.
*/
BOOL FBSnapshotTestCaseIs64Bit(void);
/**
Returns a default set of strings that is used to append a suffix based on the architectures.
@warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
*/
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
/**
Returns a fully «normalized» file name.
Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
@returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
*/
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
#import <UIKit/UIKit.h>
BOOL FBSnapshotTestCaseIs64Bit(void)
{
#if __LP64__
return YES;
#else
return NO;
#endif
}
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
{
NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
[suffixesSet addObject:@"_32"];
[suffixesSet addObject:@"_64"];
if (FBSnapshotTestCaseIs64Bit()) {
return [suffixesSet reversedOrderedSet];
}
return [suffixesSet copy];
}
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
{
UIDevice *device = [UIDevice currentDevice];
UIWindow *keyWindow = [[UIApplication sharedApplication] fb_strictKeyWindow];
CGSize screenSize = keyWindow.bounds.size;
NSString *os = device.systemVersion;
fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
fileName = [validComponents componentsJoinedByString:@"_"];
return fileName;
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
FBSnapshotTestControllerErrorCodeUnknown,
FBSnapshotTestControllerErrorCodeNeedsRecord,
FBSnapshotTestControllerErrorCodePNGCreationFailed,
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
FBSnapshotTestControllerErrorCodeImagesDifferent,
};
/**
Errors returned by the methods of FBSnapshotTestController use this domain.
*/
extern NSString *const FBSnapshotTestControllerErrorDomain;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBReferenceImageFilePathKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBReferenceImageKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBCapturedImageKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBDiffedImageKey;
/**
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
by-pixel comparison of images.
Instances are initialized with the test class, and directories to read and write to.
*/
@interface FBSnapshotTestController : NSObject
/**
Record snapshots.
*/
@property (readwrite, nonatomic, assign) BOOL recordMode;
/**
When @c YES appends the name of the device model and OS to the snapshot file name.
The default value is @c NO.
*/
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
/**
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
*/
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
/**
The directory in which referfence images are stored.
*/
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
/**
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
@returns An instance of FBSnapshotTestController.
*/
- (instancetype)initWithTestClass:(Class)testClass;
/**
Designated initializer.
@param testName The name of the tests.
@returns An instance of FBSnapshotTestController.
*/
- (instancetype)initWithTestName:(NSString *)testName;
/**
Performs the comparison of the layer.
@param layer The Layer to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs the comparison of the view.
@param view The view to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfView:(UIView *)view
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs the comparison of a view or layer.
@param view The view or layer to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Loads a reference image.
@param selector The test method being run.
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
@param errorPtr An error, if this methods returns nil, the error will be something useful.
@returns An image.
*/
- (UIImage *)referenceImageForSelector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
@param referenceImage The reference (correct) image.
@param image The image to test against the reference.
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
@param errorPtr An error that indicates why the comparison failed if it does.
@returns YES if the comparison succeeded and the images are the same(ish).
*/
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
toImage:(UIImage *)image
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Saves the reference image and the test image to `failedOutputDirectory`.
@param referenceImage The reference (correct) image.
@param testImage The image to test against the reference.
@param selector The test method being run.
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
@param errorPtr An error that indicates why the comparison failed if it does.
@returns YES if the save succeeded.
*/
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
testImage:(UIImage *)testImage
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
@end

View File

@@ -0,0 +1,358 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIImage+Compare.h>
#import <FBSnapshotTestCase/UIImage+Diff.h>
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
#import <UIKit/UIKit.h>
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
NSString *const FBReferenceImageKey = @"FBReferenceImageKey";
NSString *const FBCapturedImageKey = @"FBCapturedImageKey";
NSString *const FBDiffedImageKey = @"FBDiffedImageKey";
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
FBTestSnapshotFileNameTypeReference,
FBTestSnapshotFileNameTypeFailedReference,
FBTestSnapshotFileNameTypeFailedTest,
FBTestSnapshotFileNameTypeFailedTestDiff,
};
@implementation FBSnapshotTestController
{
NSString *_testName;
NSFileManager *_fileManager;
}
#pragma mark - Initializers
- (instancetype)initWithTestClass:(Class)testClass;
{
return [self initWithTestName:NSStringFromClass(testClass)];
}
- (instancetype)initWithTestName:(NSString *)testName
{
if (self = [super init]) {
_testName = [testName copy];
_deviceAgnostic = NO;
_fileManager = [[NSFileManager alloc] init];
}
return self;
}
#pragma mark - Overrides
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
}
#pragma mark - Public API
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
return [self compareSnapshotOfViewOrLayer:layer
selector:selector
identifier:identifier
tolerance:0
error:errorPtr];
}
- (BOOL)compareSnapshotOfView:(UIView *)view
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
return [self compareSnapshotOfViewOrLayer:view
selector:selector
identifier:identifier
tolerance:0
error:errorPtr];
}
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
if (self.recordMode) {
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
} else {
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
}
}
- (UIImage *)referenceImageForSelector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
if (nil == image && NULL != errorPtr) {
BOOL exists = [_fileManager fileExistsAtPath:filePath];
if (!exists) {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeNeedsRecord
userInfo:@{
FBReferenceImageFilePathKey: filePath,
NSLocalizedDescriptionKey: @"Unable to load reference image.",
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
}];
} else {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeUnknown
userInfo:nil];
}
}
return image;
}
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
toImage:(UIImage *)image
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size);
if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) {
return YES;
}
if (NULL != errorPtr) {
NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes";
NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100]
: [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)];
FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes;
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:errorCode
userInfo:@{
NSLocalizedDescriptionKey: errorDescription,
NSLocalizedFailureReasonErrorKey: errorReason,
FBReferenceImageKey: referenceImage,
FBCapturedImageKey: image,
FBDiffedImageKey: [referenceImage fb_diffWithImage:image],
}];
}
return NO;
}
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
testImage:(UIImage *)testImage
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
NSData *testPNGData = UIImagePNGRepresentation(testImage);
NSString *referencePath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
NSError *creationError = nil;
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
withIntermediateDirectories:YES
attributes:nil
error:&creationError];
if (!didCreateDir) {
if (NULL != errorPtr) {
*errorPtr = creationError;
}
return NO;
}
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSString *testPath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSString *diffPath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
return YES;
}
#pragma mark - Private API
- (NSString *)_fileNameForSelector:(SEL)selector
identifier:(NSString *)identifier
fileNameType:(FBTestSnapshotFileNameType)fileNameType
{
NSString *fileName = nil;
switch (fileNameType) {
case FBTestSnapshotFileNameTypeFailedReference:
fileName = @"reference_";
break;
case FBTestSnapshotFileNameTypeFailedTest:
fileName = @"failed_";
break;
case FBTestSnapshotFileNameTypeFailedTestDiff:
fileName = @"diff_";
break;
default:
fileName = @"";
break;
}
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
if (0 < identifier.length) {
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
}
if (self.isDeviceAgnostic) {
fileName = FBDeviceAgnosticNormalizedFileName(fileName);
}
if ([[UIScreen mainScreen] scale] > 1) {
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
}
fileName = [fileName stringByAppendingPathExtension:@"png"];
return fileName;
}
- (NSString *)_referenceFilePathForSelector:(SEL)selector
identifier:(NSString *)identifier
{
NSString *fileName = [self _fileNameForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeReference];
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
filePath = [filePath stringByAppendingPathComponent:fileName];
return filePath;
}
- (NSString *)_failedFilePathForSelector:(SEL)selector
identifier:(NSString *)identifier
fileNameType:(FBTestSnapshotFileNameType)fileNameType
{
NSString *fileName = [self _fileNameForSelector:selector
identifier:identifier
fileNameType:fileNameType];
NSString *folderPath = NSTemporaryDirectory();
if (getenv("IMAGE_DIFF_DIR")) {
folderPath = @(getenv("IMAGE_DIFF_DIR"));
}
NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
filePath = [filePath stringByAppendingPathComponent:fileName];
return filePath;
}
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
if (nil != referenceImage) {
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
if (!imagesSame) {
NSError *saveError = nil;
if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) {
NSLog(@"Error saving test images: %@", saveError);
}
}
return imagesSame;
}
return NO;
}
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
}
- (BOOL)_saveReferenceImage:(UIImage *)image
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
BOOL didWrite = NO;
if (nil != image) {
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
NSData *pngData = UIImagePNGRepresentation(image);
if (nil != pngData) {
NSError *creationError = nil;
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
withIntermediateDirectories:YES
attributes:nil
error:&creationError];
if (!didCreateDir) {
if (NULL != errorPtr) {
*errorPtr = creationError;
}
return NO;
}
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
if (didWrite) {
NSLog(@"Reference image save at: %@", filePath);
}
} else {
if (nil != errorPtr) {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
userInfo:@{
FBReferenceImageFilePathKey: filePath,
}];
}
}
}
return didWrite;
}
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
{
if ([viewOrLayer isKindOfClass:[UIView class]]) {
if (_usesDrawViewHierarchyInRect) {
return [UIImage fb_imageForView:viewOrLayer];
} else {
return [UIImage fb_imageForViewLayer:viewOrLayer];
}
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
return [UIImage fb_imageForLayer:viewOrLayer];
} else {
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
}
return nil;
}
@end

View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#if swift(>=3)
public extension FBSnapshotTestCase {
public func FBSnapshotVerifyView(_ view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
public func FBSnapshotVerifyLayer(_ layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
private func FBSnapshotVerifyViewOrLayer(_ viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
let envReferenceImageDirectory = self.getReferenceImageDirectory(withDefault: FB_REFERENCE_IMAGE_DIR)
var error: NSError?
var comparisonSuccess = false
if let envReferenceImageDirectory = envReferenceImageDirectory {
for suffix in suffixes {
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
if viewOrLayer.isKind(of: UIView.self) {
do {
try compareSnapshot(of: viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else if viewOrLayer.isKind(of: CALayer.self) {
do {
try compareSnapshot(of: viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else {
assertionFailure("Only UIView and CALayer classes can be snapshotted")
}
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
if comparisonSuccess || recordMode {
break
}
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
}
} else {
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
}
}
func assert(_ assertion: Bool, message: String, file: StaticString, line: UInt) {
if !assertion {
XCTFail(message, file: file, line: line)
}
}
}
#else
public extension FBSnapshotTestCase {
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
var error: NSError?
var comparisonSuccess = false
if let envReferenceImageDirectory = envReferenceImageDirectory {
for suffix in suffixes {
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
if viewOrLayer.isKindOfClass(UIView) {
do {
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else if viewOrLayer.isKindOfClass(CALayer) {
do {
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else {
assertionFailure("Only UIView and CALayer classes can be snapshotted")
}
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
if comparisonSuccess || recordMode {
break
}
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
}
} else {
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
}
}
func assert(assertion: Bool, message: String, file: StaticString, line: UInt) {
if !assertion {
XCTFail(message, file: file, line: line)
}
}
}
#endif

View File

@@ -0,0 +1,29 @@
BSD License
For the FBSnapshotTestCase software
Copyright (c) 2013, Facebook, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,97 @@
FBSnapshotTestCase
======================
[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
What it does
------------
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
`renderInContext:` method to get an image snapshot of its contents. It
compares this snapshot to a "reference image" stored in your source code
repository and fails the test if the two images don't match.
Why?
----
At Facebook we write a lot of UI code. As you might imagine, each type of
feed story is rendered using a subclass of `UIView`. There are a lot of edge
cases that we want to handle correctly:
- What if there is more text than can fit in the space available?
- What if an image doesn't match the size of an image view?
- What should the highlighted state look like?
It's straightforward to test logic code, but less obvious how you should test
views. You can do a lot of rectangle asserts, but these are hard to understand
or visualize. Looking at an image diff shows you exactly what changed and how
it will look to users.
We developed `FBSnapshotTestCase` to make snapshot tests easy.
Installation with CocoaPods
---------------------------
1. Add the following lines to your Podfile:
```
target "Tests" do
pod 'FBSnapshotTestCase'
end
```
If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
Replace "Tests" with the name of your test project.
2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
|Name|Value|
|:---|:----|
|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png)
Creating a snapshot test
------------------------
1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
2. From within your test, use `FBSnapshotVerifyView`.
3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
method. (This creates the reference images on disk.)
4. Remove the line enabling record mode and run the test.
Features
--------
- Automatically names reference images on disk according to test class and
selector.
- Prints a descriptive error message to the console on failure. (Bonus:
failure message includes a one-line command to see an image diff if
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
- Supply an optional "identifier" if you want to perform multiple snapshots
in a single test method.
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
Notes
-----
Your unit test must be an "application test", not a "logic test." (That is, it
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
and later new projects only offer application tests, but older projects will
have separate targets for the two types.
Authors
-------
`FBSnapshotTestCase` was written at Facebook by
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
[Todd Krabach](https://facebook.com/toddkrabach).
License
-------
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.

View File

@@ -0,0 +1,22 @@
{
"name": "SellyCloudSDK",
"version": "0.1.0",
"summary": "A short description of SellyCloudSDK.",
"description": "TODO: Add long description of the pod here.",
"homepage": "https://github.com/Caleb/SellyCloudSDK",
"license": {
"type": "MIT",
"file": "LICENSE"
},
"authors": {
"Caleb": "liaoqiang1123@gmail.com"
},
"source": {
"git": "https://github.com/Caleb/SellyCloudSDK.git",
"tag": "0.1.0"
},
"platforms": {
"ios": "10.0"
},
"source_files": "SellyCloudSDK/Classes/**/*"
}

View File

@@ -0,0 +1,27 @@
PODS:
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- SellyCloudSDK (0.1.0)
DEPENDENCIES:
- FBSnapshotTestCase
- SellyCloudSDK (from `../`)
SPEC REPOS:
trunk:
- FBSnapshotTestCase
EXTERNAL SOURCES:
SellyCloudSDK:
:path: "../"
SPEC CHECKSUMS:
FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
SellyCloudSDK: f71a5b97e56b934760a0c33267b5ca27b9d758db
PODFILE CHECKSUM: 9e422a4ddd0ac5aa3d5289a1a7feda4a45dcf75d
COCOAPODS: 1.16.2

File diff suppressed because it is too large Load Diff

View File

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "QuartzCore" -framework "UIKit" -framework "XCTest"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FBSnapshotTestCase
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib"
SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

@@ -0,0 +1,19 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LIBRARY_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib" "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "QuartzCore" -framework "UIKit" -framework "XCTest"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FBSnapshotTestCase
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
SWIFT_INCLUDE_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/usr/lib"
SYSTEM_FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

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

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>This application makes use of the following third party libraries:</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2026 Caleb &lt;liaoqiang1123@gmail.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SellyCloudSDK</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Acknowledgements</string>
<key>Title</key>
<string>Acknowledgements</string>
</dict>
</plist>

View File

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

View File

@@ -0,0 +1,186 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
local source="${BUILT_PRODUCTS_DIR}/$1"
elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
elif [ -r "$1" ]; then
local source="$1"
fi
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink -f "${source}")"
fi
if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
# Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied
find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do
echo "Installing $f"
install_bcsymbolmap "$f" "$destination"
rm "$f"
done
rmdir "${source}/${BCSYMBOLMAP_DIR}"
fi
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
binary="${destination}/${basename}.framework/${basename}"
if ! [ -r "$binary" ]; then
binary="${destination}/${basename}"
elif [ -L "${binary}" ]; then
echo "Destination binary is symlinked..."
dirname="$(dirname "${binary}")"
binary="${dirname}/$(readlink "${binary}")"
fi
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
strip_invalid_archs "$binary"
fi
# Resign the code if required by the build settings to avoid unstable apps
code_sign_if_enabled "${destination}/$(basename "$1")"
# Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
code_sign_if_enabled "${destination}/${lib}"
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
warn_missing_arch=${2:-true}
if [ -r "$source" ]; then
# Copy the dSYM into the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .dSYM "$source")"
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures from the dSYM.
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=1
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=0
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
local destination="${BUILT_PRODUCTS_DIR}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identity
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SellyCloudSDK/SellyCloudSDK.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SellyCloudSDK/SellyCloudSDK.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi

View File

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

View File

@@ -0,0 +1,13 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK/SellyCloudSDK.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "SellyCloudSDK"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

@@ -0,0 +1,13 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK/SellyCloudSDK.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "SellyCloudSDK"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

@@ -0,0 +1,36 @@
# Acknowledgements
This application makes use of the following third party libraries:
## FBSnapshotTestCase
BSD License
For the FBSnapshotTestCase software
Copyright (c) 2013, Facebook, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Generated by CocoaPods - https://cocoapods.org

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>This application makes use of the following third party libraries:</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>BSD License
For the FBSnapshotTestCase software
Copyright (c) 2013, Facebook, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</string>
<key>License</key>
<string>BSD</string>
<key>Title</key>
<string>FBSnapshotTestCase</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Acknowledgements</string>
<key>Title</key>
<string>Acknowledgements</string>
</dict>
</plist>

View File

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

View File

@@ -0,0 +1,186 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
local source="${BUILT_PRODUCTS_DIR}/$1"
elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
elif [ -r "$1" ]; then
local source="$1"
fi
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink -f "${source}")"
fi
if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then
# Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied
find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do
echo "Installing $f"
install_bcsymbolmap "$f" "$destination"
rm "$f"
done
rmdir "${source}/${BCSYMBOLMAP_DIR}"
fi
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
binary="${destination}/${basename}.framework/${basename}"
if ! [ -r "$binary" ]; then
binary="${destination}/${basename}"
elif [ -L "${binary}" ]; then
echo "Destination binary is symlinked..."
dirname="$(dirname "${binary}")"
binary="${dirname}/$(readlink "${binary}")"
fi
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
strip_invalid_archs "$binary"
fi
# Resign the code if required by the build settings to avoid unstable apps
code_sign_if_enabled "${destination}/$(basename "$1")"
# Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
code_sign_if_enabled "${destination}/${lib}"
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
warn_missing_arch=${2:-true}
if [ -r "$source" ]; then
# Copy the dSYM into the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .dSYM "$source")"
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures from the dSYM.
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 0 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
mkdir -p "${DWARF_DSYM_FOLDER_PATH}"
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=1
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=0
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
local destination="${BUILT_PRODUCTS_DIR}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identity
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi

View File

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

View File

@@ -0,0 +1,16 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase" "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK/SellyCloudSDK.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "Foundation" -framework "QuartzCore" -framework "SellyCloudSDK" -framework "UIKit" -framework "XCTest"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

@@ -0,0 +1,16 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase" "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK/SellyCloudSDK.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift "$(PLATFORM_DIR)/Developer/Library/Frameworks" '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "Foundation" -framework "QuartzCore" -framework "SellyCloudSDK" -framework "UIKit" -framework "XCTest"
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

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

View File

@@ -0,0 +1,12 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SellyCloudSDK
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@@ -0,0 +1,622 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
3D07BA4BC5ECC163A2DF8870 /* Pods_SellyCloudSDK_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31212504658FA418499ACDD1 /* Pods_SellyCloudSDK_Example.framework */; };
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; };
6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; };
6003F59E195388D20070C39A /* SELLYAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* SELLYAppDelegate.m */; };
6003F5A7195388D20070C39A /* SELLYViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* SELLYViewController.m */; };
6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; };
6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; };
71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
89CB040112A1197CB94AC194 /* Pods_SellyCloudSDK_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B81B32948F87BA30542BD743 /* Pods_SellyCloudSDK_Tests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6003F582195388D10070C39A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 6003F589195388D20070C39A;
remoteInfo = SellyCloudSDK;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
11A34818832D1624173E15E2 /* Pods-SellyCloudSDK_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SellyCloudSDK_Example.debug.xcconfig"; path = "Target Support Files/Pods-SellyCloudSDK_Example/Pods-SellyCloudSDK_Example.debug.xcconfig"; sourceTree = "<group>"; };
31212504658FA418499ACDD1 /* Pods_SellyCloudSDK_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SellyCloudSDK_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6003F58A195388D20070C39A /* SellyCloudSDK_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SellyCloudSDK_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
6003F595195388D20070C39A /* SellyCloudSDK-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SellyCloudSDK-Info.plist"; sourceTree = "<group>"; };
6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
6003F59B195388D20070C39A /* SellyCloudSDK-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SellyCloudSDK-Prefix.pch"; sourceTree = "<group>"; };
6003F59C195388D20070C39A /* SELLYAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SELLYAppDelegate.h; sourceTree = "<group>"; };
6003F59D195388D20070C39A /* SELLYAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SELLYAppDelegate.m; sourceTree = "<group>"; };
6003F5A5195388D20070C39A /* SELLYViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SELLYViewController.h; sourceTree = "<group>"; };
6003F5A6195388D20070C39A /* SELLYViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SELLYViewController.m; sourceTree = "<group>"; };
6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
6003F5AE195388D20070C39A /* SellyCloudSDK_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SellyCloudSDK_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = "<group>"; };
71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
7778EA7B34C3774F4CDB451C /* Pods-SellyCloudSDK_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SellyCloudSDK_Tests.release.xcconfig"; path = "Target Support Files/Pods-SellyCloudSDK_Tests/Pods-SellyCloudSDK_Tests.release.xcconfig"; sourceTree = "<group>"; };
7CF4788024F32AA726C4208E /* Pods-SellyCloudSDK_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SellyCloudSDK_Tests.debug.xcconfig"; path = "Target Support Files/Pods-SellyCloudSDK_Tests/Pods-SellyCloudSDK_Tests.debug.xcconfig"; sourceTree = "<group>"; };
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
93A182F2BD66CE238683E27D /* SellyCloudSDK.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SellyCloudSDK.podspec; path = ../SellyCloudSDK.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
B50BB82A8D33974146A5FD75 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
B81B32948F87BA30542BD743 /* Pods_SellyCloudSDK_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SellyCloudSDK_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C4A6E73488D4E3D1CD8011A0 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
F5C3AD3F14AED18A42CAB536 /* Pods-SellyCloudSDK_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SellyCloudSDK_Example.release.xcconfig"; path = "Target Support Files/Pods-SellyCloudSDK_Example/Pods-SellyCloudSDK_Example.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6003F587195388D20070C39A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
3D07BA4BC5ECC163A2DF8870 /* Pods_SellyCloudSDK_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6003F5AB195388D20070C39A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
89CB040112A1197CB94AC194 /* Pods_SellyCloudSDK_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6003F581195388D10070C39A = {
isa = PBXGroup;
children = (
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
6003F593195388D20070C39A /* Example for SellyCloudSDK */,
6003F5B5195388D20070C39A /* Tests */,
6003F58C195388D20070C39A /* Frameworks */,
6003F58B195388D20070C39A /* Products */,
86515A93FA7B4DC292F60779 /* Pods */,
);
sourceTree = "<group>";
};
6003F58B195388D20070C39A /* Products */ = {
isa = PBXGroup;
children = (
6003F58A195388D20070C39A /* SellyCloudSDK_Example.app */,
6003F5AE195388D20070C39A /* SellyCloudSDK_Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
6003F58C195388D20070C39A /* Frameworks */ = {
isa = PBXGroup;
children = (
6003F58D195388D20070C39A /* Foundation.framework */,
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
6003F5AF195388D20070C39A /* XCTest.framework */,
31212504658FA418499ACDD1 /* Pods_SellyCloudSDK_Example.framework */,
B81B32948F87BA30542BD743 /* Pods_SellyCloudSDK_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
6003F593195388D20070C39A /* Example for SellyCloudSDK */ = {
isa = PBXGroup;
children = (
6003F59C195388D20070C39A /* SELLYAppDelegate.h */,
6003F59D195388D20070C39A /* SELLYAppDelegate.m */,
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
6003F5A5195388D20070C39A /* SELLYViewController.h */,
6003F5A6195388D20070C39A /* SELLYViewController.m */,
71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */,
6003F5A8195388D20070C39A /* Images.xcassets */,
6003F594195388D20070C39A /* Supporting Files */,
);
name = "Example for SellyCloudSDK";
path = SellyCloudSDK;
sourceTree = "<group>";
};
6003F594195388D20070C39A /* Supporting Files */ = {
isa = PBXGroup;
children = (
6003F595195388D20070C39A /* SellyCloudSDK-Info.plist */,
6003F596195388D20070C39A /* InfoPlist.strings */,
6003F599195388D20070C39A /* main.m */,
6003F59B195388D20070C39A /* SellyCloudSDK-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
6003F5B5195388D20070C39A /* Tests */ = {
isa = PBXGroup;
children = (
6003F5BB195388D20070C39A /* Tests.m */,
6003F5B6195388D20070C39A /* Supporting Files */,
);
path = Tests;
sourceTree = "<group>";
};
6003F5B6195388D20070C39A /* Supporting Files */ = {
isa = PBXGroup;
children = (
6003F5B7195388D20070C39A /* Tests-Info.plist */,
6003F5B8195388D20070C39A /* InfoPlist.strings */,
606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
93A182F2BD66CE238683E27D /* SellyCloudSDK.podspec */,
B50BB82A8D33974146A5FD75 /* README.md */,
C4A6E73488D4E3D1CD8011A0 /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
86515A93FA7B4DC292F60779 /* Pods */ = {
isa = PBXGroup;
children = (
11A34818832D1624173E15E2 /* Pods-SellyCloudSDK_Example.debug.xcconfig */,
F5C3AD3F14AED18A42CAB536 /* Pods-SellyCloudSDK_Example.release.xcconfig */,
7CF4788024F32AA726C4208E /* Pods-SellyCloudSDK_Tests.debug.xcconfig */,
7778EA7B34C3774F4CDB451C /* Pods-SellyCloudSDK_Tests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
6003F589195388D20070C39A /* SellyCloudSDK_Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "SellyCloudSDK_Example" */;
buildPhases = (
612F82E020B991FDE499E5EC /* [CP] Check Pods Manifest.lock */,
6003F586195388D20070C39A /* Sources */,
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
CE5F972E2E3B410E72578FD4 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = SellyCloudSDK_Example;
productName = SellyCloudSDK;
productReference = 6003F58A195388D20070C39A /* SellyCloudSDK_Example.app */;
productType = "com.apple.product-type.application";
};
6003F5AD195388D20070C39A /* SellyCloudSDK_Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "SellyCloudSDK_Tests" */;
buildPhases = (
EF2165ABF7379C7EA6E6585B /* [CP] Check Pods Manifest.lock */,
6003F5AA195388D20070C39A /* Sources */,
6003F5AB195388D20070C39A /* Frameworks */,
6003F5AC195388D20070C39A /* Resources */,
F3A174A6B9634C8AC678DD80 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
6003F5B4195388D20070C39A /* PBXTargetDependency */,
);
name = SellyCloudSDK_Tests;
productName = SellyCloudSDKTests;
productReference = 6003F5AE195388D20070C39A /* SellyCloudSDK_Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6003F582195388D10070C39A /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = SELLY;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = Caleb;
TargetAttributes = {
6003F5AD195388D20070C39A = {
TestTargetID = 6003F589195388D20070C39A;
};
};
};
buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "SellyCloudSDK" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 6003F581195388D10070C39A;
productRefGroup = 6003F58B195388D20070C39A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6003F589195388D20070C39A /* SellyCloudSDK_Example */,
6003F5AD195388D20070C39A /* SellyCloudSDK_Tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6003F588195388D20070C39A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */,
71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */,
6003F5A9195388D20070C39A /* Images.xcassets in Resources */,
6003F598195388D20070C39A /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6003F5AC195388D20070C39A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
612F82E020B991FDE499E5EC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-SellyCloudSDK_Example-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
CE5F972E2E3B410E72578FD4 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SellyCloudSDK_Example/Pods-SellyCloudSDK_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/SellyCloudSDK/SellyCloudSDK.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SellyCloudSDK.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SellyCloudSDK_Example/Pods-SellyCloudSDK_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
EF2165ABF7379C7EA6E6585B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-SellyCloudSDK_Tests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F3A174A6B9634C8AC678DD80 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SellyCloudSDK_Tests/Pods-SellyCloudSDK_Tests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSnapshotTestCase.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SellyCloudSDK_Tests/Pods-SellyCloudSDK_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6003F586195388D20070C39A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6003F59E195388D20070C39A /* SELLYAppDelegate.m in Sources */,
6003F5A7195388D20070C39A /* SELLYViewController.m in Sources */,
6003F59A195388D20070C39A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6003F5AA195388D20070C39A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6003F5BC195388D20070C39A /* Tests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
6003F5B4195388D20070C39A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 6003F589195388D20070C39A /* SellyCloudSDK_Example */;
targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
6003F596195388D20070C39A /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
6003F597195388D20070C39A /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
6003F5B8195388D20070C39A /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
6003F5B9195388D20070C39A /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
71719F9E1E33DC2100824A3D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
6003F5BD195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
6003F5BE195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
6003F5C0195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 11A34818832D1624173E15E2 /* Pods-SellyCloudSDK_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SellyCloudSDK/SellyCloudSDK-Prefix.pch";
INFOPLIST_FILE = "SellyCloudSDK/SellyCloudSDK-Info.plist";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
WRAPPER_EXTENSION = app;
};
name = Debug;
};
6003F5C1195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F5C3AD3F14AED18A42CAB536 /* Pods-SellyCloudSDK_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SellyCloudSDK/SellyCloudSDK-Prefix.pch";
INFOPLIST_FILE = "SellyCloudSDK/SellyCloudSDK-Info.plist";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
WRAPPER_EXTENSION = app;
};
name = Release;
};
6003F5C3195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7CF4788024F32AA726C4208E /* Pods-SellyCloudSDK_Tests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "Tests/Tests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SellyCloudSDK_Example.app/SellyCloudSDK_Example";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
6003F5C4195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7778EA7B34C3774F4CDB451C /* Pods-SellyCloudSDK_Tests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
INFOPLIST_FILE = "Tests/Tests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SellyCloudSDK_Example.app/SellyCloudSDK_Example";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6003F585195388D10070C39A /* Build configuration list for PBXProject "SellyCloudSDK" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5BD195388D20070C39A /* Debug */,
6003F5BE195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "SellyCloudSDK_Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5C0195388D20070C39A /* Debug */,
6003F5C1195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "SellyCloudSDK_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5C3195388D20070C39A /* Debug */,
6003F5C4195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6003F582195388D10070C39A /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:SellyCloudSDK.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "SellyCloudSDK_Example.app"
BlueprintName = "SellyCloudSDK_Example"
ReferencedContainer = "container:SellyCloudSDK.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F5AD195388D20070C39A"
BuildableName = "SellyCloudSDK_Tests.xctest"
BlueprintName = "SellyCloudSDK_Tests"
ReferencedContainer = "container:SellyCloudSDK.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "SellyCloudSDK_Example.app"
BlueprintName = "SellyCloudSDK_Example"
ReferencedContainer = "container:SellyCloudSDK.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "SellyCloudSDK_Example.app"
BlueprintName = "SellyCloudSDK_Example"
ReferencedContainer = "container:SellyCloudSDK.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "SellyCloudSDK_Example.app"
BlueprintName = "SellyCloudSDK_Example"
ReferencedContainer = "container:SellyCloudSDK.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:SellyCloudSDK.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="whP-gf-Uak">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="wQg-tq-qST">
<objects>
<viewController id="whP-gf-Uak" customClass="SELLYViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/>
<viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="TpU-gO-2f1">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="305" y="433"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,15 @@
//
// SELLYAppDelegate.h
// SellyCloudSDK
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
@import UIKit;
@interface SELLYAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,46 @@
//
// SELLYAppDelegate.m
// SellyCloudSDK
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
#import "SELLYAppDelegate.h"
@implementation SELLYAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View File

@@ -0,0 +1,13 @@
//
// SELLYViewController.h
// SellyCloudSDK
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
@import UIKit;
@interface SELLYViewController : UIViewController
@end

View File

@@ -0,0 +1,29 @@
//
// SELLYViewController.m
// SellyCloudSDK
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
#import "SELLYViewController.h"
@interface SELLYViewController ()
@end
@implementation SELLYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,16 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
@import UIKit;
@import Foundation;
#endif

View File

@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View File

@@ -0,0 +1,17 @@
//
// main.m
// SellyCloudSDK
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
@import UIKit;
#import "SELLYAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([SELLYAppDelegate class]));
}
}

View File

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

View File

@@ -0,0 +1,7 @@
// The contents of this file are implicitly included at the beginning of every test case source file.
#ifdef __OBJC__
@import FBSnapshotTestCase;
#endif

View File

@@ -0,0 +1,35 @@
//
// SellyCloudSDKTests.m
// SellyCloudSDKTests
//
// Created by Caleb on 01/14/2026.
// Copyright (c) 2026 Caleb. All rights reserved.
//
@import XCTest;
@interface Tests : XCTestCase
@end
@implementation Tests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end

View File

@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View File

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

View File

@@ -0,0 +1,29 @@
# SellyCloudSDK
[![CI Status](https://img.shields.io/travis/Caleb/SellyCloudSDK.svg?style=flat)](https://travis-ci.org/Caleb/SellyCloudSDK)
[![Version](https://img.shields.io/cocoapods/v/SellyCloudSDK.svg?style=flat)](https://cocoapods.org/pods/SellyCloudSDK)
[![License](https://img.shields.io/cocoapods/l/SellyCloudSDK.svg?style=flat)](https://cocoapods.org/pods/SellyCloudSDK)
[![Platform](https://img.shields.io/cocoapods/p/SellyCloudSDK.svg?style=flat)](https://cocoapods.org/pods/SellyCloudSDK)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
SellyCloudSDK is available through [CocoaPods](https://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'SellyCloudSDK'
```
## Author
Caleb, liaoqiang1123@gmail.com
## License
SellyCloudSDK is available under the MIT license. See the LICENSE file for more info.

View File

@@ -0,0 +1,55 @@
#
# Be sure to run `pod lib lint SellyCloudSDK.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'SellyCloudSDK'
s.version = '0.1.0'
s.summary = 'A short description of SellyCloudSDK.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/Caleb/SellyCloudSDK'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Caleb' => 'liaoqiang1123@gmail.com' }
s.source = { :git => 'https://github.com/Caleb/SellyCloudSDK.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '13.0'
s.swift_version = "5.10"
s.source_files = 'SellyCloudSDK/Classes/**/*'
s.pod_target_xcconfig = { 'VALID_ARCHS' => 'x86_64 arm64' ,
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64',
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
'OTHER_LDFLAGS' => '-ObjC'
}
s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }
s.resources = ['SellyCloud/sdk/SellyCloudSDK.framework/*.bundle']
s.vendored_frameworks = 'SellyCloudSDK/sdk/*.framework'
s.dependency 'CocoaLumberjack', '~>3.7.2'
s.dependency 'CocoaLumberjack/Swift'
s.dependency 'ReactiveObjC'
s.dependency 'AFNetworking'
s.dependency 'Reachability'
s.dependency 'YYModel'
s.dependency 'SSZipArchive'
s.dependency 'SocketRocket'
s.dependency 'Logboard', '~> 2.5.0'
end

View File

@@ -0,0 +1,40 @@
//
// LFLiveDebug.h
// LaiFeng
//
// Created by LaiFeng on 16/5/20.
// Copyright © 2016年 LaiFeng All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface LFLiveDebug : NSObject
@property (nonatomic, copy) NSString *streamId; ///< 流id
@property (nonatomic, copy) NSString *uploadUrl; ///< 流地址
@property (nonatomic, assign) CGSize videoSize; ///< 上传的分辨率
@property (nonatomic, assign) BOOL isRtmp; ///< 上传方式TCP or RTMP
@property (nonatomic, assign) CGFloat elapsedMilli; ///< 距离上次统计的时间 单位ms
@property (nonatomic, assign) CGFloat timeStamp; ///< 当前的时间戳从而计算1s内数据
@property (nonatomic, assign) CGFloat dataFlow; ///< 总流量
@property (nonatomic, assign) CGFloat bandwidth; ///< 1s内总带宽
@property (nonatomic, assign) CGFloat currentBandwidth; ///< 上次的带宽
@property (nonatomic, assign) CGFloat audioBandwidth;
@property (nonatomic, assign) CGFloat videoBandwidth;
@property (nonatomic, assign) CGFloat currentAudioBandwidth;
@property (nonatomic, assign) CGFloat currentVideoBandwidth;
@property (nonatomic, assign) NSInteger dropFrame; ///< 丢掉的帧数
@property (nonatomic, assign) NSInteger totalFrame; ///< 总帧数
@property (nonatomic, assign) NSInteger capturedAudioCount; ///< 1s内音频捕获个数
@property (nonatomic, assign) NSInteger capturedVideoCount; ///< 1s内视频捕获个数
@property (nonatomic, assign) NSInteger currentCapturedAudioCount; ///< 上次的音频捕获个数
@property (nonatomic, assign) NSInteger currentCapturedVideoCount; ///< 上次的视频捕获个数
@property (nonatomic, assign) NSInteger unSendCount; ///< 未发送个数(代表当前缓冲区等待发送的)
@end

View File

@@ -0,0 +1,47 @@
//
// SellyCloudManager.h
// SellyCloudSDK
//
// Created by Caleb on 7/7/25.
//
#import <Foundation/Foundation.h>
#import "SellyLiveVideoConfiguration.h"
#import "SellyLiveAudioConfiguration.h"
#import "SellyLivePusherDelegate.h"
#import "SellyLivePlayerDelegate.h"
#import "LFLiveDebug.h"
#import "SellyPublicDefinition.h"
#import "SellyLiveVideoPlayer.h"
#import "SellyLiveVideoPusher.h"
#import "SellyLivePusherStats.h"
#import "SellyRTCSession.h"
#import "SellyRTCVideoConfiguration.h"
#import "SellyRTCSessionDelegate.h"
#import "SellyRTCReplayKitHandler.h"
#import "SellyCloudSDKError.h"
#import "SellyVodVideoPlayer.h"
#import "SellyVodPlayerDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@interface SellyCloudManager : NSObject
+ (instancetype)sharedInstance;
//用户id
@property (nonatomic, strong)NSString *userId;
- (void)startWithVHost:(NSString *)vhost appName:(NSString *)appName;
/**
设置代理服务器地址(如洋葱盾转化后的地址)
@param address 代理地址,格式如 http://127.0.0.1:12345
*/
+ (void)setProxyAddress:(NSString *)address;
+ (NSString *)sdkVersion;
+ (void)uploadLog:(void(^)(NSString * _Nullable url, NSError * _Nullable error))callback;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,436 @@
#if 0
#elif defined(__arm64__) && __arm64__
// Generated by Apple Swift version 6.2.3 effective-5.10 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
#ifndef SELLYCLOUDSDK_SWIFT_H
#define SELLYCLOUDSDK_SWIFT_H
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgcc-compat"
#if !defined(__has_include)
# define __has_include(x) 0
#endif
#if !defined(__has_attribute)
# define __has_attribute(x) 0
#endif
#if !defined(__has_feature)
# define __has_feature(x) 0
#endif
#if !defined(__has_warning)
# define __has_warning(x) 0
#endif
#if __has_include(<swift/objc-prologue.h>)
# include <swift/objc-prologue.h>
#endif
#pragma clang diagnostic ignored "-Wauto-import"
#if defined(__OBJC__)
#include <Foundation/Foundation.h>
#endif
#if defined(__cplusplus)
#include <cstdint>
#include <cstddef>
#include <cstdbool>
#include <cstring>
#include <stdlib.h>
#include <new>
#include <type_traits>
#else
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#endif
#if defined(__cplusplus)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module"
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
# include <ptrauth.h>
#else
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# ifndef __ptrauth_swift_value_witness_function_pointer
# define __ptrauth_swift_value_witness_function_pointer(x)
# endif
# ifndef __ptrauth_swift_class_method_pointer
# define __ptrauth_swift_class_method_pointer(x)
# endif
#pragma clang diagnostic pop
#endif
#pragma clang diagnostic pop
#endif
#if !defined(SWIFT_TYPEDEFS)
# define SWIFT_TYPEDEFS 1
# if __has_include(<uchar.h>)
# include <uchar.h>
# elif !defined(__cplusplus)
typedef unsigned char char8_t;
typedef uint_least16_t char16_t;
typedef uint_least32_t char32_t;
# endif
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
#endif
#if !defined(SWIFT_PASTE)
# define SWIFT_PASTE_HELPER(x, y) x##y
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
#endif
#if !defined(SWIFT_METATYPE)
# define SWIFT_METATYPE(X) Class
#endif
#if !defined(SWIFT_CLASS_PROPERTY)
# if __has_feature(objc_class_property)
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
# else
# define SWIFT_CLASS_PROPERTY(...)
# endif
#endif
#if !defined(SWIFT_RUNTIME_NAME)
# if __has_attribute(objc_runtime_name)
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
# else
# define SWIFT_RUNTIME_NAME(X)
# endif
#endif
#if !defined(SWIFT_COMPILE_NAME)
# if __has_attribute(swift_name)
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
# else
# define SWIFT_COMPILE_NAME(X)
# endif
#endif
#if !defined(SWIFT_METHOD_FAMILY)
# if __has_attribute(objc_method_family)
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
# else
# define SWIFT_METHOD_FAMILY(X)
# endif
#endif
#if !defined(SWIFT_NOESCAPE)
# if __has_attribute(noescape)
# define SWIFT_NOESCAPE __attribute__((noescape))
# else
# define SWIFT_NOESCAPE
# endif
#endif
#if !defined(SWIFT_RELEASES_ARGUMENT)
# if __has_attribute(ns_consumed)
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
# else
# define SWIFT_RELEASES_ARGUMENT
# endif
#endif
#if !defined(SWIFT_WARN_UNUSED_RESULT)
# if __has_attribute(warn_unused_result)
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
# else
# define SWIFT_WARN_UNUSED_RESULT
# endif
#endif
#if !defined(SWIFT_NORETURN)
# if __has_attribute(noreturn)
# define SWIFT_NORETURN __attribute__((noreturn))
# else
# define SWIFT_NORETURN
# endif
#endif
#if !defined(SWIFT_CLASS_EXTRA)
# define SWIFT_CLASS_EXTRA
#endif
#if !defined(SWIFT_PROTOCOL_EXTRA)
# define SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_ENUM_EXTRA)
# define SWIFT_ENUM_EXTRA
#endif
#if !defined(SWIFT_CLASS)
# if __has_attribute(objc_subclassing_restricted)
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# else
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
# endif
#endif
#if !defined(SWIFT_RESILIENT_CLASS)
# if __has_attribute(objc_class_stub)
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
# else
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
# endif
#endif
#if !defined(SWIFT_PROTOCOL)
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
#endif
#if !defined(SWIFT_EXTENSION)
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
#endif
#if !defined(OBJC_DESIGNATED_INITIALIZER)
# if __has_attribute(objc_designated_initializer)
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
# else
# define OBJC_DESIGNATED_INITIALIZER
# endif
#endif
#if !defined(SWIFT_ENUM_ATTR)
# if __has_attribute(enum_extensibility)
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
# else
# define SWIFT_ENUM_ATTR(_extensibility)
# endif
#endif
#if !defined(SWIFT_ENUM)
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# if __has_feature(generalized_swift_name)
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
# else
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
# endif
#endif
#if !defined(SWIFT_UNAVAILABLE)
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
#endif
#if !defined(SWIFT_UNAVAILABLE_MSG)
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
#endif
#if !defined(SWIFT_AVAILABILITY)
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
#endif
#if !defined(SWIFT_WEAK_IMPORT)
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#endif
#if !defined(SWIFT_DEPRECATED)
# define SWIFT_DEPRECATED __attribute__((deprecated))
#endif
#if !defined(SWIFT_DEPRECATED_MSG)
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
#endif
#if !defined(SWIFT_DEPRECATED_OBJC)
# if __has_feature(attribute_diagnose_if_objc)
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
# else
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
# endif
#endif
#if defined(__OBJC__)
#if !defined(IBSegueAction)
# define IBSegueAction
#endif
#endif
#if !defined(SWIFT_EXTERN)
# if defined(__cplusplus)
# define SWIFT_EXTERN extern "C"
# else
# define SWIFT_EXTERN extern
# endif
#endif
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif
#if !defined(SWIFT_INDIRECT_RESULT)
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#endif
#if !defined(SWIFT_CONTEXT)
# define SWIFT_CONTEXT __attribute__((swift_context))
#endif
#if !defined(SWIFT_ERROR_RESULT)
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
#endif
#if defined(__cplusplus)
# define SWIFT_NOEXCEPT noexcept
#else
# define SWIFT_NOEXCEPT
#endif
#if !defined(SWIFT_C_INLINE_THUNK)
# if __has_attribute(always_inline)
# if __has_attribute(nodebug)
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
# else
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
# endif
# else
# define SWIFT_C_INLINE_THUNK inline
# endif
#endif
#if defined(_WIN32)
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
#endif
#else
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
# define SWIFT_IMPORT_STDLIB_SYMBOL
#endif
#endif
#if defined(__OBJC__)
#if __has_feature(objc_modules)
#if __has_warning("-Watimport-in-framework-header")
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
#endif
@import AVFoundation;
@import CoreFoundation;
@import Foundation;
@import MetalKit;
@import ObjectiveC;
@import UIKit;
#endif
#endif
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
#if __has_warning("-Wpragma-clang-attribute")
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
#endif
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wnullability"
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#if __has_attribute(external_source_symbol)
# pragma push_macro("any")
# undef any
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="SellyCloudSDK",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
# pragma pop_macro("any")
#endif
#if defined(__OBJC__)
@class UIView;
@protocol HKRtmpPusherProtocol;
@class SellyLiveVideoConfiguration;
@class SellyLiveAudioConfiguration;
@class NSString;
SWIFT_CLASS("_TtC13SellyCloudSDK12HKRtmpPusher")
@interface HKRtmpPusher : NSObject
@property (nonatomic, strong) UIView * _Nullable preview;
@property (nonatomic) NSInteger reconnectInterval;
@property (nonatomic) NSInteger reconnectCount;
@property (nonatomic, strong) id <HKRtmpPusherProtocol> _Nullable delegate;
@property (nonatomic) enum AVCaptureDevicePosition currentPosition;
@property (nonatomic) BOOL muted;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (void)startRunning:(enum AVCaptureDevicePosition)captureDevicePosition videoConfig:(SellyLiveVideoConfiguration * _Nonnull)videoConfig audioConfig:(SellyLiveAudioConfiguration * _Nonnull)audioConfig;
- (void)startRunningAudio:(SellyLiveAudioConfiguration * _Nonnull)audioConfig;
- (void)startLive:(NSString * _Nonnull)urlStr;
- (void)stopLive;
- (void)startCamera;
- (void)stopCamera;
- (void)switchCamera;
- (void)setPreviewView:(UIView * _Nonnull)view;
@end
@class SellyLivePusherStats;
SWIFT_PROTOCOL("_TtP13SellyCloudSDK20HKRtmpPusherProtocol_")
@protocol HKRtmpPusherProtocol
///
- (void)pusher:(HKRtmpPusher * _Nonnull)pusher onPushStatusChanged:(NSString * _Nonnull)status;
@optional
- (void)pusher:(HKRtmpPusher * _Nonnull)pusher onLiveStatsChanged:(SellyLivePusherStats * _Nonnull)stats;
@end
/// The <code>IOStream</code> class is the foundation of a RTMPStream.
SWIFT_CLASS("_TtC13SellyCloudSDK8IOStream")
@interface IOStream : NSObject
/// The number of frames per second being displayed.
@property (nonatomic, readonly) uint16_t currentFPS;
/// Creates an object.
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
@class NSCoder;
@protocol MTLDevice;
/// A view that displays a video content of a NetStream object which uses Metal api.
SWIFT_CLASS("_TtC13SellyCloudSDK8MTHKView")
@interface MTHKView : MTKView
/// Initializes and returns a newly allocated view object with the specified frame rectangle.
- (nonnull instancetype)initWithFrame:(CGRect)frame OBJC_DESIGNATED_INITIALIZER;
/// Returns an object initialized from data in a given unarchiver.
- (nonnull instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER;
/// Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
- (void)awakeFromNib;
- (void)drawRect:(CGRect)rect;
- (nonnull instancetype)initWithFrame:(CGRect)frameRect device:(id <MTLDevice> _Nullable)device SWIFT_UNAVAILABLE;
@end
/// The NetSocket class creates a two-way connection between a client and a server as a client. This class is wrapper for a InputStream and an OutputStream.
SWIFT_CLASS("_TtC13SellyCloudSDK9NetSocket")
@interface NetSocket : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end
/// The NetClient class creates a two-way connection between a NetService.
SWIFT_CLASS("_TtC13SellyCloudSDK9NetClient")
@interface NetClient : NetSocket
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
/// The NetService class creates a two-way connection between a client and a server as a server.
SWIFT_CLASS("_TtC13SellyCloudSDK10NetService")
@interface NetService : NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
@class NSNetService;
@class NSInputStream;
@class NSOutputStream;
@interface NetService (SWIFT_EXTENSION(SellyCloudSDK)) <NSNetServiceDelegate>
- (void)netService:(NSNetService * _Nonnull)sender didAcceptConnectionWithInputStream:(NSInputStream * _Nonnull)inputStream outputStream:(NSOutputStream * _Nonnull)outputStream;
@end
@class NSStream;
@interface NetSocket (SWIFT_EXTENSION(SellyCloudSDK)) <NSStreamDelegate>
- (void)stream:(NSStream * _Nonnull)aStream handleEvent:(NSStreamEvent)eventCode;
@end
@class AVSampleBufferDisplayLayer;
/// A view that displays a video content of a NetStream object which uses AVSampleBufferDisplayLayer api.
SWIFT_CLASS("_TtC13SellyCloudSDK9PiPHKView")
@interface PiPHKView : UIView
/// Returns the class used to create the layer for instances of this class.
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) Class _Nonnull layerClass;)
+ (Class _Nonnull)layerClass SWIFT_WARN_UNUSED_RESULT;
/// The views Core Animation layer used for rendering.
@property (nonatomic, readonly, strong) AVSampleBufferDisplayLayer * _Nonnull layer;
/// Initializes and returns a newly allocated view object with the specified frame rectangle.
- (nonnull instancetype)initWithFrame:(CGRect)frame OBJC_DESIGNATED_INITIALIZER;
/// Returns an object initialized from data in a given unarchiver.
- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER;
/// Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
- (void)awakeFromNib;
@end
/// An object that provides the interface to control a one-way channel over a RtmpConnection.
SWIFT_CLASS("_TtC13SellyCloudSDK10RTMPStream")
@interface RTMPStream : IOStream
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
#endif
#if __has_attribute(external_source_symbol)
# pragma clang attribute pop
#endif
#if defined(__cplusplus)
#endif
#pragma clang diagnostic pop
#endif
#else
#error unsupported Swift architecture
#endif

View File

@@ -0,0 +1,38 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "SellyCloudSDKError.h"
#import "SellyLiveAudioConfiguration.h"
#import "SellyLivePlayerDelegate.h"
#import "SellyLivePlayerStats.h"
#import "SellyLivePusherDelegate.h"
#import "SellyLivePusherStats.h"
#import "SellyLiveVideoConfiguration.h"
#import "SellyLiveVideoPlayer.h"
#import "SellyLiveVideoPusher.h"
#import "SellyPlayerStreamInfo.h"
#import "SellyPublicDefinition.h"
#import "SellyRTCReplayKitHandler.h"
#import "SellyRTCSession.h"
#import "SellyRTCSessionDelegate.h"
#import "SellyRTCStats.h"
#import "SellyRtcVideoCanvas.h"
#import "SellyRTCVideoConfiguration.h"
#import "SellyRTCVideoFrame.h"
#import "SellyVodPlayerDelegate.h"
#import "SellyVodVideoPlayer.h"
#import "SellyCloudManager.h"
#import "LFLiveDebug.h"
FOUNDATION_EXPORT double SellyCloudSDKVersionNumber;
FOUNDATION_EXPORT const unsigned char SellyCloudSDKVersionString[];

View File

@@ -0,0 +1,170 @@
//
// SellyCloudSDKError.h
// SellyCloudSDK
//
// Created on 19/12/25.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Error Domain
/// SellyCloudSDK 错误域
FOUNDATION_EXPORT NSErrorDomain const SellyCloudSDKErrorDomain;
#pragma mark - Error Code Ranges
/*
错误代码范围规划:
1000-1999: 通用错误(参数校验、配置错误等)
2000-2999: 直播推流相关错误
3000-3999: 视频播放相关错误
4000-4999: 网络请求相关错误
5000-5999: 信令相关错误WebSocket、RTC信令
6000-6999: 音视频会议相关错误
7000-7999: 媒体设备相关错误
8000-8999: 编解码相关错误
9000-9999: 预留
*/
#pragma mark - Common Errors (1000-1999)
typedef NS_ENUM(NSInteger, SellyCloudSDKErrorCode) {
// MARK: - 通用错误 (1000-1999)
/// 通用错误(具体错误见描述)
SellyCloudSDKErrorCodeGeneral = 1000,
/// 参数为空
SellyCloudSDKErrorCodeEmptyParameter = 1001,
/// 配置错误
SellyCloudSDKErrorCodeInvalidConfiguration = 1002,
/// SDK 未初始化
SellyCloudSDKErrorCodeNotInitialized = 1003,
/// 操作不支持
SellyCloudSDKErrorCodeOperationNotSupported = 1004,
/// 重复操作
SellyCloudSDKErrorCodeDuplicateOperation = 1005,
// MARK: - 直播推流错误 (2000-2999)
/// URL/StreamID 不能为空
SellyCloudSDKErrorCodePushURLEmpty = 2000,
/// 预览窗口不能为空
SellyCloudSDKErrorCodePreviewEmpty = 2001,
/// 语音直播下无法开启相机
SellyCloudSDKErrorCodeCannotStartCameraInAudioMode = 2002,
/// 推流已经在运行
SellyCloudSDKErrorCodePushAlreadyRunning = 2003,
/// 推流未运行
SellyCloudSDKErrorCodePushNotRunning = 2004,
/// 推流连接失败
SellyCloudSDKErrorCodePushConnectionFailed = 2005,
/// 推流断开连接
SellyCloudSDKErrorCodePushDisconnected = 2006,
/// 推流超时
SellyCloudSDKErrorCodePushTimeout = 2007,
// MARK: - 视频播放错误 (3000-3999)
/// 播放 URL 为空
SellyCloudSDKErrorCodePlayURLEmpty = 3000,
/// 播放器初始化失败
SellyCloudSDKErrorCodePlayerInitFailed = 3001,
/// 播放失败
SellyCloudSDKErrorCodePlaybackFailed = 3002,
/// 播放超时
SellyCloudSDKErrorCodePlaybackTimeout = 3003,
// MARK: - 网络请求错误 (4000-4999)
/// AppKey/Name 未初始化
SellyCloudSDKErrorCodeAppKeyNotInitialized = 4000,
/// 网络请求失败
SellyCloudSDKErrorCodeNetworkRequestFailed = 4001,
/// 网络连接超时
SellyCloudSDKErrorCodeNetworkTimeout = 4002,
/// 服务器返回错误
SellyCloudSDKErrorCodeServerError = 4003,
/// 数据解析失败
SellyCloudSDKErrorCodeDataParseFailed = 4004,
// MARK: - 信令相关错误 (5000-5999)
/// WebSocket 连接失败
SellyCloudSDKErrorCodeWebSocketConnectionFailed = 5000,
/// WebSocket 断开连接
SellyCloudSDKErrorCodeWebSocketDisconnected = 5001,
/// 信令超时无响应
SellyCloudSDKErrorCodeSignalNoACK = 5002,
/// 信令消息为空
SellyCloudSDKErrorCodeSignalMessageEmpty = 5003,
/// 信令消息格式错误
SellyCloudSDKErrorCodeSignalMessageInvalid = 5004,
// MARK: - 音视频会议错误 (6000-6999)
/// RTC 连接失败
SellyCloudSDKErrorCodeRTCConnectionFailed = 6000,
/// RTC 会议创建失败
SellyCloudSDKErrorCodeRTCSessionCreateFailed = 6001,
/// RTC 对等连接失败
SellyCloudSDKErrorCodeRTCP2PConnectionFailed = 6002,
/// 加入房间失败
SellyCloudSDKErrorCodeJoinRoomFailed = 6003,
/// 离开房间失败
SellyCloudSDKErrorCodeLeaveRoomFailed = 6004,
// MARK: - 媒体设备错误 (7000-7999)
/// 相机权限被拒绝
SellyCloudSDKErrorCodeCameraPermissionDenied = 7000,
/// 麦克风权限被拒绝
SellyCloudSDKErrorCodeMicrophonePermissionDenied = 7001,
/// 相机不可用
SellyCloudSDKErrorCodeCameraUnavailable = 7002,
/// 麦克风不可用
SellyCloudSDKErrorCodeMicrophoneUnavailable = 7003,
/// 设备占用
SellyCloudSDKErrorCodeDeviceBusy = 7004,
// MARK: - 编解码错误 (8000-8999)
/// 编码器初始化失败
SellyCloudSDKErrorCodeEncoderInitFailed = 8000,
/// 解码器初始化失败
SellyCloudSDKErrorCodeDecoderInitFailed = 8001,
/// 编码失败
SellyCloudSDKErrorCodeEncodeFailed = 8002,
/// 解码失败
SellyCloudSDKErrorCodeDecodeFailed = 8003,
};
#pragma mark - Error UserInfo Keys
/// 错误的详细描述 key
FOUNDATION_EXPORT NSErrorUserInfoKey const SellyCloudSDKErrorDescriptionKey;
/// 错误的恢复建议 key
FOUNDATION_EXPORT NSErrorUserInfoKey const SellyCloudSDKErrorRecoverySuggestionKey;
/// 底层错误如系统错误、第三方库错误key
FOUNDATION_EXPORT NSErrorUserInfoKey const SellyCloudSDKErrorUnderlyingErrorKey;
/// 服务端原始错误码 key用于调试和日志记录
FOUNDATION_EXPORT NSErrorUserInfoKey const SellyCloudSDKErrorServerCodeKey;
#pragma mark - Error Creation Helper
/// 创建 SellyCloudSDK 错误的便捷方法
FOUNDATION_EXPORT NSError * SellyCloudSDKError(NSInteger code, NSString *description);
/// 创建带底层错误的 SellyCloudSDK 错误
FOUNDATION_EXPORT NSError * SellyCloudSDKErrorWithUnderlying(NSInteger code, NSString *description, NSError * _Nullable underlyingError);
/// 创建服务端错误(包含服务端原始错误码)
FOUNDATION_EXPORT NSError * SellyCloudSDKServerError(NSInteger serverCode, NSString *description);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,73 @@
//
// SellyLiveAudioConfiguration.h
// SellyCloudLiveKit
//
// Created by LaiFeng on 16/5/20.
// Copyright © 2016年 LaiFeng All rights reserved.
//
#import <Foundation/Foundation.h>
/// 音频码率 (默认96Kbps)
typedef NS_ENUM (NSUInteger, SellyLiveAudioBitRate) {
/// 32Kbps 音频码率
SellyLiveAudioBitRate_32Kbps = 32000,
/// 64Kbps 音频码率
SellyLiveAudioBitRate_64Kbps = 64000,
/// 96Kbps 音频码率
SellyLiveAudioBitRate_96Kbps = 96000,
/// 128Kbps 音频码率
SellyLiveAudioBitRate_128Kbps = 128000,
/// 默认音频码率,默认为 96Kbps
SellyLiveAudioBitRate_Default = SellyLiveAudioBitRate_96Kbps
};
/// 音频采样率 (默认44.1KHz)
typedef NS_ENUM (NSUInteger, SellyLiveAudioSampleRate){
/// 16KHz 采样率
SellyLiveAudioSampleRate_16000Hz = 16000,
/// 44.1KHz 采样率
SellyLiveAudioSampleRate_44100Hz = 44100,
/// 48KHz 采样率
SellyLiveAudioSampleRate_48000Hz = 48000,
/// 默认音频采样率,默认为 44.1KHz
SellyLiveAudioSampleRate_Default = SellyLiveAudioSampleRate_44100Hz
};
/// Audio Live quality音频质量
typedef NS_ENUM (NSUInteger, SellyLiveAudioQuality){
/// 低音频质量 audio sample rate: 16KHz audio bitrate: numberOfChannels 1 : 32Kbps 2 : 64Kbps
SellyLiveAudioQuality_Low = 0,
/// 中音频质量 audio sample rate: 44.1KHz audio bitrate: 96Kbps
SellyLiveAudioQuality_Medium = 1,
/// 高音频质量 audio sample rate: 44.1MHz audio bitrate: 128Kbps
SellyLiveAudioQuality_High = 2,
/// 超高音频质量 audio sample rate: 48KHz, audio bitrate: 128Kbps
SellyLiveAudioQuality_VeryHigh = 3,
/// 默认音频质量 audio sample rate: 44.1KHz, audio bitrate: 96Kbps
SellyLiveAudioQuality_Default = SellyLiveAudioQuality_High
};
@interface SellyLiveAudioConfiguration : NSObject<NSCoding, NSCopying>
/// 默认音频配置
+ (instancetype)defaultConfiguration;
/// 音频配置
+ (instancetype)defaultConfigurationForQuality:(SellyLiveAudioQuality)audioQuality;
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/// 声道数目(default 2)
@property (nonatomic, assign) NSUInteger numberOfChannels;
/// 采样率
@property (nonatomic, assign) SellyLiveAudioSampleRate audioSampleRate;
/// 码率
@property (nonatomic, assign) SellyLiveAudioBitRate audioBitrate;
/// flv编码音频头 44100 为0x12 0x10
@property (nonatomic, assign, readonly) char *asc;
/// 缓存区长度
@property (nonatomic, assign,readonly) NSUInteger bufferLength;
@end

View File

@@ -0,0 +1,69 @@
//
// SellyPlayerDelegate.h
// SellyCloudSDK
//
// Created by Caleb on 14/7/25.
//
#import <Foundation/Foundation.h>
#import "SellyPublicDefinition.h"
#import "SellyLiveVideoPlayer.h"
#import "SellyRTCVideoFrame.h"
#import "SellyLivePlayerStats.h"
NS_ASSUME_NONNULL_BEGIN
@protocol SellyLivePlayerDelegate <NSObject>
@optional
/**
SellyCloudMPMovieFinishReason
*/
- (void)player:(SellyLiveVideoPlayer *)player playbackDidFinished:(NSDictionary *)resultInfo;
- (void)player:(SellyLiveVideoPlayer *)player playbackStateChanged:(SellyPlayerState)state;
/**
首帧视频完成加载
@param elapse 调用play到加载完成耗时ms
*/
- (void)player:(SellyLiveVideoPlayer *)player firstRemoteVideoFrame:(NSInteger)elapse;
/**
首帧音频完成加载
@param elapse 调用play到加载完成耗时ms
*/
- (void)player:(SellyLiveVideoPlayer *)player firstRemoteAudioFrame:(NSInteger)elapse;
/**
追帧开始
@param rate 追帧速率
*/
- (void)player:(SellyLiveVideoPlayer *)player onFrameCatchingStart:(CGFloat)rate;
/**
追帧结束
*/
- (void)playerDidEndFrameCatching:(SellyLiveVideoPlayer *)player;
- (void)player:(SellyLiveVideoPlayer *)player onError:(NSError *)error;
/**
视频帧渲染回调
@param player 播放器实例
@param videoFrame 视频帧数据
@return 是否继续处理该帧
* - YES: 不忽略该帧
* - NO: 忽略该帧,不会继续渲染
*/
- (BOOL)player:(SellyLiveVideoPlayer *)player onRenderVideoFrame:(SellyRTCVideoFrame *)videoFrame;
/**
播放器调试信息回调
@param player 播放器实例
@param stats 播放器统计信息,包含各种性能指标和调试数据
*/
- (void)player:(SellyLiveVideoPlayer *)player onDebugInfo:(SellyLivePlayerStats *)stats;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,51 @@
//
// SellyLivePlayerStats.h
// SellyCloudSDK
//
// Created by Caleb on 26/1/13.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyLivePlayerStats : NSObject <NSCopying>
/// 播放协议RTMP/HLS/WebRTC等
@property (nonatomic, strong) NSString *protocol;
/// App / System CPU%
@property (nonatomic, assign) NSUInteger appCpu;
@property (nonatomic, assign) NSUInteger systemCpu;
/// 视频帧率fps
@property (nonatomic, assign) NSInteger fps;
/// 视频 / 音频码率kbps
@property (nonatomic, assign) NSInteger videoBitrate;
@property (nonatomic, assign) NSInteger audioBitrate;
/// 总下行速率kbps等于 videoBitrate + audioBitrate
@property (nonatomic, assign) NSInteger netSpeed;
/// RTT - 往返时延ms
@property (nonatomic, assign) NSInteger rtt;
/// 视频分辨率
@property (nonatomic, assign) CGSize videoSize;
/// 快照时间ms since boot
@property (nonatomic, assign) uint64_t timestampMs;
/// 是否正在loading
@property (nonatomic, assign) BOOL isLoading;
/// 丢包率(%
@property (nonatomic, assign) CGFloat packetLossRate;
/// 卡顿时间ms- 统计周期内的累积卡顿时长
@property (nonatomic, assign) NSUInteger freezeTime;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,25 @@
//
// SellyCloudRTMPDelegate.h
// SellyCloudSDK
//
// Created by Caleb on 7/7/25.
//
#import <Foundation/Foundation.h>
#import "LFLiveDebug.h"
#import "SellyPublicDefinition.h"
#import "SellyLivePusherStats.h"
NS_ASSUME_NONNULL_BEGIN
@class SellyLiveVideoPusher;
@protocol SellyLivePusherDelegate <NSObject>
@optional
- (void)pusher:(SellyLiveVideoPusher *)pusher liveStatusDidChanged:(SellyLiveState)status;
- (void)pusher:(SellyLiveVideoPusher *)pusher onError:(NSError *)error;
//视频前处理
- (CVPixelBufferRef)pusher:(SellyLiveVideoPusher *)pusher onCaptureVideoFrame:(CVPixelBufferRef)pixelBuffer;
//直播中stats回调
- (void)pusher:(SellyLiveVideoPusher *)pusher onStatisticsUpdate:(SellyLivePusherStats *)stats;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,47 @@
//
// SellyLivePusherStats.h
// AFNetworking
//
// Created by Caleb on 23/9/25.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyLivePusherStats : NSObject <NSCopying>
//直播协议
@property (nonatomic, strong)NSString *protocol;
/// App / System CPU%
@property (nonatomic, assign) NSUInteger appCpu;
@property (nonatomic, assign) NSUInteger systemCpu;
/// 帧率fps
@property (nonatomic, assign) NSUInteger fps;
/// 视频 / 音频码率kbps
@property (nonatomic, assign) NSUInteger videoBitrate;
@property (nonatomic, assign) NSUInteger audioBitrate;
/// 总上行速率kbps等于videoBitrate+audioBitrate
@property (nonatomic, assign) NSUInteger netSpeed;
/// RTTms
@property (nonatomic, assign) NSUInteger rtt;
/// 丢包率(%
@property (nonatomic, assign) CGFloat packetLossRate;
//卡顿时长
@property (nonatomic, assign) NSInteger freezeTime;
/// 快照时间ms since boot
@property (nonatomic, assign) uint64_t timestampMs;
//videoSize
@property (nonatomic, assign)CGSize size;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,42 @@
//
// SellyLiveVideoConfiguration.h
// SellyCloudLiveKit
//
// Created by LaiFeng on 16/5/20.
// Copyright © 2016年 LaiFeng All rights reserved.
//
#import <Foundation/Foundation.h>
#import "SellyPublicDefinition.h"
@interface SellyLiveVideoConfiguration : NSObject <NSCopying>
/// 默认视频配置
+ (instancetype)defaultConfiguration;
#pragma mark - Attribute
///=============================================================================
/// @name Attribute
///=============================================================================
/// 视频输出分辨率,宽高务必设定为 2 的倍数,否则解码播放时可能出现绿边
@property (nonatomic, assign) SellyRTCVideoResolution videoSize;
/// 视频输出方向
@property (nonatomic, assign) UIInterfaceOrientation outputImageOrientation;
/// 视频的帧率,即 fps
@property (nonatomic, assign) NSUInteger videoFrameRate;
/// 视频的最小帧率,即 fps
@property (nonatomic, assign) NSUInteger videoMinFrameRate;
/// 最大关键帧间隔,可设定为 fps 的2倍影响一个 gop 的大小
@property (nonatomic, assign) NSUInteger videoMaxKeyframeInterval;
/// 视频的码率,单位是 bps
@property (nonatomic, assign) NSUInteger videoBitRate;
/// 视频的最小码率,单位是 bps
@property (nonatomic, assign) NSUInteger videoMinBitRate;
@end

View File

@@ -0,0 +1,44 @@
//
// SellyVideoPlayer.h
// SellyCloudSDK
// 直播rtmp、rtc播放器
// Created by Caleb on 17/9/25.
//
#import <Foundation/Foundation.h>
#import "SellyPlayerStreamInfo.h"
@protocol SellyLivePlayerDelegate;
NS_ASSUME_NONNULL_BEGIN
@interface SellyLiveVideoPlayer : NSObject
//接盾的情况下请调用该方法初始化
- (void)startPlayStreamInfo:(SellyPlayerStreamInfo *)streamInfo;
- (void)startPlayUrl:(NSString *)url;
- (void)resume;
- (void)pause;
- (void)stop;
- (BOOL)isPlaying;
/** 截取当前图片 */
- (UIImage *)getCurrentImage;
/** 设置渲染视图(类似腾讯 V2TXLivePlayer 的 setRenderView:*/
- (void)setRenderView:(nullable UIView *)view;
//业务层需要传入用于显示的容器视图
@property (nonatomic, weak, readonly, nullable)UIView *renderView;
//代理
@property (nonatomic, weak) id<SellyLivePlayerDelegate> delegate;
//音量 静音播放设置0即可
@property (nonatomic, assign) CGFloat playbackVolume;
//播放状态
@property(nonatomic, readonly) SellyPlayerState playbackState;
//缩放
@property (nonatomic, assign)SellyPlayerScalingMode scaleMode;
//token
@property (nonatomic, strong)NSString *token;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,122 @@
//
// SellyVideoPusher.h
// SellyCloudSDK
//
// Created by Caleb on 18/9/25.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "SellyLivePusherDelegate.h"
#import "SellyPlayerStreamInfo.h"
NS_ASSUME_NONNULL_BEGIN
@interface SellyLiveVideoPusher : NSObject
/**
* 初始化直播推流器
*
* @param liveMode 推流协议类型RTMP/RTC 协议。
*/
- (instancetype)initWithLiveMode:(SellyLiveMode)liveMode NS_DESIGNATED_INITIALIZER;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
/**
是否开启本地预览镜像
*/
@property (nonatomic, assign)BOOL mirror;
/**
开始采集音视频流程,默认会采集语音和视频
@param captureDevicePosition 使用前置还是后置摄像头
@param videoConfig 视频采集/推流参数传nil将使用默认推流参数
@param audioConfig 音频采集/推流参数传nil将使用默认推流参数
*/
- (void)startRunning:(AVCaptureDevicePosition)captureDevicePosition videoConfig:(nullable SellyLiveVideoConfiguration *)videoConfig audioConfig:(nullable SellyLiveAudioConfiguration *)audioConfig;
/**
纯语音直播调用该方法,开始采集语音流程,不会采集视频
无法在直播中开启视频需要开关摄像头的请调用startRunning:videoConfig:audioConfig:方法
@param audioConfig 音频采集/推流参数传nil将使用默认推流参数
*/
- (void)startRunningAudio:(nullable SellyLiveAudioConfiguration *)audioConfig;
/**
开启视频采集,中途关闭摄像头再次开启需要调用
*/
- (nullable NSError *)startCamera;
/**
停止视频采集,中途关闭摄像头调用
*/
- (void)stopCamera;
/**
切换前后摄像头
*/
- (void)switchCameraPosition:(void (^)(NSError * _Nullable error))completion;
/**
开启音频采集
*/
- (void)startMicrophone;
/**
停止语音采集
*/
- (void)stopMicrophone;
/**
开始推流
@return 返回nil表示流程成功不代表推流成功
@param url 推流地址
*/
- (nullable NSError *)startLiveWithUrl:(NSString *)url;
- (nullable NSError *)startLiveWithStreamId:(NSString *)streamId;
/**
停止推流
*/
- (void)stopLive:(void(^)(NSError *error))callback;
/**
截取当前图片
*/
- (UIImage *)getCurrentImage;
/**
推送静态图片调用1次即可不用循环调用
*/
- (void)pushStaticImage:(UIImage *)image;
/**
停止推送静态图片
*/
- (void)stopPushImage;
/**
本地预览view
*/
@property (nonatomic, strong)UIView *preview;
/**
回调
*/
@property (nonatomic, weak)id<SellyLivePusherDelegate> delegate;
@property (nonatomic, assign, readonly)BOOL isMute;
@property (nonatomic, assign, readonly)BOOL isCameraEnable;
@property (nonatomic, assign, readonly)SellyLiveState liveState;
/**
是否支持视频前置处理,例如实现美颜功能. default no
*/
@property (nonatomic, assign)BOOL enableCustomVideoProcess;
//当前的摄像头方向
@property (nonatomic, assign)AVCaptureDevicePosition captureDevicePosition;
//缩放
@property (nonatomic, assign)SellyPlayerScalingMode scaleMode;
//videoConfig
@property (nonatomic, assign)SellyLiveVideoConfiguration *videoConfig;
//token
@property (nonatomic, strong)NSString *token;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,18 @@
//
// SellyPlayerStreamInfo.h
// SellyCloudSDK
//
// Created by Caleb on 18/9/25.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyPlayerStreamInfo : NSObject
@property (nonatomic, strong)NSString *streamId;
//rtmp,rtc
@property (nonatomic, assign)SellyLiveMode protocol;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,98 @@
//
// SellyPublicDefinition.h
// Pods
//
// Created by Caleb on 22/8/25.
//
#ifndef SellyPublicDefinition_h
#define SellyPublicDefinition_h
//视频输出分辨率
typedef CGSize SellyRTCVideoResolution;
extern SellyRTCVideoResolution const SellyRTCVideoResolution480x360;
extern SellyRTCVideoResolution const SellyRTCVideoResolution480x480;
extern SellyRTCVideoResolution const SellyRTCVideoResolution640x360;
extern SellyRTCVideoResolution const SellyRTCVideoResolution640x480;
extern SellyRTCVideoResolution const SellyRTCVideoResolution960x540;
extern SellyRTCVideoResolution const SellyRTCVideoResolution1280x720;
typedef NS_ENUM(NSInteger, SellyVideoCodecType) {
SellyVideoCodecTypeH264 = 1,
SellyVideoCodecTypeH265 = 2,
};
/**
* 支持协议
*/
typedef NS_ENUM(NSUInteger, SellyLiveMode) {
/// 支持协议: RTMP。
SellyLiveMode_RTMP,
/// 支持协议: TRTC。
SellyLiveMode_RTC
};
typedef NS_ENUM(NSInteger, SellyPlayerScalingMode) {
SellyPlayerScalingModeNone, // No scaling
SellyPlayerScalingModeAspectFit, // Uniform scale until one dimension fits
SellyPlayerScalingModeAspectFill, // Uniform scale until the movie fills the visible bounds. One dimension may have clipped contents
};
typedef NS_ENUM(NSInteger, SellyPlayerState) {
SellyPlayerStateIdle = 0,
SellyPlayerStateConnecting = 1,
SellyPlayerStatePlaying = 2,
SellyPlayerStatePaused = 3,
SellyPlayerStateStoppedOrEnded = 4,
SellyPlayerStateFailed = 5
};
typedef NS_ENUM(NSInteger, SellyCloudMPMovieFinishReason) {
SellyCloudMPMovieFinishReasonPlaybackEnded,
SellyCloudMPMovieFinishReasonPlaybackError,
SellyCloudMPMovieFinishReasonUserExited
};
typedef NS_ENUM(NSInteger, SellyLiveState) {
SellyLiveStateIdle = 0, // 初始/未开始
SellyLiveStateConnecting, // 建立会话RTMP连接 或 SDP/ICE协商中
SellyLiveStatePublishing, // 已在上行推流(码流稳定输出)
SellyLiveStateReconnecting, // 断线重试中(短暂)
SellyLiveStateStopped, // 正常结束stop
SellyLiveStateFailed // 异常结束(错误/不可恢复)
};
typedef NS_ENUM (NSUInteger, SellyCloudLiveSocketErrorCode) {
SellyCloudLiveSocketError_PreView = 201, ///< 预览失败
SellyCloudLiveSocketError_GetStreamInfo = 202, ///< 获取流媒体信息失败
SellyCloudLiveSocketError_ConnectSocket = 203, ///< 连接socket失败
SellyCloudLiveSocketError_Verification = 204, ///< 验证服务器失败
SellyCloudLiveSocketError_ReConnectTimeOut = 205, ///< 重新连接服务器超时
SellyCloudLiveSocketError_HttpRequest = 400, //http请求报错
};
typedef NS_ENUM(NSInteger, SellyVideoCallType) {
SellyVideoCallTypeAudio, // 语音
SellyVideoCallTypeVideo, // 视频
};
//信令连接状态
typedef NS_ENUM(NSInteger, SellyRoomConnectionState) {
SellyRoomStateDisconnected,
SellyRoomStateConnecting,
SellyRoomStateConnected,
SellyRoomStateReconnecting,
SellyRoomStateFailed,
};
//屏幕分享状态
typedef NS_ENUM(uint8_t, SellyScreenShareState) {
SellyScreenShareStateStarted = 1, // 开始
SellyScreenShareStatePaused = 2, // 暂停
SellyScreenShareStateResumed = 3, // 恢复
SellyScreenShareStateStopped = 4, // 结束
};
#endif /* SellyPublicDefinition_h */

View File

@@ -0,0 +1,17 @@
//
// SellyRTCReplayKitHandler.h
// SellyCloudSDK
//
// Created by Caleb on 12/10/25.
//
#import <Foundation/Foundation.h>
#import <ReplayKit/ReplayKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyRTCReplayKitHandler : RPBroadcastSampleHandler
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,99 @@
//
// SellyRTCSession.h
// SellyCloudSDK
//
// Created by Caleb on 17/11/25.
//
#import <Foundation/Foundation.h>
#import "SellyRTCVideoConfiguration.h"
#import "SellyRTCSessionDelegate.h"
#import <AVFoundation/AVFoundation.h>
#import "SellyRTCVideoCanvas.h"
NS_ASSUME_NONNULL_BEGIN
@interface SellyRTCSession : NSObject
/**
@param isP2p 是否单聊
*/
- (instancetype)initWithType:(BOOL)isP2p;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/**
@brief 发起通话
*/
- (void)startWithChannelId:(NSString *)channelId token:(NSString *)token;
/// 结束通话
- (void)end;
//是否开启本地视频,默认不开启
- (void)enableLocalVideo:(BOOL)enable;
//是否开启本地音频采集,默认开启
- (void)enableLocalAudio:(BOOL)enable;
//切换前后摄像头
- (void)switchCamera;
/**
* Starts the local video preview before joining a channel.
*
* Once you call this method to start the local video preview, if you leave
* the channel by calling \ref leaveChannel: leaveChannel, the local video
* preview remains until you call \ref stopPreview to disable it.
*/
- (void)startPreview;
/**
* Stops the local video preview and the video.
*/
- (void)stopPreview;
/**
屏蔽对方的声音单聊不用传userId
*/
- (void)muteRemoteAudioStream:(NSString *)userId mute:(BOOL)mute;
/**
@brief 设置声音输出通道
如果没有外接设备(蓝牙,耳机)等,可以直接调用这个方法在听筒和扬声器直接来回切换
如果有外接设备建议直接使用AVRoutePickerView
有外接设备的情况下,无法切换为听筒播放,但是可以切换为扬声器播放
*/
- (void)setAudioOutput:(AVAudioSessionPortOverride)port;
/**
发送自定义消息给对方
*/
- (void)sendMessage:(NSString *)message completion:(nonnull void (^)(NSError * _Nullable))completion;
//本地localView
- (void)setLocalCanvas:(SellyRTCVideoCanvas *)localCanvas;
//远程remoteView
- (void)setRemoteCanvas:(SellyRTCVideoCanvas *)remoteCanvas;
//更新token
- (void)renewToken:(NSString * _Nonnull)token;
//Starts screen sharing.
- (void)startScreenCapture;
//外部推流不使用sdk默认的视频采集
- (void)pushExternalVideoFrame:(CVPixelBufferRef)pixelBuffer;
//
@property (nonatomic, weak)id<SellyRTCSessionDelegate> delegate;
//视频编码设置 需要在startWithChannelId之前调用
@property (nonatomic, strong)SellyRTCVideoConfiguration *videoConfig;
////通话连接状态
@property (nonatomic, assign, readonly)SellyRoomConnectionState connectionState;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,124 @@
//
// SellyRTCSessionDelegate.h
// SellyCloudSDK
//
// Created by Caleb on 5/11/25.
//
#import <Foundation/Foundation.h>
#import "SellyRTCStats.h"
#import "SellyRTCVideoFrame.h"
@class SellyRTCSession;
NS_ASSUME_NONNULL_BEGIN
//流的连接状态
typedef enum : NSUInteger {
SellyRTCConnectStateDisconnected, //断开连接
SellyRTCConnectStateConnecting, //连接中
SellyRTCConnectStateConnected, //连接成功
SellyRTCConnectStateReconnecting, //重连
} SellyRTCConnectState;
@protocol SellyRTCSessionDelegate <NSObject>
@optional
/**
通话报错无法恢复,需要结束流程
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onError:(NSError *)error;
/** A remote user's video was enabled or disabled.
@param enabled Enabled or disabled:
* Yes: User has enabled the video function.
* No: User has disabled the video function.
@param userId Remote user ID.
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session videoEnabled:(BOOL)enabled userId:(nullable NSString *)userId;
/** A remote user's audio was enabled or disabled.
@param enabled Enabled or disabled:
* Yes: User has enabled the audio function.
* No: User has disabled the audio function.
@param userId Remote user ID.
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session audioEnabled:(BOOL)enabled userId:(nullable NSString *)userId;
/**
收到对方自定义消息
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session didReceiveMessage:(NSString *)message userId:(nullable NSString *)userId;
/**
@brief stream连接状态发生改变
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session connectionStateChanged:(SellyRTCConnectState)state userId:(nullable NSString *)userId;
/**
@brief 会话连接状态发生改变
会话重连期间内ice会持续重启直到超时
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onRoomConnectionStateChanged:(SellyRoomConnectionState)state;
//视频前处理
- (CVPixelBufferRef)rtcSession:(SellyRTCSession * _Nonnull)session onCaptureVideoFrame:(CVPixelBufferRef)pixelBuffer;
/**
* Occurs each time the SDK receives a video frame sent by the remote user.
*
* After you successfully register the video frame observer, the SDK triggers this callback each time a
* video frame is received. In this callback, you can get the video data sent by the remote user. You
* can then post-process the data according to your scenarios.
*
@return Determines whether to ignore the current video frame if the pre-processing fails:
* - true: Do not ignore.
* - false: Ignore, in which case this method does not sent the current video frame to the SDK.
*/
- (BOOL)rtcSession:(SellyRTCSession *)session onRenderVideoFrame:(SellyRTCVideoFrame *)videoFrame userId:(NSString *)userId;
/**
统计功能
如果是单聊 userId = nil
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onStats:(SellyRTCStats *)stats userId:(nullable NSString *)userId;
/**
用户加入频道
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onUserJoined:(nullable NSString *)userId;
/**
用户离开频道
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onUserLeave:(nullable NSString *)userId;
/**
通话时长回调
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onDuration:(NSInteger)duration;
/**
收到token将要过期消息
@param token The token that will expire in 60 seconds.
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session tokenWillExpire:(NSString *)token;
/**
收到token已过期消息
token过期后依然可以正常通话但是断网重连会失败
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session tokenExpired:(NSString *)token;
/**
屏幕分享状态发生变化
*/
- (void)rtcSession:(SellyRTCSession * _Nonnull)session onScreenShareStatusChanged:(SellyScreenShareState)state;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,49 @@
//
// SellyRTCP2pStats.h
// SellyCloudSDK
//
// Created by Caleb on 10/11/25.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyRTCStats : NSObject
/// 基本
/// 传输ICE/DTLS
@property(nonatomic, assign) double transportRttMs; // candidate-pair.currentRoundTripTime * 1000
@property(nonatomic, copy, nullable) NSString *relayProtocol; // udp/tcp/tlsTURN时可见
@property(nonatomic, copy) NSString *audioCodec;
@property(nonatomic, copy) NSString *videoCodec;
/// 媒体累计字节
@property(nonatomic, assign) uint64_t txAudioBytes;
@property(nonatomic, assign) uint64_t txVideoBytes;
@property(nonatomic, assign) uint64_t rxAudioBytes;
@property(nonatomic, assign) uint64_t rxVideoBytes;
@property(nonatomic, assign, readonly) uint64_t txBytes; // 汇总
@property(nonatomic, assign, readonly) uint64_t rxBytes;
/// 视频信息(能拿到就填)
@property(nonatomic, assign) double sentFps;
@property(nonatomic, assign) NSInteger sentWidth;
@property(nonatomic, assign) NSInteger sentHeight;
@property(nonatomic, assign) double recvFps;
@property(nonatomic, assign) NSInteger recvWidth;
@property(nonatomic, assign) NSInteger recvHeight;
/// —— 新增:瞬时 Kbps由 Helper 用“前一帧”差分计算)——
@property(nonatomic, assign) double txAudioKbps; // 本次 - 上次 bytes / Δt
@property(nonatomic, assign) double txVideoKbps;
@property(nonatomic, assign) double rxAudioKbps;
@property(nonatomic, assign) double rxVideoKbps;
@property(nonatomic, assign) double txKbps; // = txAudioKbps + txVideoKbps
@property(nonatomic, assign) double rxKbps; // = rxAudioKbps + rxVideoKbps
/// 时间戳(内部用)
@property(nonatomic, assign) NSTimeInterval intervalMs; // 与上次快照的间隔(毫秒),首帧为 0
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,31 @@
//
// SellyRTCVideoConfiguration.h
// SellyCloudSDK
//
// Created by Caleb on 5/11/25.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyRTCVideoConfiguration : NSObject
//分辨率
@property(assign, nonatomic) SellyRTCVideoResolution resolution;
//帧率
@property(assign, nonatomic) NSInteger frameRate;
//最大码率 bps
@property(assign, nonatomic) NSInteger maxBitrate;
//最小码率 bps
@property(assign, nonatomic) NSInteger minBitrate;
/**
会议模式仅支持h264单聊支持h264h265
*/
@property (nonatomic, assign) SellyVideoCodecType preferCodec;
//默认使用前/后置摄像头
@property (nonatomic, assign)AVCaptureDevicePosition preferPosition;
+ (instancetype)defaultConfig;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,26 @@
//
// SCSVideoFrame.h
// SellyCloudSDK
//
// Created by Caleb on 19/11/25.
//
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
NS_ASSUME_NONNULL_BEGIN
@interface SellyRTCVideoFrame : NSObject
/// 宽度(像素)
@property (nonatomic, assign) int width;
/// 高度(像素)
@property (nonatomic, assign) int height;
/// 旋转角度0 / 90 / 180 / 270
@property (nonatomic, assign) NSInteger rotation;
/// 时间戳(可选)
@property (nonatomic, assign) int64_t timestamp;
/// 像素数据
@property (nonatomic, assign, nullable) CVPixelBufferRef pixelBuffer;
@end
NS_ASSUME_NONNULL_END

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