Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LoopBack.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ Pod::Spec.new do |s|
s.requires_arc = true

s.ios.deployment_target = '6.1'
s.ios.frameworks = 'UIKit', 'Foundation', 'MobileCoreServices', 'SystemConfiguration'
s.ios.frameworks = 'UIKit', 'Foundation', 'MobileCoreServices', 'SystemConfiguration', 'CoreLocation'

end
26 changes: 16 additions & 10 deletions LoopBack.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
47F8E813185B7A4E0088BB73 /* LBPushNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F8E811185B7A4E0088BB73 /* LBPushNotification.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F8E814185B7A4E0088BB73 /* LBPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F8E812185B7A4E0088BB73 /* LBPushNotification.m */; };
8915F0631BF72CD100540BD3 /* module.modulemap in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8915F0621BF72CB500540BD3 /* module.modulemap */; };
893C8CBF1C1ABFD20050045C /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 893C8CBE1C1ABFD20050045C /* CoreLocation.framework */; };
895B41801AB16D660000A9D7 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B278D50187B584F00FFC135 /* MobileCoreServices.framework */; };
895B41811AB16D670000A9D7 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B278D50187B584F00FFC135 /* MobileCoreServices.framework */; };
895B41821AB16D6A0000A9D7 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B278D4A187B584A00FFC135 /* SystemConfiguration.framework */; };
Expand Down Expand Up @@ -276,15 +277,15 @@
47C48B931962120E00995044 /* SLRESTContract.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLRESTContract.h; sourceTree = "<group>"; };
47C48B941962120E00995044 /* SLRESTContract.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLRESTContract.m; sourceTree = "<group>"; };
47C48BB61962123900995044 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
47C48BB91962123900995044 /* contract-class.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "contract-class.js"; sourceTree = "<group>"; };
47C48BBA1962123900995044 /* contract.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = contract.js; sourceTree = "<group>"; };
47C48BBB1962123900995044 /* index.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = index.js; sourceTree = "<group>"; };
47C48BBC1962123900995044 /* nonroot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = nonroot.js; sourceTree = "<group>"; };
47C48BB91962123900995044 /* contract-class.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = "contract-class.js"; sourceTree = "<group>"; tabWidth = 2; };
47C48BBA1962123900995044 /* contract.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = contract.js; sourceTree = "<group>"; tabWidth = 2; };
47C48BBB1962123900995044 /* index.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = index.js; sourceTree = "<group>"; tabWidth = 2; };
47C48BBC1962123900995044 /* nonroot.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = nonroot.js; sourceTree = "<group>"; tabWidth = 2; };
47C48BBF1962123900995044 /* certificate.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = certificate.pem; sourceTree = "<group>"; };
47C48BC01962123900995044 /* privatekey.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = privatekey.pem; sourceTree = "<group>"; };
47C48BC11962123900995044 /* simple-class.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "simple-class.js"; sourceTree = "<group>"; };
47C48BC21962123900995044 /* simple.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = simple.js; sourceTree = "<group>"; };
47C48BC31962123900995044 /* ssl-config.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "ssl-config.js"; sourceTree = "<group>"; };
47C48BC11962123900995044 /* simple-class.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = "simple-class.js"; sourceTree = "<group>"; tabWidth = 2; };
47C48BC21962123900995044 /* simple.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = simple.js; sourceTree = "<group>"; tabWidth = 2; };
47C48BC31962123900995044 /* ssl-config.js */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = "ssl-config.js"; sourceTree = "<group>"; tabWidth = 2; };
47C48BC41962123900995044 /* SLRemotingTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SLRemotingTests-Info.plist"; sourceTree = "<group>"; };
47C48BC61962123900995044 /* SLRESTAdapterNonRootTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLRESTAdapterNonRootTests.m; sourceTree = "<group>"; };
47C48BC71962123900995044 /* SLRESTAdapterSSLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLRESTAdapterSSLTests.m; sourceTree = "<group>"; };
Expand All @@ -294,11 +295,12 @@
47F8E811185B7A4E0088BB73 /* LBPushNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBPushNotification.h; sourceTree = "<group>"; };
47F8E812185B7A4E0088BB73 /* LBPushNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBPushNotification.m; sourceTree = "<group>"; };
8915F0621BF72CB500540BD3 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
893C8CBE1C1ABFD20050045C /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
8973753C1AB2A60A00FB31A2 /* SLStreamParam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLStreamParam.h; sourceTree = "<group>"; };
8973753D1AB2A60A00FB31A2 /* SLStreamParam.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLStreamParam.m; sourceTree = "<group>"; };
89C410321B4D4B0200B2ED51 /* LBPersistedModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LBPersistedModel.h; sourceTree = "<group>"; };
89C410331B4D4B0200B2ED51 /* LBPersistedModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBPersistedModel.m; sourceTree = "<group>"; };
89D3C9521AB17258005A1B90 /* index.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = index.js; sourceTree = "<group>"; };
89D3C9521AB17258005A1B90 /* index.js */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.javascript; path = index.js; sourceTree = "<group>"; tabWidth = 2; };
89D3DB031AB17263005A1B90 /* f1.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = f1.txt; sourceTree = "<group>"; };
89D3DB071AB17263005A1B90 /* f1.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = f1.txt; sourceTree = "<group>"; };
89F4F36B1B53E6A80070D8EB /* LBModelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LBModelTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -341,10 +343,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0B278D51187B584F00FFC135 /* MobileCoreServices.framework in Frameworks */,
0B278D4B187B584A00FFC135 /* SystemConfiguration.framework in Frameworks */,
B3D220FB17722AE800B7CB63 /* Foundation.framework in Frameworks */,
89EB399F1AB16ED900B1EB9D /* UIKit.framework in Frameworks */,
0B278D51187B584F00FFC135 /* MobileCoreServices.framework in Frameworks */,
0B278D4B187B584A00FFC135 /* SystemConfiguration.framework in Frameworks */,
893C8CBF1C1ABFD20050045C /* CoreLocation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -547,6 +550,7 @@
B3D2214317725DAB00B7CB63 /* UIKit.framework */,
0B278D50187B584F00FFC135 /* MobileCoreServices.framework */,
0B278D4A187B584A00FFC135 /* SystemConfiguration.framework */,
893C8CBE1C1ABFD20050045C /* CoreLocation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
Expand Down Expand Up @@ -1159,6 +1163,7 @@
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
Expand Down Expand Up @@ -1189,6 +1194,7 @@
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
Expand Down
62 changes: 62 additions & 0 deletions LoopBack/LBModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,68 @@

#import "SLRemoting.h"

/**
* Blocks of this type are executed for a successful method invocation that doesn't return
* any value as a callback argument.
*/
typedef void (^LBModelVoidSuccessBlock)();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For convenience (especially for CodeGen), the success block types with supported data types are typedef'ed.


/**
* Blocks of this type are executed for a successful method invocation that returns
* a BOOL value as a callback argument.
*/
typedef void (^LBModelBoolSuccessBlock)(BOOL boolean);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an NSInteger value as a callback argument.
*/
typedef void (^LBModelNumberSuccessBlock)(NSInteger number);

/**
* Blocks of this type are executed for a successful method invocation that returns
* a NSString value as a callback argument.
*/
typedef void (^LBModelStringSuccessBlock)(NSString *string);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an LBModel instance as a callback argument.
*/
@class LBModel;
typedef void (^LBModelObjectSuccessBlock)(LBModel *model);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an NSDictionary instance as a callback argument.
*/
typedef void (^LBModelDictionarySuccessBlock)(NSDictionary *dictionary);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an NSArray instance as a callback argument.
*/
typedef void (^LBModelArraySuccessBlock)(NSArray *array);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an NSDate instance as a callback argument.
*/
typedef void (^LBModelDateSuccessBlock)(NSDate *date);

/**
* Blocks of this type are executed for a successful method invocation that returns
* an NSData instance as a callback argument.
*/
typedef void (^LBModelDataSuccessBlock)(NSData *data);

/**
* Blocks of this type are executed for a successful method invocation that returns
* a CLLocation instance as a callback argument.
*/
typedef void (^LBModelLocationSuccessBlock)(CLLocation *location);


/**
* A local representative of a single model instance on the server. The data is
* immediately accessible locally.
Expand Down
34 changes: 17 additions & 17 deletions LoopBack/LBModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
* @copyright (c) 2013 StrongLoop. All rights reserved.
*/

#import "LBModel.h"

#import <objc/runtime.h>

#define NSSelectorForSetter(key) NSSelectorFromString([NSString stringWithFormat:@"set%@:", [key stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[key substringToIndex:1] capitalizedString]]])
#import "LBModel.h"


@interface LBModel() {
Expand All @@ -20,7 +18,6 @@ - (NSMutableDictionary *)_overflow;

@end

static NSDateFormatter *jsonDateFormatter = nil;

@implementation LBModel

Expand All @@ -31,12 +28,6 @@ - (instancetype)initWithRepository:(SLRepository *)repository parameters:(NSDict
__overflow = [NSMutableDictionary dictionary];
}

if (jsonDateFormatter == nil) {
jsonDateFormatter = [[NSDateFormatter alloc] init];
[jsonDateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"];
[jsonDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}

return self;
}

Expand All @@ -63,9 +54,6 @@ - (NSDictionary *)toDictionary {
NSString *propertyName = [NSString stringWithCString:property_getName(properties[i])
encoding:NSUTF8StringEncoding];
id obj = [self valueForKey:propertyName];
if ([obj isKindOfClass:[NSDate class]]) {
obj = [jsonDateFormatter stringFromDate:obj];
}
[dict setValue:obj forKey:propertyName];
}
free(properties);
Expand Down Expand Up @@ -105,7 +93,7 @@ - (SLRESTContract *)contract {
}

- (LBModel *)modelWithDictionary:(NSDictionary *)dictionary {
LBModel __block *model = (LBModel *)[[self.modelClass alloc] initWithRepository:self parameters:dictionary];
LBModel *model = (LBModel *)[[self.modelClass alloc] initWithRepository:self parameters:dictionary];

[[model _overflow] addEntriesFromDictionary:dictionary];

Expand All @@ -122,9 +110,21 @@ - (LBModel *)modelWithDictionary:(NSDictionary *)dictionary {
}

const char *type = property_getAttributes(property);
// if the property type is NSDate, convert the string to a date object
if ([obj isKindOfClass:[NSString class]] && strncmp(type, "T@\"NSDate\",", 11) == 0) {
obj = [jsonDateFormatter dateFromString:obj];
if ([obj isKindOfClass:[NSString class]]) {
// if the property type is NSDate, convert the string to a date object
if (strncmp(type, "T@\"NSDate\",", 11) == 0) {
obj = [SLObject dateFromEncodedProperty:obj];
}
} else if ([obj isKindOfClass:[NSDictionary class]]) {
// if the property type is NSMutableData, convert the json object to a data object
if (strncmp(type, "T@\"NSMutableData\",", 18) == 0 ||
strncmp(type, "T@\"NSData\",", 11) == 0) {
obj = [SLObject dataFromEncodedProperty:obj];
}
// if the property type is CLLocation, convert the json object to a location object
else if (strncmp(type, "T@\"CLLocation\",", 15) == 0) {
obj = [SLObject locationFromEncodedProperty:obj];
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter conversions back to iOS data types are needed to be performed here in LBModel (instead of SLObject) since it requires access to model property type attributes.

}

@try {
Expand Down
25 changes: 18 additions & 7 deletions LoopBack/LBPersistedModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@

#import "LBModel.h"


/**
* Blocks of this type are executed for a successful method invocation that returns
* an LBPersistedModel instance as a callback argument.
*/
@class LBPersistedModel;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since those success block types rather belong to LBModel, they have been moved there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, isn't this a breaking change? I mean if there is any existing user code relying on LBPersistedModelVoidSuccessBlock (for example), then it will break after upgrading to a new version of iOS SDK.

Can we keep these typedefs around, perhaps with a deprecation notice (if there is a mechanism for that).

typedef LBModelVoidSuccessBlock LBPersistedModelVoidSuccessBlock;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't care about backward compatibility for those since those typedefs are added by me on Oct. 29 as a part of CodeGen support. It seems highly unlikely that someone already depends on the typedefs. How about make the above change and consider backward compatibility when we get any Issue on it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following @bajtos 's suggestion, I've left the typedefs with deprecation warning since I'm informed that Ver. v1.3.2 has been recently which includes all the typedefs (which I didn't realise until yesterday).

typedef void (^LBPersistedModelObjectSuccessBlock)(LBPersistedModel *model);
typedef void (^LBPersistedModelArraySuccessBlock)(NSArray *array);
typedef void (^LBPersistedModelVoidSuccessBlock)();
typedef void (^LBPersistedModelBoolSuccessBlock)(BOOL boolean);
typedef void (^LBPersistedModelNumberSuccessBlock)(NSInteger number);

// The following typedefs are left for backward compatibility.
typedef LBModelVoidSuccessBlock LBPersistedModelVoidSuccessBlock
__attribute((deprecated("use LBModelVoidSuccessBlock")));
typedef LBModelBoolSuccessBlock LBPersistedModelBoolSuccessBlock
__attribute((deprecated("use LBModelBoolSuccessBlock")));
typedef LBModelNumberSuccessBlock LBPersistedModelNumberSuccessBlock
__attribute((deprecated("use LBModelNumberSuccessBlock")));
typedef LBModelArraySuccessBlock LBPersistedModelArraySuccessBlock
__attribute((deprecated("use LBModelArraySuccessBlock")));

/**
* A local representative of a single persisted model instance on the server.
Expand All @@ -27,7 +38,7 @@ typedef void (^LBPersistedModelNumberSuccessBlock)(NSInteger number);
* Blocks of this type are executed when LBPersistedModel::saveWithSuccess:failure: is
* successful.
*/
typedef LBPersistedModelVoidSuccessBlock LBPersistedModelSaveSuccessBlock;
typedef LBModelVoidSuccessBlock LBPersistedModelSaveSuccessBlock;
/**
* Saves the LBPersistedModel to the server.
*
Expand All @@ -43,7 +54,7 @@ typedef LBPersistedModelVoidSuccessBlock LBPersistedModelSaveSuccessBlock;
* Blocks of this type are executed when LBPersistedModel::destroyWithSuccess:failure: is
* successful.
*/
typedef LBPersistedModelVoidSuccessBlock LBPersistedModelDestroySuccessBlock;
typedef LBModelVoidSuccessBlock LBPersistedModelDestroySuccessBlock;
/**
* Destroys the LBPersistedModel from the server.
*
Expand Down Expand Up @@ -88,7 +99,7 @@ typedef LBPersistedModelObjectSuccessBlock LBPersistedModelFindSuccessBlock;
* Blocks of this type are executed when
* LBPersistedModelRepository::allWithSuccess:failure: is successful.
*/
typedef LBPersistedModelArraySuccessBlock LBPersistedModelAllSuccessBlock;
typedef LBModelArraySuccessBlock LBPersistedModelAllSuccessBlock;
/**
* Finds and downloads all models of this type on and from the server.
*
Expand Down
4 changes: 0 additions & 4 deletions LoopBack/LBPersistedModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@

#import "LBPersistedModel.h"

#import <objc/runtime.h>

#define NSSelectorForSetter(key) NSSelectorFromString([NSString stringWithFormat:@"set%@:", [key stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[key substringToIndex:1] capitalizedString]]])


@interface LBPersistedModel() {
id __id;
Expand Down
5 changes: 2 additions & 3 deletions LoopBack/LBRESTAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ - (LBModelRepository *)repositoryWithModelName:(NSString *)name persisted:(BOOL)
- (LBModelRepository *)repositoryWithClass:(Class)type {
if (type == nil) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:[NSString stringWithFormat:@"Argument cannot be nil"]
reason:@"Argument cannot be nil"
userInfo:nil];
}
if (![type isSubclassOfClass:[LBModelRepository class]]) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:[NSString stringWithFormat:
@"Argument needs to be a subclass of LBModelRepository"]
reason:@"Argument needs to be a subclass of LBModelRepository"
userInfo:nil];
}
if (![type respondsToSelector:@selector(repository)]) {
Expand Down
1 change: 1 addition & 0 deletions LoopBack/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ framework module LoopBack {

link framework "MobileCoreServices"
link framework "SystemConfiguration"
link framework "CoreLocation"
}
Loading