1 Commits

Author SHA1 Message Date
Patrick McDonagh
2e08234eef Adds search function for list view 2018-06-11 10:21:03 -05:00
38 changed files with 991 additions and 823 deletions

View File

@@ -3,15 +3,15 @@ PODS:
- ChameleonFramework (2.1.0):
- ChameleonFramework/Default (= 2.1.0)
- ChameleonFramework/Default (2.1.0)
- Firebase/Auth (5.1.0):
- Firebase/Auth (5.2.0):
- Firebase/CoreOnly
- FirebaseAuth (= 5.0.0)
- Firebase/Core (5.1.0):
- FirebaseAuth (= 5.0.1)
- Firebase/Core (5.2.0):
- Firebase/CoreOnly
- FirebaseAnalytics (= 5.0.1)
- Firebase/CoreOnly (5.1.0):
- FirebaseCore (= 5.0.2)
- Firebase/Database (5.1.0):
- Firebase/CoreOnly (5.2.0):
- FirebaseCore (= 5.0.3)
- Firebase/Database (5.2.0):
- Firebase/CoreOnly
- FirebaseDatabase (= 5.0.1)
- FirebaseAnalytics (5.0.1):
@@ -19,15 +19,15 @@ PODS:
- FirebaseInstanceID (~> 3.0)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- nanopb (~> 0.3)
- FirebaseAuth (5.0.0):
- FirebaseAuth (5.0.1):
- FirebaseCore (~> 5.0)
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (5.0.2):
- FirebaseCore (5.0.3):
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- FirebaseDatabase (5.0.1):
- FirebaseCore (~> 5.0)
- leveldb-library (~> 1.18)
- FirebaseInstanceID (3.0.0):
- FirebaseInstanceID (3.1.0):
- FirebaseCore (~> 5.0)
- GoogleToolboxForMac/Defines (2.1.4)
- "GoogleToolboxForMac/NSData+zlib (2.1.4)":
@@ -44,11 +44,11 @@ PODS:
- Alamofire (~> 4.0)
- PromiseKit/CorePromise
- PromiseKit/CorePromise (6.2.8)
- Realm (3.6.0):
- Realm/Headers (= 3.6.0)
- Realm/Headers (3.6.0)
- RealmSwift (3.6.0):
- Realm (= 3.6.0)
- Realm (3.7.0):
- Realm/Headers (= 3.7.0)
- Realm/Headers (3.7.0)
- RealmSwift (3.7.0):
- Realm (= 3.7.0)
- SideMenu (4.0.0)
- SVProgressHUD (2.2.5)
- SwiftChart (1.0.1)
@@ -94,20 +94,20 @@ SPEC REPOS:
SPEC CHECKSUMS:
Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
ChameleonFramework: d21a3cc247abfe5e37609a283a8238b03575cf64
Firebase: e08fb0795f35707aeb1d8a715c731c45bdf6fd56
Firebase: 25ed0412036d7d008568d1fb4d2e9d81ea8a0a2c
FirebaseAnalytics: b3628aea54c50464c32c393fb2ea032566e7ecc2
FirebaseAuth: acbeef02fe7c3a26624e309849f3fe30c84115af
FirebaseCore: b81044df1044c0857a0737c6324678b72d4f7f00
FirebaseAuth: 463b8ce33bd5d05f706dcd4615499e3212b4132b
FirebaseCore: a3c87242451633fff8490183898075ce77d168d2
FirebaseDatabase: 482bad9c2abd422bb2321194fb8c937e67426a89
FirebaseInstanceID: 83e0040351565df711a5db3d8ebe5ea21aca998a
FirebaseInstanceID: 05d779cbb97bd5bd5c51a38a903fc9cfe1b2454a
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
GTMSessionFetcher: 5fa5b80fd20e439ef5f545fb2cb3ca6c6714caa2
Kingfisher: 976d828df2b24834c6a3f2fc4d82cdbd26552be1
leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
PromiseKit: 6788ce1a0ed5448b83d4aaf56b9fc49fb7647d32
Realm: 08b464b462d4f31bbd4ba5f5a1c8722ef0a700b7
RealmSwift: 973e1499c44ab571f894c2c908e2db450be280c3
Realm: 4998c6ced1ea15b3792f273b18f8e6faaf935b5c
RealmSwift: 7dc2ab780b9742a1fc7469b2e4776b9773e2e825
SideMenu: 70ee5657df63ec3382660ec4ef470bf1cf5db07d
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
SwiftChart: ba767a678d568a5ee22d419e146a0582865e1aff

View File

@@ -1,3 +1,13 @@
# v5.0.1
- Restore 4.x level of support for extensions (#1357).
# v5.0.0
- Adds APIs for phone Auth testing to bypass the verification flow (#1192).
- Changes the callback block signature for sign in and create user methods
to provide an AuthDataResult that includes the user and user info (#1123, #1186).
- Removes GoogleToolboxForMac dependency (#1175).
- Removes miscellaneous deprecated APIs (#1188, #1200).
# v4.6.1
- Fixes crash which occurred when certain Firebase IDTokens were being parsed (#1076).

View File

@@ -19,6 +19,7 @@
#import "FIRAuth_Internal.h"
#import <FirebaseCore/FIRAppAssociationRegistration.h>
#import <FirebaseCore/FIRAppEnvironmentUtil.h>
#import <FirebaseCore/FIRAppInternal.h>
#import <FirebaseCore/FIRLogger.h>
#import <FirebaseCore/FIROptions.h>
@@ -438,7 +439,18 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
_settings = [[FIRAuthSettings alloc] init];
_firebaseAppName = [appName copy];
#if TARGET_OS_IOS
UIApplication *application = [UIApplication sharedApplication];
static Class applicationClass = nil;
// iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
// responds to it.
if (![FIRAppEnvironmentUtil isAppExtension]) {
Class cls = NSClassFromString(@"UIApplication");
if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
applicationClass = cls;
}
}
UIApplication *application = [applicationClass sharedApplication];
// Initialize the shared FIRAuthAppDelegateProxy instance in the main thread if not already.
[FIRAuthAppDelegateProxy sharedInstance];
#endif

View File

@@ -16,6 +16,8 @@
#import "FIRAuthAppDelegateProxy.h"
#import <FirebaseCore/FIRAppEnvironmentUtil.h>
#import <objc/runtime.h>
NS_ASSUME_NONNULL_BEGIN
@@ -200,8 +202,18 @@ static BOOL isIOS9orLater() {
+ (nullable instancetype)sharedInstance {
static dispatch_once_t onceToken;
static FIRAuthAppDelegateProxy *_Nullable sharedInstance;
// iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
// responds to it.
static Class applicationClass = nil;
if (![FIRAppEnvironmentUtil isAppExtension]) {
Class cls = NSClassFromString(@"UIApplication");
if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
applicationClass = cls;
}
}
UIApplication *application = [applicationClass sharedApplication];
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initWithApplication:[UIApplication sharedApplication]];
sharedInstance = [[self alloc] initWithApplication:application];
});
return sharedInstance;
}

View File

@@ -16,6 +16,8 @@
#import "FIRAuthDefaultUIDelegate.h"
#import <FirebaseCore/FIRAppEnvironmentUtil.h>
NS_ASSUME_NONNULL_BEGIN
@interface FIRAuthDefaultUIDelegate ()
@@ -58,8 +60,17 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (id<FIRAuthUIDelegate>)defaultUIDelegate {
UIViewController *topViewController =
[UIApplication sharedApplication].keyWindow.rootViewController;
// iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
// responds to it.
static Class applicationClass = nil;
if (![FIRAppEnvironmentUtil isAppExtension]) {
Class cls = NSClassFromString(@"UIApplication");
if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
applicationClass = cls;
}
}
UIApplication *application = [applicationClass sharedApplication];
UIViewController *topViewController = application.keyWindow.rootViewController;
while (true){
if (topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;

View File

@@ -357,7 +357,7 @@ static NSString *const kFIRAuthErrorMessageNotificationNotForwarded = @"If app d
*/
static NSString *const kFIRAuthErrorMessageAppNotVerified = @"Firebase could not retrieve the "
"silent push notification and therefore could not verify your app. Ensure that you configured "
"your app correctly to recieve push notifications.";
"your app correctly to receive push notifications.";
/** @var kFIRAuthErrorMessageCaptchaCheckFailed
@brief Message for @c FIRAuthErrorCodeCaptchaCheckFailed error code.

View File

@@ -410,20 +410,6 @@ NS_SWIFT_NAME(Auth)
@param password The user's password.
@param completion Optionally; a block which is invoked when the sign in flow finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
@remarks Possible error codes:
+ `FIRAuthErrorCodeOperationNotAllowed` - Indicates that email and password
accounts are not enabled. Enable them in the Auth section of the
Firebase console.
+ `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled.
+ `FIRAuthErrorCodeWrongPassword` - Indicates the user attempted
sign in with an incorrect password.
+ `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed.
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
*/
- (void)signInAndRetrieveDataWithEmail:(NSString *)email
password:(NSString *)password
@@ -543,21 +529,6 @@ NS_SWIFT_NAME(Auth)
`signInAnonymously(Completion:)` for Swift instead.
@param completion Optionally; a block which is invoked when the sign in finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
@remarks If there is already an anonymous user signed in, that user will be returned instead.
If there is any other existing user signed in, that user will be signed out.
@remarks Possible error codes:
+ `FIRAuthErrorCodeOperationNotAllowed` - Indicates that anonymous accounts are
not enabled. Enable them in the Auth section of the Firebase console.
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
@remarks This method will only exist until the next major Firebase release following 4.x.x.
After the next major release the method `signInAnonymouslyWithCompletion` will support the
`FIRAuthDataResultCallback`.
*/
- (void)signInAnonymouslyAndRetrieveDataWithCompletion:
(nullable FIRAuthDataResultCallback)completion
@@ -592,22 +563,6 @@ NS_SWIFT_NAME(Auth)
@param token A self-signed custom auth token.
@param completion Optionally; a block which is invoked when the sign in finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
@remarks Possible error codes:
+ `FIRAuthErrorCodeInvalidCustomToken` - Indicates a validation error with
the custom token.
+ `FIRAuthErrorCodeCustomTokenMismatch` - Indicates the service account and the API key
belong to different projects.
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
@remarks This method will only exist until the next major Firebase release following 4.x.x.
After the next major release the method `createUserWithEmail:password:completion:` will
support the `FIRAuthDataResultCallback`.
*/
- (void)signInAndRetrieveDataWithCustomToken:(NSString *)token
completion:(nullable FIRAuthDataResultCallback)completion
@@ -650,24 +605,6 @@ NS_SWIFT_NAME(Auth)
@param password The user's desired password.
@param completion Optionally; a block which is invoked when the sign up flow finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
@remarks Possible error codes:
+ `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed.
+ `FIRAuthErrorCodeEmailAlreadyInUse` - Indicates the email used to attempt sign up
already exists. Call fetchProvidersForEmail to check which sign-in mechanisms the user
used, and prompt the user to sign in with one of those.
+ `FIRAuthErrorCodeOperationNotAllowed` - Indicates that email and password accounts
are not enabled. Enable them in the Auth section of the Firebase console.
+ `FIRAuthErrorCodeWeakPassword` - Indicates an attempt to set a password that is
considered too weak. The NSLocalizedFailureReasonErrorKey field in the NSError.userInfo
dictionary object will contain more detailed explanation that can be shown to the user.
@remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
@remarks This method will only exist until the next major Firebase release following 4.x.x.
After the next major release the method `createUserWithEmail:password:completion:` will
support the `FIRAuthDataResultCallback`.
*/
- (void)createUserAndRetrieveDataWithEmail:(NSString *)email
password:(NSString *)password

View File

@@ -43,7 +43,7 @@ NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED";
NSString *const kFIRLibraryVersionID =
@"5" // Major version (one or more digits)
@"00" // Minor version (exactly 2 digits)
@"02" // Build number (exactly 2 digits)
@"03" // Build number (exactly 2 digits)
@"000"; // Fixed "000"
// Plist file name.
NSString *const kServiceInfoFileName = @"GoogleService-Info";

View File

@@ -1,3 +1,8 @@
# 2018-06-12 -- v3.1.0
- Added a new API to fetch InstanceID and Token with a completion handler. The completion handler returns a FIRInstanceIDResult with a instanceID and a token properties.
- Deprecated the token method.
- Added support to log a new customized label provided by developer.
# 2018-05-08 -- v3.0.0
- Removed deprecated method `setAPNSToken:type` defined in FIRInstanceID, please use `setAPNSToken:type` defined in FIRMessaging instead.
- Removed deprecated enum `FIRInstanceIDAPNSTokenType` defined in FIRInstanceID, please use `FIRMessagingAPNSTokenType` defined in FIRMessaging instead.

View File

@@ -1,11 +1,14 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class FIRInstanceIDResult;
/**
* @memberof FIRInstanceID
*
* The scope to be used when fetching/deleting a token for Firebase Messaging.
*/
FOUNDATION_EXPORT NSString *__nonnull const kFIRInstanceIDScopeFirebaseMessaging
FOUNDATION_EXPORT NSString *const kFIRInstanceIDScopeFirebaseMessaging
NS_SWIFT_NAME(InstanceIDScopeFirebaseMessaging);
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@@ -17,7 +20,7 @@ FOUNDATION_EXPORT NSString *__nonnull const kFIRInstanceIDScopeFirebaseMessaging
* Instance ID service will throttle the refresh event across all devices
* to control the rate of token updates on application servers.
*/
FOUNDATION_EXPORT const NSNotificationName __nonnull kFIRInstanceIDTokenRefreshNotification
FOUNDATION_EXPORT const NSNotificationName kFIRInstanceIDTokenRefreshNotification
NS_SWIFT_NAME(InstanceIDTokenRefresh);
#else
/**
@@ -28,7 +31,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull kFIRInstanceIDTokenRefreshN
* Instance ID service will throttle the refresh event across all devices
* to control the rate of token updates on application servers.
*/
FOUNDATION_EXPORT NSString *__nonnull const kFIRInstanceIDTokenRefreshNotification
FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenRefreshNotification
NS_SWIFT_NAME(InstanceIDTokenRefreshNotification);
#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
@@ -56,7 +59,7 @@ typedef void (^FIRInstanceIDTokenHandler)(NSString *__nullable token, NSError *_
* @param error The error describing why deleting the token failed.
* See the error codes below for a more detailed description.
*/
typedef void (^FIRInstanceIDDeleteTokenHandler)(NSError *__nullable error)
typedef void (^FIRInstanceIDDeleteTokenHandler)(NSError *error)
NS_SWIFT_NAME(InstanceIDDeleteTokenHandler);
/**
@@ -84,6 +87,20 @@ typedef void (^FIRInstanceIDHandler)(NSString *__nullable identity, NSError *__n
typedef void (^FIRInstanceIDDeleteHandler)(NSError *__nullable error)
NS_SWIFT_NAME(InstanceIDDeleteHandler);
/**
* @related FIRInstanceID
*
* The completion handler invoked when the app identity and token are fetched. If the
* identity wasn't created for some reason we return the appropriate error code.
*
* @param result The result containing an identity for the app instance and a valid token,
* nil if there was an error while creating the result.
* @param error The error if fetching the identity or token fails else nil.
*/
typedef void (^FIRInstanceIDResultHandler)(FIRInstanceIDResult *__nullable result,
NSError *__nullable error)
NS_SWIFT_NAME(InstanceIDResultHandler);
/**
* Public errors produced by InstanceID.
*/
@@ -113,6 +130,24 @@ typedef NS_ENUM(NSUInteger, FIRInstanceIDError) {
FIRInstanceIDErrorInvalidRequest = 7,
} NS_SWIFT_NAME(InstanceIDError);
/**
* A class contains the results of InstanceID and token query.
*/
NS_SWIFT_NAME(InstanceIDResult)
@interface FIRInstanceIDResult : NSObject <NSCopying>
/**
* An instanceID uniquely identifies the app instance.
*/
@property(nonatomic, readonly, copy) NSString *instanceID;
/*
* Returns a Firebase Messaging scoped token for the firebase app.
*/
@property(nonatomic, readonly, copy) NSString *token;
@end
/**
* Instance ID provides a unique identifier for each app instance and a mechanism
* to authenticate and authorize actions (for example, sending an FCM message).
@@ -140,22 +175,30 @@ NS_SWIFT_NAME(InstanceID)
*
* @return A shared instance of FIRInstanceID.
*/
+ (nonnull instancetype)instanceID NS_SWIFT_NAME(instanceID());
+ (instancetype)instanceID NS_SWIFT_NAME(instanceID());
/**
* Unavailable. Use +instanceID instead.
*/
- (nonnull instancetype)init __attribute__((unavailable("Use +instanceID instead.")));
- (instancetype)init __attribute__((unavailable("Use +instanceID instead.")));
#pragma mark - Tokens
/**
* Returns a result of app instance identifier InstanceID and a Firebase Messaging scoped token.
* param handler The callback handler invoked when an app instanceID and a default token
* are generated and returned. If instanceID and token fetching fail for some
* reason the callback is invoked with nil `result` and the appropriate error.
*/
- (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler;
/**
* Returns a Firebase Messaging scoped token for the firebase app.
*
* @return Returns the stored token if the device has registered with Firebase Messaging, otherwise
* returns nil.
*/
- (nullable NSString *)token;
- (nullable NSString *)token __deprecated_msg("Use instanceIDWithHandler: instead.");
/**
* Returns a token that authorizes an Entity (example: cloud service) to perform
@@ -196,10 +239,10 @@ NS_SWIFT_NAME(InstanceID)
* is nil and a valid `error` is returned. The valid error
* codes have been documented above.
*/
- (void)tokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity
scope:(nonnull NSString *)scope
- (void)tokenWithAuthorizedEntity:(NSString *)authorizedEntity
scope:(NSString *)scope
options:(nullable NSDictionary *)options
handler:(nonnull FIRInstanceIDTokenHandler)handler;
handler:(FIRInstanceIDTokenHandler)handler;
/**
* Revokes access to a scope (action) for an entity previously
@@ -220,9 +263,9 @@ NS_SWIFT_NAME(InstanceID)
* In case of error an appropriate error object is returned
* else error is nil.
*/
- (void)deleteTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity
scope:(nonnull NSString *)scope
handler:(nonnull FIRInstanceIDDeleteTokenHandler)handler;
- (void)deleteTokenWithAuthorizedEntity:(NSString *)authorizedEntity
scope:(NSString *)scope
handler:(FIRInstanceIDDeleteTokenHandler)handler;
#pragma mark - Identity
@@ -240,7 +283,7 @@ NS_SWIFT_NAME(InstanceID)
* a valid identifier is returned and a valid identifier for the
* application instance.
*/
- (void)getIDWithHandler:(nonnull FIRInstanceIDHandler)handler NS_SWIFT_NAME(getID(handler:));
- (void)getIDWithHandler:(FIRInstanceIDHandler)handler NS_SWIFT_NAME(getID(handler:));
/**
* Resets Instance ID and revokes all tokens.
@@ -254,7 +297,8 @@ NS_SWIFT_NAME(InstanceID)
* (like FCM, RemoteConfig or Analytics) or user explicitly calls Instance ID APIs to get an
* Instance ID and token again.
*/
- (void)deleteIDWithHandler:(nonnull FIRInstanceIDDeleteHandler)handler
NS_SWIFT_NAME(deleteID(handler:));
- (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler NS_SWIFT_NAME(deleteID(handler:));
@end
NS_ASSUME_NONNULL_END

40
Pods/Manifest.lock generated
View File

@@ -3,15 +3,15 @@ PODS:
- ChameleonFramework (2.1.0):
- ChameleonFramework/Default (= 2.1.0)
- ChameleonFramework/Default (2.1.0)
- Firebase/Auth (5.1.0):
- Firebase/Auth (5.2.0):
- Firebase/CoreOnly
- FirebaseAuth (= 5.0.0)
- Firebase/Core (5.1.0):
- FirebaseAuth (= 5.0.1)
- Firebase/Core (5.2.0):
- Firebase/CoreOnly
- FirebaseAnalytics (= 5.0.1)
- Firebase/CoreOnly (5.1.0):
- FirebaseCore (= 5.0.2)
- Firebase/Database (5.1.0):
- Firebase/CoreOnly (5.2.0):
- FirebaseCore (= 5.0.3)
- Firebase/Database (5.2.0):
- Firebase/CoreOnly
- FirebaseDatabase (= 5.0.1)
- FirebaseAnalytics (5.0.1):
@@ -19,15 +19,15 @@ PODS:
- FirebaseInstanceID (~> 3.0)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- nanopb (~> 0.3)
- FirebaseAuth (5.0.0):
- FirebaseAuth (5.0.1):
- FirebaseCore (~> 5.0)
- GTMSessionFetcher/Core (~> 1.1)
- FirebaseCore (5.0.2):
- FirebaseCore (5.0.3):
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- FirebaseDatabase (5.0.1):
- FirebaseCore (~> 5.0)
- leveldb-library (~> 1.18)
- FirebaseInstanceID (3.0.0):
- FirebaseInstanceID (3.1.0):
- FirebaseCore (~> 5.0)
- GoogleToolboxForMac/Defines (2.1.4)
- "GoogleToolboxForMac/NSData+zlib (2.1.4)":
@@ -44,11 +44,11 @@ PODS:
- Alamofire (~> 4.0)
- PromiseKit/CorePromise
- PromiseKit/CorePromise (6.2.8)
- Realm (3.6.0):
- Realm/Headers (= 3.6.0)
- Realm/Headers (3.6.0)
- RealmSwift (3.6.0):
- Realm (= 3.6.0)
- Realm (3.7.0):
- Realm/Headers (= 3.7.0)
- Realm/Headers (3.7.0)
- RealmSwift (3.7.0):
- Realm (= 3.7.0)
- SideMenu (4.0.0)
- SVProgressHUD (2.2.5)
- SwiftChart (1.0.1)
@@ -94,20 +94,20 @@ SPEC REPOS:
SPEC CHECKSUMS:
Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
ChameleonFramework: d21a3cc247abfe5e37609a283a8238b03575cf64
Firebase: e08fb0795f35707aeb1d8a715c731c45bdf6fd56
Firebase: 25ed0412036d7d008568d1fb4d2e9d81ea8a0a2c
FirebaseAnalytics: b3628aea54c50464c32c393fb2ea032566e7ecc2
FirebaseAuth: acbeef02fe7c3a26624e309849f3fe30c84115af
FirebaseCore: b81044df1044c0857a0737c6324678b72d4f7f00
FirebaseAuth: 463b8ce33bd5d05f706dcd4615499e3212b4132b
FirebaseCore: a3c87242451633fff8490183898075ce77d168d2
FirebaseDatabase: 482bad9c2abd422bb2321194fb8c937e67426a89
FirebaseInstanceID: 83e0040351565df711a5db3d8ebe5ea21aca998a
FirebaseInstanceID: 05d779cbb97bd5bd5c51a38a903fc9cfe1b2454a
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
GTMSessionFetcher: 5fa5b80fd20e439ef5f545fb2cb3ca6c6714caa2
Kingfisher: 976d828df2b24834c6a3f2fc4d82cdbd26552be1
leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
PromiseKit: 6788ce1a0ed5448b83d4aaf56b9fc49fb7647d32
Realm: 08b464b462d4f31bbd4ba5f5a1c8722ef0a700b7
RealmSwift: 973e1499c44ab571f894c2c908e2db450be280c3
Realm: 4998c6ced1ea15b3792f273b18f8e6faaf935b5c
RealmSwift: 7dc2ab780b9742a1fc7469b2e4776b9773e2e825
SideMenu: 70ee5657df63ec3382660ec4ef470bf1cf5db07d
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
SwiftChart: ba767a678d568a5ee22d419e146a0582865e1aff

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
#import "RLMRealm_Private.hpp"
#import "RLMResults_Private.hpp"
#import "RLMSchema.h"
#import "RLMSyncSession.h"
#import "results.hpp"
#import "sync/partial_sync.hpp"
@@ -51,4 +52,8 @@ using namespace realm;
partial_sync::register_query(_realm, className.UTF8String, query.UTF8String, std::move(cb));
}
- (RLMSyncSession *)syncSession {
return [RLMSyncSession sessionForRealm:self];
}
@end

View File

@@ -94,7 +94,7 @@ static BOOL isValidRealmURL(NSURL *url) {
return [self.realmURL isEqual:that.realmURL]
&& [self.user isEqual:that.user]
&& self.stopPolicy == that.stopPolicy
&& self.isPartial == that.isPartial;
&& self.fullSynchronization == that.fullSynchronization;
}
- (void)setEnableSSLValidation:(BOOL)enableSSLValidation {
@@ -113,6 +113,14 @@ static BOOL isValidRealmURL(NSURL *url) {
return (BOOL)_config->is_partial;
}
- (void)setFullSynchronization:(BOOL)fullSynchronization {
_config->is_partial = !(bool)fullSynchronization;
}
- (BOOL)fullSynchronization {
return !(BOOL)_config->is_partial;
}
- (realm::SyncConfig)rawConfiguration {
return *_config;
}
@@ -158,6 +166,23 @@ static BOOL isValidRealmURL(NSURL *url) {
errorHandler:nullptr];
}
- (instancetype)initWithUser:(RLMSyncUser *)user
realmURL:(NSURL *)url
isPartial:(BOOL)isPartial
urlPrefix:(NSString *)urlPrefix
stopPolicy:(RLMSyncStopPolicy)stopPolicy
enableSSLValidation:(BOOL)enableSSLValidation {
auto config = [self initWithUser:user
realmURL:url
customFileURL:nil
isPartial:isPartial
stopPolicy:stopPolicy
errorHandler:nullptr];
config.urlPrefix = urlPrefix;
config.enableSSLValidation = enableSSLValidation;
return config;
}
- (instancetype)initWithUser:(RLMSyncUser *)user
realmURL:(NSURL *)url
customFileURL:(nullable NSURL *)customFileURL

View File

@@ -24,6 +24,8 @@
#import "RLMRealmConfiguration_Private.hpp"
#import "RLMRealmUtil.hpp"
#import "RLMResults_Private.hpp"
#import "RLMSyncConfiguration.h"
#import "RLMSyncConfiguration_Private.hpp"
#import "RLMSyncManager_Private.h"
#import "RLMSyncPermissionResults.h"
#import "RLMSyncPermission_Private.hpp"
@@ -156,11 +158,9 @@ PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBloc
- (instancetype)initPrivate {
if (self = [super init]) {
_configMaker = std::make_unique<ConfigMaker>([](std::shared_ptr<SyncUser> user, std::string url) {
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())];
RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:objCUser realmURL:objCUrl];
return [config config];
return [objCUser configurationWithURL:objCUrl fullSynchronization:true].config;
});
return self;
}
@@ -206,6 +206,44 @@ PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBloc
completionBlock:completion];
}
- (RLMRealmConfiguration *)configuration {
return [self configurationWithURL:nil
fullSynchronization:NO
enableSSLValidation:YES
urlPrefix:nil];
}
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url {
return [self configurationWithURL:url
fullSynchronization:NO
enableSSLValidation:YES
urlPrefix:nil];
}
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url fullSynchronization:(bool)fullSynchronization {
return [self configurationWithURL:url
fullSynchronization:fullSynchronization
enableSSLValidation:YES
urlPrefix:nil];
}
- (RLMRealmConfiguration *)configurationWithURL:(NSURL *)url
fullSynchronization:(bool)fullSynchronization
enableSSLValidation:(bool)enableSSLValidation
urlPrefix:(NSString * _Nullable)urlPrefix {
auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self
realmURL:url ?: self.defaultRealmURL
customFileURL:nil
isPartial:!fullSynchronization
stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
errorHandler:nullptr];
syncConfig.urlPrefix = urlPrefix;
syncConfig.enableSSLValidation = enableSSLValidation;
RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
config.syncConfiguration = syncConfig;
return config;
}
- (void)logOut {
if (!_user) {
return;

75
Pods/Realm/build.sh generated
View File

@@ -42,7 +42,6 @@ Usage: sh $0 command [argument]
command:
clean: clean up/remove all generated files
download-core: downloads core library (binary version)
download-object-server: downloads and installs the Realm Object Server
download-sync: downloads sync library (binary version, core+sync)
build: builds all iOS and OS X frameworks
ios-static: builds fat iOS static framework
@@ -124,6 +123,11 @@ xc() {
fi
}
xctest() {
xc "$@" build
xc "$@" test
}
copy_bcsymbolmap() {
find "$1" -name '*.bcsymbolmap' -type f -exec cp {} "$2" \;
}
@@ -304,26 +308,6 @@ fi
# Downloading
######################################
kill_object_server() {
set +e
# Based on build.sh conventions we always run ROS from a path ending in 'ros/bin/ros'.
pkill -f ros/bin/ros\ start
# 0 = process killed, 1 = no processes running, 2+ = error
local status=$?
if [ $status -gt 1 ]; then
exit $status
fi
set -e
}
download_object_server() {
rm -rf ./test-ros-instance
mkdir -p ./test-ros-instance/ros
chmod 777 ./test-ros-instance
/usr/local/bin/node /usr/local/bin/npm install --scripts-prepend-node-path=auto --prefix ./test-ros-instance/ros \
-g realm-object-server@${REALM_OBJECT_SERVER_VERSION}
}
download_common() {
local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path
@@ -421,38 +405,6 @@ case "$COMMAND" in
exit 0
;;
######################################
# Object Server
######################################
"download-object-server")
download_object_server
exit 0
;;
"reset-ros-server-state")
rm -rf "./test-ros-instance/data"
rm -rf "./test-ros-instance/realm-object-server"
exit 0
;;
"reset-ros-client-state")
rm -rf ~/Library/Application\ Support/xctest
rm -rf ~/Library/Application\ Support/io.realm.TestHost
rm -rf ~/Library/Application\ Support/xctest-child
exit 0
;;
"reset-object-server")
kill_object_server
# Add a short delay, so file system doesn't complain about files in use
sleep 1
sh build.sh reset-ros-server-state
sh build.sh reset-ros-client-state
# Add another delay to ensure files are actually gone from file system
sleep 1
exit 0
;;
######################################
# Core
######################################
@@ -678,7 +630,7 @@ case "$COMMAND" in
else
destination="Apple TV 1080p"
fi
xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
xctest "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'"
exit $?
;;
@@ -688,7 +640,7 @@ case "$COMMAND" in
else
destination="Apple TV 1080p"
fi
xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
xctest "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'"
exit $?
;;
@@ -701,17 +653,17 @@ case "$COMMAND" in
if [[ "$CONFIGURATION" == "Debug" ]]; then
COVERAGE_PARAMS="GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"
fi
xc "-scheme Realm -configuration $CONFIGURATION test $COVERAGE_PARAMS"
xctest "-scheme Realm -configuration $CONFIGURATION $COVERAGE_PARAMS"
exit 0
;;
"test-osx-swift")
xc "-scheme RealmSwift -configuration $CONFIGURATION test"
xctest "-scheme RealmSwift -configuration $CONFIGURATION"
exit 0
;;
"test-osx-object-server")
xc "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx test"
xctest "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx"
exit 0
;;
@@ -849,9 +801,7 @@ case "$COMMAND" in
;;
"verify-osx-object-server")
sh build.sh download-object-server
sh build.sh test-osx-object-server
sh build.sh reset-object-server
exit 0
;;
@@ -1079,6 +1029,8 @@ EOM
"ci-pr")
mkdir -p build/reports
export REALM_DISABLE_ANALYTICS=1
export REALM_DISABLE_UPDATE_CHECKER=1
# FIXME: Re-enable once CI can properly unlock the keychain
export REALM_DISABLE_METADATA_ENCRYPTION=1
@@ -1098,6 +1050,9 @@ EOM
export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO REALM_PREFIX_HEADER=Realm/RLMPrefix.h'
sh build.sh prelaunch-simulator
source $(brew --prefix nvm)/nvm.sh
export REALM_NODE_PATH="$(nvm which 8)"
# Reset CoreSimulator.log
mkdir -p ~/Library/Logs/CoreSimulator
echo > ~/Library/Logs/CoreSimulator/CoreSimulator.log

View File

@@ -20,7 +20,7 @@
#import "RLMRealm.h"
@class RLMResults;
@class RLMResults, RLMSyncSession;
/**
A callback used to vend the results of a partial sync fetch.
@@ -45,6 +45,12 @@ NS_ASSUME_NONNULL_BEGIN
- (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback
__deprecated_msg("Use -[RLMResults subscribe]");
/**
Get the RLMSyncSession used by this Realm. Will be nil if this is not a
synchronized Realm.
*/
@property (nonatomic, nullable, readonly) RLMSyncSession *syncSession;
@end
NS_ASSUME_NONNULL_END

View File

@@ -54,12 +54,23 @@ NS_ASSUME_NONNULL_BEGIN
except those matching queries that the user explicitly specifies.
@warning Partial synchronization is a tech preview. Its APIs are subject to change.
*/
@property (nonatomic) BOOL isPartial;
*/
@property (nonatomic) BOOL isPartial DEPRECATED_MSG_ATTRIBUTE("Use 'fullSynchronization' instead.");
/**
The prefix that is prepended to the path in the HTTP request
that initiates a sync connection. The value specified must match with the server's expectation.
Whether this Realm should be a fully synchronized Realm.
Synchronized Realms comes in two flavors: Query-based and Fully synchronized.
A fully synchronized Realm will automatically synchronize the entire Realm in
the background while a query-based Realm will only synchronize the data being
subscribed to. Synchronized realms are by default query-based unless this
boolean is set.
*/
@property (nonatomic) BOOL fullSynchronization;
/**
The prefix that is prepended to the path in the HTTP request that initiates a
sync connection. The value specified must match with the server's expectation.
Changing the value of `urlPrefix` should be matched with a corresponding
change of the server's configuration.
If no value is specified here then the default `/realm-sync` path is used.
@@ -75,21 +86,21 @@ NS_ASSUME_NONNULL_BEGIN
contain the wildcard marker `~`, which will automatically be filled in with
the user identity by the Realm Object Server.
*/
- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url;
- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url __attribute__((deprecated("Use [RLMSyncUser configurationWithURL] instead")));
/**
Return a Realm configuration for syncing with the default Realm of the currently logged-in sync user.
Partial synchronization is enabled in the returned configuration.
*/
+ (RLMRealmConfiguration *)automaticConfiguration;
+ (RLMRealmConfiguration *)automaticConfiguration __attribute__((deprecated("Use [RLMSyncUser configuration] instead")));
/**
Return a Realm configuration for syncing with the default Realm of the given sync user.
Partial synchronization is enabled in the returned configuration.
*/
+ (RLMRealmConfiguration *)automaticConfigurationForUser:(RLMSyncUser *)user;
+ (RLMRealmConfiguration *)automaticConfigurationForUser:(RLMSyncUser *)user __attribute__((deprecated("Use [RLMSyncUser configuration] instead")));
/// :nodoc:
- (instancetype)init __attribute__((unavailable("This type cannot be created directly")));

View File

@@ -28,6 +28,13 @@ typedef NS_ENUM(NSUInteger, RLMSyncStopPolicy) {
@interface RLMSyncConfiguration ()
- (instancetype)initWithUser:(RLMSyncUser *)user
realmURL:(NSURL *)url
isPartial:(BOOL)isPartial
urlPrefix:(nullable NSString *)urlPrefix
stopPolicy:(RLMSyncStopPolicy)stopPolicy
enableSSLValidation:(BOOL)enableSSLValidation;
@property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy;
// Internal-only APIs

View File

@@ -18,14 +18,31 @@
#import "RLMSyncConfiguration_Private.h"
#import <functional>
#import <memory>
namespace realm {
class SyncSession;
struct SyncConfig;
struct SyncError;
using SyncSessionErrorHandler = void(std::shared_ptr<SyncSession>, SyncError);
}
NS_ASSUME_NONNULL_BEGIN
@interface RLMSyncConfiguration ()
- (instancetype)initWithUser:(RLMSyncUser *)user
realmURL:(NSURL *)url
customFileURL:(nullable NSURL *)customFileURL
isPartial:(BOOL)isPartial
stopPolicy:(RLMSyncStopPolicy)stopPolicy
errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler;
- (instancetype)initWithRawConfig:(realm::SyncConfig)config;
- (realm::SyncConfig)rawConfiguration;
@end
NS_ASSUME_NONNULL_END

View File

@@ -18,6 +18,7 @@
#import <Foundation/Foundation.h>
#import "RLMRealmConfiguration.h"
#import "RLMResults.h"
#import "RLMSyncCredentials.h"
#import "RLMSyncPermission.h"
@@ -145,6 +146,64 @@ NS_ASSUME_NONNULL_BEGIN
onCompletion:(RLMUserCompletionBlock)completion
NS_SWIFT_UNAVAILABLE("Use the full version of this API.");
/**
Returns the default configuration for the user. The default configuration
points to the default query-based Realm on the server the user authenticated against.
*/
- (RLMRealmConfiguration *)configuration NS_REFINED_FOR_SWIFT;
/**
Create a query-based configuration instance for the given url.
@param url The unresolved absolute URL to the Realm on the Realm Object Server,
e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the
path should contain the wildcard marker `~`, which will automatically
be filled in with the user identity by the Realm Object Server.
@return A default configuration object with the sync configuration set to use the given URL.
*/
- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
/**
Create a configuration instance for the given url.
@param url The unresolved absolute URL to the Realm on the Realm Object Server,
e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the
path should contain the wildcard marker `~`, which will automatically
be filled in with the user identity by the Realm Object Server.
@param fullSynchronization If YES, all objects in the server Realm are
automatically synchronized, and the query subscription
methods cannot be used.
@return A default configuration object with the sync configuration set to use
the given URL and options.
*/
- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url
fullSynchronization:(bool)fullSynchronization NS_REFINED_FOR_SWIFT;
/**
Create a configuration instance for the given url.
@param url The unresolved absolute URL to the Realm on the Realm Object Server,
e.g. "realm://example.org/~/path/to/realm". "Unresolved" means the
path should contain the wildcard marker `~`, which will automatically
be filled in with the user identity by the Realm Object Server.
@param fullSynchronization If YES, all objects in the server Realm are
automatically synchronized, and the query subscription
methods cannot be used.
@param enableSSLValidation If NO, invalid SSL certificates for the server will
not be rejected. THIS SHOULD NEVER BE USED IN
PRODUCTION AND EXISTS ONLY FOR TESTING PURPOSES.
@param urlPrefix A prefix which is prepending to URLs constructed for
the server. This should normally be `nil`, and customized only
to match corresponding settings on the server.
@return A default configuration object with the sync configuration set to use
the given URL and options.
*/
- (RLMRealmConfiguration *)configurationWithURL:(nullable NSURL *)url
fullSynchronization:(bool)fullSynchronization
enableSSLValidation:(bool)enableSSLValidation
urlPrefix:(nullable NSString *)urlPrefix NS_REFINED_FOR_SWIFT;
/**
Log a user out, destroying their server state, unregistering them from the SDK,
and removing any synced Realms associated with them from on-disk storage on

View File

@@ -1 +1 @@
let swiftLanguageVersion = "4.1"
let swiftLanguageVersion = "4.1.2"

View File

@@ -17,7 +17,7 @@
////////////////////////////////////////////////////////////////////////////
import Realm
import Foundation
import Realm.Private
/**
An object representing a Realm Object Server user.
@@ -210,7 +210,20 @@ public struct SyncConfiguration {
-warning: Partial synchronization is a tech preview. Its APIs are subject to change.
*/
public let isPartial: Bool
@available(*, deprecated, message: "Use fullSynchronization instead")
public var isPartial: Bool {
return !fullSynchronization
}
/**
Whether this Realm should be a fully synchronized Realm.
Synchronized Realms comes in two flavors: Query-based and Fully synchronized.
A fully synchronized Realm will automatically synchronize the entire Realm in the background
while a query-based Realm will only synchronize the data being subscribed to.
Synchronized realms are by default query-based unless this boolean is set.
*/
public let fullSynchronization: Bool
/**
The prefix that is prepended to the path in the HTTP request
@@ -226,17 +239,16 @@ public struct SyncConfiguration {
self.realmURL = config.realmURL
self.stopPolicy = config.stopPolicy
self.enableSSLValidation = config.enableSSLValidation
self.isPartial = config.isPartial
self.fullSynchronization = config.fullSynchronization
self.urlPrefix = config.urlPrefix
}
func asConfig() -> RLMSyncConfiguration {
let config = RLMSyncConfiguration(user: user, realmURL: realmURL)
config.stopPolicy = stopPolicy
config.enableSSLValidation = enableSSLValidation
config.isPartial = isPartial
config.urlPrefix = urlPrefix
return config
return RLMSyncConfiguration(user: user, realmURL: realmURL,
isPartial: !fullSynchronization,
urlPrefix: urlPrefix,
stopPolicy: stopPolicy,
enableSSLValidation: enableSSLValidation)
}
/**
@@ -253,12 +265,13 @@ public struct SyncConfiguration {
- warning: NEVER disable SSL validation for a system running in production.
*/
@available(*, deprecated, message: "Use SyncUser.configuration() instead")
public init(user: SyncUser, realmURL: URL, enableSSLValidation: Bool = true, isPartial: Bool = false, urlPrefix: String? = nil) {
self.user = user
self.realmURL = realmURL
self.stopPolicy = .afterChangesUploaded
self.enableSSLValidation = enableSSLValidation
self.isPartial = isPartial
self.fullSynchronization = !isPartial
self.urlPrefix = urlPrefix
}
@@ -269,6 +282,7 @@ public struct SyncConfiguration {
- requires: There be exactly one logged-in `SyncUser`
*/
@available(*, deprecated, message: "Use SyncUser.configuration() instead")
public static func automatic() -> Realm.Configuration {
return ObjectiveCSupport.convert(object: RLMSyncConfiguration.automaticConfiguration())
}
@@ -278,6 +292,7 @@ public struct SyncConfiguration {
Partial synchronization is enabled in the returned configuration.
*/
@available(*, deprecated, message: "Use SyncUser.configuration() instead")
public static func automatic(user: SyncUser) -> Realm.Configuration {
return ObjectiveCSupport.convert(object: RLMSyncConfiguration.automaticConfiguration(for: user))
}
@@ -482,6 +497,29 @@ extension SyncUser {
callback(token, nil)
}
}
/**
Create a sync configuration instance.
Additional settings can be optionally specified. Descriptions of these
settings follow.
`enableSSLValidation` is true by default. It can be disabled for debugging
purposes.
- warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
`.realm`, `.realm.lock` or `.realm.management`.
- warning: NEVER disable SSL validation for a system running in production.
*/
public func configuration(realmURL: URL? = nil, fullSynchronization: Bool = false,
enableSSLValidation: Bool = true, urlPrefix: String? = nil) -> Realm.Configuration {
let config = self.__configuration(with: realmURL,
fullSynchronization: fullSynchronization,
enableSSLValidation: enableSSLValidation,
urlPrefix: urlPrefix)
return ObjectiveCSupport.convert(object: config)
}
}
/**
@@ -647,6 +685,14 @@ extension Realm {
completion(results.map { Results<T>($0) }, error)
}
}
/**
Get the SyncSession used by this Realm. Will be nil if this is not a
synchronized Realm.
*/
public var syncSession: SyncSession? {
return SyncSession(for: rlmRealm)
}
}
// MARK: - Permissions and permission results

View File

@@ -42,7 +42,6 @@ Usage: sh $0 command [argument]
command:
clean: clean up/remove all generated files
download-core: downloads core library (binary version)
download-object-server: downloads and installs the Realm Object Server
download-sync: downloads sync library (binary version, core+sync)
build: builds all iOS and OS X frameworks
ios-static: builds fat iOS static framework
@@ -124,6 +123,11 @@ xc() {
fi
}
xctest() {
xc "$@" build
xc "$@" test
}
copy_bcsymbolmap() {
find "$1" -name '*.bcsymbolmap' -type f -exec cp {} "$2" \;
}
@@ -304,26 +308,6 @@ fi
# Downloading
######################################
kill_object_server() {
set +e
# Based on build.sh conventions we always run ROS from a path ending in 'ros/bin/ros'.
pkill -f ros/bin/ros\ start
# 0 = process killed, 1 = no processes running, 2+ = error
local status=$?
if [ $status -gt 1 ]; then
exit $status
fi
set -e
}
download_object_server() {
rm -rf ./test-ros-instance
mkdir -p ./test-ros-instance/ros
chmod 777 ./test-ros-instance
/usr/local/bin/node /usr/local/bin/npm install --scripts-prepend-node-path=auto --prefix ./test-ros-instance/ros \
-g realm-object-server@${REALM_OBJECT_SERVER_VERSION}
}
download_common() {
local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path
@@ -421,38 +405,6 @@ case "$COMMAND" in
exit 0
;;
######################################
# Object Server
######################################
"download-object-server")
download_object_server
exit 0
;;
"reset-ros-server-state")
rm -rf "./test-ros-instance/data"
rm -rf "./test-ros-instance/realm-object-server"
exit 0
;;
"reset-ros-client-state")
rm -rf ~/Library/Application\ Support/xctest
rm -rf ~/Library/Application\ Support/io.realm.TestHost
rm -rf ~/Library/Application\ Support/xctest-child
exit 0
;;
"reset-object-server")
kill_object_server
# Add a short delay, so file system doesn't complain about files in use
sleep 1
sh build.sh reset-ros-server-state
sh build.sh reset-ros-client-state
# Add another delay to ensure files are actually gone from file system
sleep 1
exit 0
;;
######################################
# Core
######################################
@@ -678,7 +630,7 @@ case "$COMMAND" in
else
destination="Apple TV 1080p"
fi
xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
xctest "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'"
exit $?
;;
@@ -688,7 +640,7 @@ case "$COMMAND" in
else
destination="Apple TV 1080p"
fi
xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
xctest "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination'"
exit $?
;;
@@ -701,17 +653,17 @@ case "$COMMAND" in
if [[ "$CONFIGURATION" == "Debug" ]]; then
COVERAGE_PARAMS="GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES"
fi
xc "-scheme Realm -configuration $CONFIGURATION test $COVERAGE_PARAMS"
xctest "-scheme Realm -configuration $CONFIGURATION $COVERAGE_PARAMS"
exit 0
;;
"test-osx-swift")
xc "-scheme RealmSwift -configuration $CONFIGURATION test"
xctest "-scheme RealmSwift -configuration $CONFIGURATION"
exit 0
;;
"test-osx-object-server")
xc "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx test"
xctest "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx"
exit 0
;;
@@ -849,9 +801,7 @@ case "$COMMAND" in
;;
"verify-osx-object-server")
sh build.sh download-object-server
sh build.sh test-osx-object-server
sh build.sh reset-object-server
exit 0
;;
@@ -1079,6 +1029,8 @@ EOM
"ci-pr")
mkdir -p build/reports
export REALM_DISABLE_ANALYTICS=1
export REALM_DISABLE_UPDATE_CHECKER=1
# FIXME: Re-enable once CI can properly unlock the keychain
export REALM_DISABLE_METADATA_ENCRYPTION=1
@@ -1098,6 +1050,9 @@ EOM
export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO REALM_PREFIX_HEADER=Realm/RLMPrefix.h'
sh build.sh prelaunch-simulator
source $(brew --prefix nvm)/nvm.sh
export REALM_NODE_PATH="$(nvm which 8)"
# Reset CoreSimulator.log
mkdir -p ~/Library/Logs/CoreSimulator
echo > ~/Library/Logs/CoreSimulator/CoreSimulator.log

View File

@@ -1,6 +1,6 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRAuth_VERSION=5.0.0 FIRAuth_MINOR_VERSION=5.0
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRAuth_VERSION=5.0.1 FIRAuth_MINOR_VERSION=5.0
OTHER_LDFLAGS = -framework "Security"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@@ -1,7 +1,7 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_CFLAGS = $(inherited) -fno-autolink -DFIRCore_VERSION=5.0.2 -DFirebase_VERSION=5.1.0
OTHER_CFLAGS = $(inherited) -fno-autolink -DFIRCore_VERSION=5.0.3 -DFirebase_VERSION=5.2.0
OTHER_LDFLAGS = -framework "Foundation" -framework "SystemConfiguration"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

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

View File

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

View File

@@ -35,13 +35,13 @@
C5B75C2120BE350600EB850D /* MeshifyFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B75C2020BE350600EB850D /* MeshifyFunctions.swift */; };
C5B75C2320BEFE3500EB850D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C5B75C2220BEFE3500EB850D /* GoogleService-Info.plist */; };
C5B75C4920C0BD8900EB850D /* MeshifyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B75C4820C0BD8900EB850D /* MeshifyValue.swift */; };
C5D24FD120C869F400262BE8 /* AdminViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD020C869F400262BE8 /* AdminViewController.swift */; };
C5D24FD120C869F400262BE8 /* CompanyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD020C869F400262BE8 /* CompanyViewController.swift */; };
C5D24FD320C86A6000262BE8 /* PillUILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD220C86A6000262BE8 /* PillUILabel.swift */; };
C5D24FD520C86CD900262BE8 /* AdminCompanyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD420C86CD900262BE8 /* AdminCompanyCell.swift */; };
C5D24FD720C86DBB00262BE8 /* Company.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD620C86DBB00262BE8 /* Company.swift */; };
C5D24FD920C87DBA00262BE8 /* NavigationMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FD820C87DBA00262BE8 /* NavigationMenuController.swift */; };
C5D24FDB20C88C3E00262BE8 /* MainNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FDA20C88C3E00262BE8 /* MainNavController.swift */; };
C5D24FDD20C891B600262BE8 /* CompanyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FDC20C891B600262BE8 /* CompanyViewController.swift */; };
C5D24FDD20C891B600262BE8 /* CompanyDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FDC20C891B600262BE8 /* CompanyDetailViewController.swift */; };
C5D24FDF20C895C500262BE8 /* Migrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5D24FDE20C895C500262BE8 /* Migrations.swift */; };
/* End PBXBuildFile section */
@@ -77,13 +77,13 @@
C5B75C2020BE350600EB850D /* MeshifyFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshifyFunctions.swift; sourceTree = "<group>"; };
C5B75C2220BEFE3500EB850D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
C5B75C4820C0BD8900EB850D /* MeshifyValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshifyValue.swift; sourceTree = "<group>"; };
C5D24FD020C869F400262BE8 /* AdminViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminViewController.swift; sourceTree = "<group>"; };
C5D24FD020C869F400262BE8 /* CompanyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyViewController.swift; sourceTree = "<group>"; };
C5D24FD220C86A6000262BE8 /* PillUILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillUILabel.swift; sourceTree = "<group>"; };
C5D24FD420C86CD900262BE8 /* AdminCompanyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminCompanyCell.swift; sourceTree = "<group>"; };
C5D24FD620C86DBB00262BE8 /* Company.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Company.swift; sourceTree = "<group>"; };
C5D24FD820C87DBA00262BE8 /* NavigationMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationMenuController.swift; sourceTree = "<group>"; };
C5D24FDA20C88C3E00262BE8 /* MainNavController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavController.swift; sourceTree = "<group>"; };
C5D24FDC20C891B600262BE8 /* CompanyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyViewController.swift; sourceTree = "<group>"; };
C5D24FDC20C891B600262BE8 /* CompanyDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyDetailViewController.swift; sourceTree = "<group>"; };
C5D24FDE20C895C500262BE8 /* Migrations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Migrations.swift; sourceTree = "<group>"; };
D7E8B9ABE8755B533A815A1B /* Pods_pocloud.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_pocloud.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -195,8 +195,8 @@
C53407DF20B7462C0016CFAB /* DeviceDetailViewController.swift */,
C58C142920B86C1C001BE9DA /* ChannelDetailViewController.swift */,
C58C142D20BDD6BA001BE9DA /* HistoryGraphViewController.swift */,
C5D24FD020C869F400262BE8 /* AdminViewController.swift */,
C5D24FDC20C891B600262BE8 /* CompanyViewController.swift */,
C5D24FD020C869F400262BE8 /* CompanyViewController.swift */,
C5D24FDC20C891B600262BE8 /* CompanyDetailViewController.swift */,
);
path = Controller;
sourceTree = "<group>";
@@ -372,7 +372,7 @@
C5D24FDF20C895C500262BE8 /* Migrations.swift in Sources */,
C5D24FD720C86DBB00262BE8 /* Company.swift in Sources */,
C53407E820B76B7C0016CFAB /* GatewayAnnotation.swift in Sources */,
C5D24FDD20C891B600262BE8 /* CompanyViewController.swift in Sources */,
C5D24FDD20C891B600262BE8 /* CompanyDetailViewController.swift in Sources */,
C53407E420B74B6F0016CFAB /* ChanValue.swift in Sources */,
C53407D820B72AE80016CFAB /* Gateway.swift in Sources */,
C5589C0420B4AA9600639393 /* User.swift in Sources */,
@@ -385,7 +385,7 @@
C53407E620B753940016CFAB /* DeviceDetailChannelCell.swift in Sources */,
C53407DA20B7328C0016CFAB /* DeviceType.swift in Sources */,
C5B75C4920C0BD8900EB850D /* MeshifyValue.swift in Sources */,
C5D24FD120C869F400262BE8 /* AdminViewController.swift in Sources */,
C5D24FD120C869F400262BE8 /* CompanyViewController.swift in Sources */,
C58C142A20B86C1C001BE9DA /* ChannelDetailViewController.swift in Sources */,
C5D24FD320C86A6000262BE8 /* PillUILabel.swift in Sources */,
);

View File

@@ -1,93 +0,0 @@
//
// AdminViewController.swift
// pocloud
//
// Created by Patrick McDonagh on 6/6/18.
// Copyright © 2018 patrickjmcd. All rights reserved.
//
import UIKit
import RealmSwift
import PromiseKit
class AdminViewController: UITableViewController {
let realm = try! Realm()
let baseURL = (UIApplication.shared.delegate as! AppDelegate).baseURL
let user = (UIApplication.shared.delegate as! AppDelegate).user
let appAuth = AppAuth()
var companiesWithGateways : Results<Company>?
var companiesNoGateways : Results<Company>?
var selectedCompany : Company?
override func viewDidLoad() {
super.viewDidLoad()
// Get company data from Realm
companiesWithGateways = realm.objects(Company.self).filter("gateways.@count > 0")
companiesNoGateways = realm.objects(Company.self).filter("gateways.@count == 0")
if (companiesWithGateways!.count + companiesNoGateways!.count) == 0{
firstly {
getAllMeshifyData(baseURL: self.baseURL, authToken: self.user!.authToken)
}.done {
self.tableView.reloadData()
}.catch { error in
print("Error getting all meshify data in AdminViewController: \(error)")
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return companiesWithGateways?.count ?? 0
} else {
return companiesNoGateways?.count ?? 0
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Companies with Devices"
} else {
return "Companies with NO Devices"
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "companyCell", for: indexPath) as! AdminCompanyCell
if indexPath.section == 0 {
cell.companyNameLabel.text = companiesWithGateways![indexPath.row].name
cell.companyCountLabel.text = String(companiesWithGateways![indexPath.row].gateways.count)
} else {
cell.companyNameLabel.text = companiesNoGateways![indexPath.row].name
cell.companyCountLabel.isHidden = true
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
selectedCompany = companiesWithGateways![indexPath.row]
performSegue(withIdentifier: "openCompanyDetailView", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openCompanyDetailView" {
let targetVC = segue.destination as! CompanyViewController
targetVC.thisCompany = selectedCompany
}
}
}

View File

@@ -0,0 +1,102 @@
//
// CompanyViewController.swift
// pocloud
//
// Created by Patrick McDonagh on 6/6/18.
// Copyright © 2018 patrickjmcd. All rights reserved.
//
import UIKit
import MapKit
class CompanyDetailViewController: UIViewController, MKMapViewDelegate, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var tableView: UITableView!
var thisCompany : Company?
var selectedGateway : Gateway?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
mapView.delegate = self
title = thisCompany?.name
addMapDot()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let company = thisCompany{
return company.gateways.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "companyGatewayCell", for: indexPath)
cell.textLabel?.text = thisCompany?.gateways[indexPath.row].name
return cell
}
func addMapDot(){
let coord = CLLocationCoordinate2D(latitude: (thisCompany?.address!.lat)!, longitude: (thisCompany?.address!.long)!)
let gatewayAnnotation = GatewayAnnotation(coordinate: coord, title: (thisCompany?.name)!, subtitle: (thisCompany?.address?.streetAddress)!, gateway: nil)
mapView.addAnnotation(gatewayAnnotation)
let regionRadius: CLLocationDistance = 1000
let coordinateRegion = MKCoordinateRegionMakeWithDistance(coord, regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "marker"
if annotation.isKind(of: GatewayAnnotation.self) {
var view: MKMarkerAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
return view
} else {
return nil
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedGateway = thisCompany?.gateways[indexPath.row]
performSegue(withIdentifier: "openGatewayDetail", sender: self)
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! GatewayAnnotation
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openGatewayDetail" {
let destinationVC = segue.destination as! MapDetailViewController
destinationVC.gateway = selectedGateway
}
}
}

View File

@@ -7,96 +7,87 @@
//
import UIKit
import MapKit
import RealmSwift
import PromiseKit
class CompanyViewController: UIViewController, MKMapViewDelegate, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var tableView: UITableView!
class CompanyViewController: UITableViewController {
let realm = try! Realm()
let baseURL = (UIApplication.shared.delegate as! AppDelegate).baseURL
let user = (UIApplication.shared.delegate as! AppDelegate).user
let appAuth = AppAuth()
var thisCompany : Company?
var selectedGateway : Gateway?
var companiesWithGateways : Results<Company>?
var companiesNoGateways : Results<Company>?
var selectedCompany : Company?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
mapView.delegate = self
title = thisCompany?.name
// Get company data from Realm
companiesWithGateways = realm.objects(Company.self).filter("gateways.@count > 0")
companiesNoGateways = realm.objects(Company.self).filter("gateways.@count == 0")
addMapDot()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let company = thisCompany{
return company.gateways.count
} else {
return 0
if (companiesWithGateways!.count + companiesNoGateways!.count) == 0{
firstly {
getAllMeshifyData(baseURL: self.baseURL, authToken: self.user!.authToken)
}.done {
self.tableView.reloadData()
}.catch { error in
print("Error getting all meshify data in CompanyViewController: \(error)")
}
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return companiesWithGateways?.count ?? 0
} else {
return companiesNoGateways?.count ?? 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "companyGatewayCell", for: indexPath)
cell.textLabel?.text = thisCompany?.gateways[indexPath.row].name
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Companies with Devices"
} else {
return "Companies with NO Devices"
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "companyCell", for: indexPath) as! AdminCompanyCell
if indexPath.section == 0 {
cell.companyNameLabel.text = companiesWithGateways![indexPath.row].name
cell.companyCountLabel.text = String(companiesWithGateways![indexPath.row].gateways.count)
} else {
cell.companyNameLabel.text = companiesNoGateways![indexPath.row].name
cell.companyCountLabel.isHidden = true
cell.accessoryType = .none
}
return cell
}
func addMapDot(){
let coord = CLLocationCoordinate2D(latitude: (thisCompany?.address!.lat)!, longitude: (thisCompany?.address!.long)!)
let gatewayAnnotation = GatewayAnnotation(coordinate: coord, title: (thisCompany?.name)!, subtitle: (thisCompany?.address?.streetAddress)!, gateway: nil)
mapView.addAnnotation(gatewayAnnotation)
let regionRadius: CLLocationDistance = 1000
let coordinateRegion = MKCoordinateRegionMakeWithDistance(coord, regionRadius, regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "marker"
if annotation.isKind(of: GatewayAnnotation.self) {
var view: MKMarkerAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
return view
} else {
return nil
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
selectedCompany = companiesWithGateways![indexPath.row]
performSegue(withIdentifier: "openCompanyDetailView", sender: self)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedGateway = thisCompany?.gateways[indexPath.row]
performSegue(withIdentifier: "openGatewayDetail", sender: self)
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! GatewayAnnotation
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openGatewayDetail" {
let destinationVC = segue.destination as! MapDetailViewController
destinationVC.gateway = selectedGateway
if segue.identifier == "openCompanyDetailView" {
let targetVC = segue.destination as! CompanyDetailViewController
targetVC.thisCompany = selectedCompany
}
}
}

View File

@@ -33,6 +33,10 @@ class DeviceDetailViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableViewAutomaticDimension
ref = Database.database().reference()
let macAddress = String((thisDevice?.macAddress.replacingOccurrences(of: ":", with: "").uppercased().dropLast(4))!)
let deviceTypeName = (thisDevice?.parentDeviceType.first?.name)!
@@ -112,9 +116,18 @@ class DeviceDetailViewController: UITableViewController {
let value = snapshot.value as! NSDictionary
let chanVal = MeshifyValue()
chanVal.name = (value["name"] as? String)!
chanVal.timestamp = Int((value["timestamp"] as? String)!)!
chanVal.value = (value["value"] as? String)!
if let name = value["name"] as? String {
chanVal.name = name
}
if let timestamp = value["timestamp"] as? String {
chanVal.timestamp = Int(Double(timestamp)!)
}
if let readValue = value["value"] as? String {
chanVal.value = readValue
}
let prevChanValue = self.values[chanVal.name]?.value
if prevChanValue != chanVal.value {
self.changedChannelNames.append(chanVal.name)

View File

@@ -28,6 +28,7 @@ class DeviceListViewController: UITableViewController {
var deviceTypes : Results<DeviceType>?
let baseURL = (UIApplication.shared.delegate as! AppDelegate).baseURL
let deviceTypeFilter = NSPredicate(format: "devices.@count > 0 AND NOT name IN %@", ["gen", "mainHP", "M1"])
let ignoreDeviceTypes = ["M1", "Gateway"]
var selectedDevice: Device?
@@ -40,6 +41,8 @@ class DeviceListViewController: UITableViewController {
searchBar.delegate = self
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Log Out", style: .plain, target: self, action: #selector(self.logOut))
SVProgressHUD.show()
@@ -82,6 +85,24 @@ class DeviceListViewController: UITableViewController {
}
func getUniqueDeviceTypeNames() -> [String]{
var deviceTypeNames : [String] = [String]()
if let devicesList = devices {
for d in devicesList {
if let deviceParent = d.parentDeviceType.first {
deviceTypeNames.append(deviceParent.vanityName)
}
}
}
return deviceTypeNames.reduce([], { initialValue, collectionElement in
initialValue.contains(collectionElement) ? initialValue : initialValue + [collectionElement]
})
.filter({ (name) -> Bool in
!self.ignoreDeviceTypes.contains(name)
})
}
@objc func refresh() {
SVProgressHUD.show()
@@ -101,18 +122,17 @@ class DeviceListViewController: UITableViewController {
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
return deviceTypesWithDevices?.count ?? 0
return getUniqueDeviceTypeNames().count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
return deviceTypesWithDevices?[section].devices.count ?? 0
let uniqueDeviceTypes = getUniqueDeviceTypeNames()
let deviceTypesWithDevices = devices?.filter("ANY parentDeviceType.vanityName == %@", uniqueDeviceTypes[section])
return deviceTypesWithDevices?.count ?? 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
return ((deviceTypesWithDevices?.count)! > 0) ? deviceTypesWithDevices?[section].vanityName : "Unknown"
return getUniqueDeviceTypeNames()[section]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@@ -124,21 +144,24 @@ class DeviceListViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "deviceListCell", for: indexPath)
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
let uniqueDeviceTypes = getUniqueDeviceTypeNames()
let deviceTypesWithDevices = devices?.filter("ANY parentDeviceType.vanityName == %@", uniqueDeviceTypes[indexPath.section]).sorted(byKeyPath: "vanityName")
if (deviceTypesWithDevices?.count)! > 0 {
cell.textLabel?.text = deviceTypesWithDevices?[indexPath.section].devices[indexPath.row].vanityName
cell.textLabel?.text = deviceTypesWithDevices?[indexPath.row].vanityName
cell.accessoryType = .disclosureIndicator
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
if let thisSection = deviceTypesWithDevices?[indexPath.section] {
selectedDevice = thisSection.devices[indexPath.row]
// print(selectedDevice)
let uniqueDeviceTypes = getUniqueDeviceTypeNames()
let deviceTypesWithDevices = devices?.filter("ANY parentDeviceType.vanityName == %@", uniqueDeviceTypes[indexPath.section]).sorted(byKeyPath: "vanityName")
// let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
// if let thisSection = deviceTypesWithDevices?[indexPath.section] {
// selectedDevice = thisSection.devices[indexPath.row]
selectedDevice = deviceTypesWithDevices?[indexPath.row]
performSegue(withIdentifier: "openDeviceDetailView", sender: self)
}
// }
}
@@ -166,16 +189,16 @@ extension DeviceListViewController : UISearchBarDelegate {
if searchBar.text?.count == 0 {
firstly {
self.loadRealmData()
}.then{ _ in
self.loadJSONData()
}.done { _ in
self.tableView.reloadData()
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
}.catch { error in
print("Error in getting data in DeviceListViewController: \(error)")
}.then{ _ in
self.loadJSONData()
}.done { _ in
self.tableView.reloadData()
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
}.catch { error in
print("Error in getting data in DeviceListViewController: \(error)")
}
}
}

View File

@@ -13,6 +13,9 @@ class NavigationMenuController: UITableViewController {
let appAuth = AppAuth()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableViewAutomaticDimension
}

View File

@@ -17,7 +17,9 @@
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>7</string>
<string>9-rc1</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationUsageDescription</key>

View File

@@ -119,10 +119,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Map View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="K2m-on-u83">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="WkR-Ec-bck"/>
</constraints>
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -131,7 +128,7 @@
<constraints>
<constraint firstAttribute="trailing" secondItem="K2m-on-u83" secondAttribute="trailing" constant="16" id="itX-5w-OBv"/>
<constraint firstItem="K2m-on-u83" firstAttribute="leading" secondItem="Zft-87-4vv" secondAttribute="leading" constant="16" id="ov7-hP-RzB"/>
<constraint firstAttribute="bottom" secondItem="K2m-on-u83" secondAttribute="bottom" id="uWk-am-QXU"/>
<constraint firstAttribute="bottom" relation="lessThanOrEqual" secondItem="K2m-on-u83" secondAttribute="bottom" id="uWk-am-QXU"/>
<constraint firstItem="K2m-on-u83" firstAttribute="top" secondItem="Zft-87-4vv" secondAttribute="top" id="v3E-fu-Z3h"/>
</constraints>
</tableViewCellContentView>
@@ -147,10 +144,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="List View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O2Q-ia-Znc">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="OUu-Sl-qO8"/>
</constraints>
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -159,7 +153,7 @@
<constraints>
<constraint firstItem="O2Q-ia-Znc" firstAttribute="top" secondItem="cHZ-P5-57P" secondAttribute="top" id="6b0-uT-Dyz"/>
<constraint firstAttribute="trailing" secondItem="O2Q-ia-Znc" secondAttribute="trailing" constant="16" id="X35-2D-Vmj"/>
<constraint firstAttribute="bottom" secondItem="O2Q-ia-Znc" secondAttribute="bottom" id="gii-t8-r0I"/>
<constraint firstAttribute="bottom" relation="lessThanOrEqual" secondItem="O2Q-ia-Znc" secondAttribute="bottom" id="gii-t8-r0I"/>
<constraint firstItem="O2Q-ia-Znc" firstAttribute="leading" secondItem="cHZ-P5-57P" secondAttribute="leading" constant="16" id="tNz-Jj-6c9"/>
</constraints>
</tableViewCellContentView>
@@ -175,10 +169,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Company View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PBz-lV-jm5">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="W4g-rH-3kB"/>
</constraints>
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -187,7 +178,7 @@
<constraints>
<constraint firstItem="PBz-lV-jm5" firstAttribute="top" secondItem="uOu-qn-Bsk" secondAttribute="top" id="eFS-1d-xye"/>
<constraint firstItem="PBz-lV-jm5" firstAttribute="leading" secondItem="uOu-qn-Bsk" secondAttribute="leading" constant="16" id="uJV-EL-4pQ"/>
<constraint firstAttribute="bottom" secondItem="PBz-lV-jm5" secondAttribute="bottom" id="y0g-od-TRr"/>
<constraint firstAttribute="bottom" relation="lessThanOrEqual" secondItem="PBz-lV-jm5" secondAttribute="bottom" id="y0g-od-TRr"/>
<constraint firstAttribute="trailing" secondItem="PBz-lV-jm5" secondAttribute="trailing" constant="16" id="y7M-Vh-JFu"/>
</constraints>
</tableViewCellContentView>
@@ -218,7 +209,7 @@
<!--Admin View-->
<scene sceneID="luK-QG-5Bk">
<objects>
<tableViewController title="Admin View" id="gRf-OH-LM3" customClass="AdminViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController title="Admin View" id="gRf-OH-LM3" customClass="CompanyViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="pRZ-8h-aCt">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -241,7 +232,7 @@
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cnt" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="16b-fu-GPA" customClass="PillUILabel" customModule="pocloud" customModuleProvider="target">
<rect key="frame" x="278.5" y="8.5" width="46.5" height="26.5"/>
<rect key="frame" x="278.5" y="11.5" width="46.5" height="20.5"/>
<color key="backgroundColor" red="0.0" green="0.32852089410000002" blue="0.57488495110000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -297,7 +288,7 @@
<!--Title-->
<scene sceneID="zaO-o6-5qR">
<objects>
<viewController id="1Oj-LF-SeF" customClass="CompanyViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="1Oj-LF-SeF" customClass="CompanyDetailViewController" customModule="pocloud" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="GRC-ek-5fe">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -412,13 +403,13 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="xug-gE-FQz">
<rect key="frame" x="0.0" y="65" width="375" height="200"/>
<rect key="frame" x="0.0" y="109" width="375" height="200"/>
<constraints>
<constraint firstAttribute="height" constant="200" id="tu9-2Y-yHN"/>
</constraints>
</mapView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GatewayName" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="izu-lT-xWY">
<rect key="frame" x="8" y="20" width="359" height="45"/>
<rect key="frame" x="8" y="64" width="359" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="jbO-lj-xVg"/>
</constraints>
@@ -427,7 +418,7 @@
<nil key="highlightedColor"/>
</label>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="tRH-fh-elp">
<rect key="frame" x="0.0" y="275" width="375" height="392"/>
<rect key="frame" x="0.0" y="319" width="375" height="304"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="dataViewCell" rowHeight="60" id="5Sz-TR-mVj" customClass="MapDetailDeviceCell" customModule="pocloud" customModuleProvider="target">
@@ -520,37 +511,33 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Um-Bu-9sz">
<rect key="frame" x="16" y="0.0" width="343" height="44"/>
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bkh-Zy-Gmc">
<rect key="frame" x="0.0" y="0.0" width="172" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="172" id="RRS-6S-a7K"/>
</constraints>
<rect key="frame" x="0.0" y="0.0" width="171.5" height="43.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EXp-nl-Xlu">
<rect key="frame" x="172" y="0.0" width="171" height="44"/>
<rect key="frame" x="171.5" y="0.0" width="171.5" height="43.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="EXp-nl-Xlu" firstAttribute="top" secondItem="8Um-Bu-9sz" secondAttribute="top" id="9Fj-fN-yuC"/>
<constraint firstAttribute="bottom" secondItem="Bkh-Zy-Gmc" secondAttribute="bottom" id="GU8-9u-iwe"/>
<constraint firstAttribute="bottom" secondItem="EXp-nl-Xlu" secondAttribute="bottom" id="cd8-j2-5wB"/>
<constraint firstItem="Bkh-Zy-Gmc" firstAttribute="top" secondItem="8Um-Bu-9sz" secondAttribute="top" id="hsq-tb-wgL"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="pNb-PY-jok"/>
<constraint firstItem="EXp-nl-Xlu" firstAttribute="width" secondItem="Bkh-Zy-Gmc" secondAttribute="width" id="WFF-y6-e24"/>
<constraint firstAttribute="bottom" relation="lessThanOrEqual" secondItem="EXp-nl-Xlu" secondAttribute="bottom" id="bXa-Ey-uWU"/>
<constraint firstItem="EXp-nl-Xlu" firstAttribute="top" secondItem="8Um-Bu-9sz" secondAttribute="top" id="ub8-1t-95f"/>
</constraints>
</stackView>
</subviews>
<constraints>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="5QW-f5-FX9"/>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="leading" secondItem="u2w-Yy-Exm" secondAttribute="leading" constant="16" id="DTN-Mt-MOn"/>
<constraint firstAttribute="trailing" secondItem="8Um-Bu-9sz" secondAttribute="trailing" constant="16" id="Xho-aE-Uhw"/>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="top" secondItem="u2w-Yy-Exm" secondAttribute="top" id="4RE-WM-Oiu"/>
<constraint firstAttribute="bottom" relation="lessThanOrEqual" secondItem="8Um-Bu-9sz" secondAttribute="bottom" id="5dq-rd-hVI"/>
<constraint firstAttribute="trailing" secondItem="8Um-Bu-9sz" secondAttribute="trailing" constant="16" id="HoX-Eq-hZq"/>
<constraint firstItem="8Um-Bu-9sz" firstAttribute="leading" secondItem="u2w-Yy-Exm" secondAttribute="leading" constant="16" id="m6D-Tr-TBa"/>
</constraints>
</tableViewCellContentView>
<connections>
@@ -752,7 +739,10 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mlm-Lp-0uP">
<rect key="frame" x="287" y="300" width="80" height="30"/>
<rect key="frame" x="277" y="300" width="90" height="30"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="90" id="DFj-0N-UCR"/>
</constraints>
<state key="normal" title="View Graph"/>
<connections>
<segue destination="sJg-Af-FtE" kind="show" identifier="openHistoryGraph" id="WaU-6H-sQq"/>
@@ -770,6 +760,7 @@
<constraint firstAttribute="trailingMargin" secondItem="qKa-BE-rae" secondAttribute="trailing" id="N8l-nU-Wfp"/>
<constraint firstItem="UQ2-66-Iuf" firstAttribute="top" secondItem="Q4o-w1-JdP" secondAttribute="bottom" constant="8" id="OMk-cW-txI"/>
<constraint firstItem="M2H-Lw-iw3" firstAttribute="trailing" secondItem="Eh7-ay-CB3" secondAttribute="trailing" id="Owt-WR-jEW"/>
<constraint firstItem="Mlm-Lp-0uP" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="s1w-I3-Ih9" secondAttribute="leading" constant="20" symbolic="YES" id="T6q-nx-3Al"/>
<constraint firstItem="M2H-Lw-iw3" firstAttribute="top" secondItem="7j2-Cq-PoN" secondAttribute="bottom" id="Wre-RL-KKv"/>
<constraint firstItem="hri-QF-EMo" firstAttribute="top" secondItem="UQ2-66-Iuf" secondAttribute="bottom" id="X8X-hY-04u"/>
<constraint firstItem="M2H-Lw-iw3" firstAttribute="leading" secondItem="Eh7-ay-CB3" secondAttribute="leading" id="YRv-Ke-HWg"/>
@@ -812,7 +803,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="qLG-JD-OG3" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3004" y="531"/>
<point key="canvasLocation" x="3004" y="530.28485757121439"/>
</scene>
<!--History Graph View Controller-->
<scene sceneID="WkV-VI-zTb">
@@ -943,8 +934,8 @@
</resources>
<inferredMetricsTieBreakers>
<segue reference="9Om-lw-Jgn"/>
<segue reference="wUb-ND-zk7"/>
<segue reference="DQm-0d-brY"/>
<segue reference="4nz-VA-pdT"/>
<segue reference="bJo-JM-CaN"/>
<segue reference="LUR-lb-5qK"/>
</inferredMetricsTieBreakers>
</document>