mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-06-16 21:50:57 +00:00
Avoid storing images in the database
This commit is contained in:
@@ -28,7 +28,6 @@
|
|||||||
- (NSArray*) getHosts;
|
- (NSArray*) getHosts;
|
||||||
- (void) updateHost:(TemporaryHost*)host;
|
- (void) updateHost:(TemporaryHost*)host;
|
||||||
- (void) updateAppsForExistingHost:(TemporaryHost *)host;
|
- (void) updateAppsForExistingHost:(TemporaryHost *)host;
|
||||||
- (void) updateIconForExistingApp:(TemporaryApp*)app;
|
|
||||||
- (void) removeHost:(TemporaryHost*)host;
|
- (void) removeHost:(TemporaryHost*)host;
|
||||||
- (void) removeApp:(TemporaryApp*)app;
|
- (void) removeApp:(TemporaryApp*)app;
|
||||||
|
|
||||||
|
|||||||
@@ -128,20 +128,6 @@
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) updateIconForExistingApp:(TemporaryApp*)app {
|
|
||||||
[_managedObjectContext performBlockAndWait:^{
|
|
||||||
App* parentApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
|
|
||||||
if (parentApp == nil) {
|
|
||||||
// The app must exist to be updated
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parentApp.image = app.image;
|
|
||||||
|
|
||||||
[self saveData];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (TemporarySettings*) getSettings {
|
- (TemporarySettings*) getSettings {
|
||||||
__block TemporarySettings *tempSettings;
|
__block TemporarySettings *tempSettings;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
@interface TemporaryApp : NSObject
|
@interface TemporaryApp : NSObject
|
||||||
|
|
||||||
@property (nullable, nonatomic, retain) NSString *id;
|
@property (nullable, nonatomic, retain) NSString *id;
|
||||||
@property (nullable, nonatomic, retain) NSData *image;
|
|
||||||
@property (nullable, nonatomic, retain) NSString *name;
|
@property (nullable, nonatomic, retain) NSString *name;
|
||||||
@property (nonatomic) BOOL hdrSupported;
|
@property (nonatomic) BOOL hdrSupported;
|
||||||
@property (nullable, nonatomic, retain) TemporaryHost *host;
|
@property (nullable, nonatomic, retain) TemporaryHost *host;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
self = [self init];
|
self = [self init];
|
||||||
|
|
||||||
self.id = app.id;
|
self.id = app.id;
|
||||||
self.image = app.image;
|
|
||||||
self.name = app.name;
|
self.name = app.name;
|
||||||
self.hdrSupported = app.hdrSupported;
|
self.hdrSupported = app.hdrSupported;
|
||||||
self.host = tempHost;
|
self.host = tempHost;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>Moonlight v1.1.xcdatamodel</string>
|
<string>Moonlight v1.2.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14135" systemVersion="17G65" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||||
|
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
|
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
|
||||||
|
<element name="Host" positionX="0" positionY="0" width="128" height="165"/>
|
||||||
|
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
||||||
@@ -21,5 +21,6 @@
|
|||||||
- (id) initWithCallback:(id<AppAssetCallback>)callback;
|
- (id) initWithCallback:(id<AppAssetCallback>)callback;
|
||||||
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
|
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
|
||||||
- (void) stopRetrieving;
|
- (void) stopRetrieving;
|
||||||
|
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,6 +19,22 @@
|
|||||||
|
|
||||||
static const int MAX_REQUEST_COUNT = 4;
|
static const int MAX_REQUEST_COUNT = 4;
|
||||||
|
|
||||||
|
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app {
|
||||||
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||||
|
NSString *filePath = [paths objectAtIndex:0];
|
||||||
|
|
||||||
|
// Keep app assets separate by host UUID
|
||||||
|
filePath = [filePath stringByAppendingPathComponent:app.host.uuid];
|
||||||
|
|
||||||
|
// Use the app ID as the file name
|
||||||
|
filePath = [filePath stringByAppendingPathComponent:app.id];
|
||||||
|
|
||||||
|
// Add a png extension
|
||||||
|
filePath = [filePath stringByAppendingPathExtension:@"png"];
|
||||||
|
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
- (id) initWithCallback:(id<AppAssetCallback>)callback {
|
- (id) initWithCallback:(id<AppAssetCallback>)callback {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
@@ -30,7 +46,7 @@ static const int MAX_REQUEST_COUNT = 4;
|
|||||||
|
|
||||||
- (void) retrieveAssetsFromHost:(TemporaryHost*)host {
|
- (void) retrieveAssetsFromHost:(TemporaryHost*)host {
|
||||||
for (TemporaryApp* app in host.appList) {
|
for (TemporaryApp* app in host.appList) {
|
||||||
if (app.image == nil) {
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[AppAssetManager boxArtPathForApp:app]]) {
|
||||||
AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init];
|
AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init];
|
||||||
retriever.app = app;
|
retriever.app = app;
|
||||||
retriever.host = host;
|
retriever.host = host;
|
||||||
|
|||||||
@@ -18,25 +18,24 @@ static const double RETRY_DELAY = 2; // seconds
|
|||||||
static const int MAX_ATTEMPTS = 5;
|
static const int MAX_ATTEMPTS = 5;
|
||||||
|
|
||||||
- (void) main {
|
- (void) main {
|
||||||
|
|
||||||
OSImage* appImage = nil;
|
|
||||||
|
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
while (![self isCancelled] && appImage == nil && attempts++ < MAX_ATTEMPTS) {
|
while (![self isCancelled] && attempts++ < MAX_ATTEMPTS) {
|
||||||
|
|
||||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]];
|
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]];
|
||||||
AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init];
|
AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init];
|
||||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]];
|
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]];
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
appImage = [UIImage imageWithData:appAssetResp.data];
|
if (appAssetResp.data != nil) {
|
||||||
self.app.image = UIImagePNGRepresentation(appImage);
|
NSString* boxArtPath = [AppAssetManager boxArtPathForApp:self.app];
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtPath:[boxArtPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||||
|
[appAssetResp.data writeToFile:boxArtPath atomically:NO];
|
||||||
|
break;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
if (![self isCancelled] && appImage == nil) {
|
if (![self isCancelled]) {
|
||||||
[NSThread sleepForTimeInterval:RETRY_DELAY];
|
[NSThread sleepForTimeInterval:RETRY_DELAY];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "UIAppView.h"
|
#import "UIAppView.h"
|
||||||
|
#import "AppAssetManager.h"
|
||||||
|
|
||||||
@implementation UIAppView {
|
@implementation UIAppView {
|
||||||
TemporaryApp* _app;
|
TemporaryApp* _app;
|
||||||
@@ -74,19 +75,20 @@ static UIImage* noImage;
|
|||||||
[_appOverlay setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/6)];
|
[_appOverlay setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/6)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Improve no-app image detection
|
|
||||||
BOOL noAppImage = false;
|
BOOL noAppImage = false;
|
||||||
|
|
||||||
if (_app.image != nil) {
|
// First check the memory cache
|
||||||
// Load the decoded image from the cache
|
UIImage* appImage = [_artCache objectForKey:_app];
|
||||||
UIImage* appImage = [_artCache objectForKey:_app];
|
if (appImage == nil) {
|
||||||
if (appImage == nil) {
|
// Next try to load from the on disk cache
|
||||||
// Not cached; we have to decode this now
|
appImage = [UIImage imageWithContentsOfFile:[AppAssetManager boxArtPathForApp:_app]];
|
||||||
appImage = [UIImage imageWithData:_app.image];
|
if (appImage != nil) {
|
||||||
[_artCache setObject:appImage forKey:_app];
|
[_artCache setObject:appImage forKey:_app];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (appImage != nil) {
|
||||||
// This size of image might be blank image received from GameStream.
|
// This size of image might be blank image received from GameStream.
|
||||||
|
// TODO: Improve no-app image detection
|
||||||
if (!(appImage.size.width == 130.f && appImage.size.height == 180.f) && // GFE 2.0
|
if (!(appImage.size.width == 130.f && appImage.size.height == 180.f) && // GFE 2.0
|
||||||
!(appImage.size.width == 628.f && appImage.size.height == 888.f)) { // GFE 3.0
|
!(appImage.size.width == 628.f && appImage.size.height == 888.f)) { // GFE 3.0
|
||||||
_appButton.frame = CGRectMake(0, 0, appImage.size.width / 2, appImage.size.height / 2);
|
_appButton.frame = CGRectMake(0, 0, appImage.size.width / 2, appImage.size.height / 2);
|
||||||
|
|||||||
@@ -263,9 +263,6 @@ static NSMutableSet* hostList;
|
|||||||
// on the main thread
|
// on the main thread
|
||||||
[self updateBoxArtCacheForApp:app];
|
[self updateBoxArtCacheForApp:app];
|
||||||
|
|
||||||
DataManager* dataManager = [[DataManager alloc] init];
|
|
||||||
[dataManager updateIconForExistingApp: app];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.collectionView reloadData];
|
[self.collectionView reloadData];
|
||||||
});
|
});
|
||||||
@@ -926,7 +923,13 @@ static NSMutableSet* hostList;
|
|||||||
+ (UIImage*) loadBoxArtForCaching:(TemporaryApp*)app {
|
+ (UIImage*) loadBoxArtForCaching:(TemporaryApp*)app {
|
||||||
UIImage* boxArt;
|
UIImage* boxArt;
|
||||||
|
|
||||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)app.image, NULL);
|
NSData* imageData = [NSData dataWithContentsOfFile:[AppAssetManager boxArtPathForApp:app]];
|
||||||
|
if (imageData == nil) {
|
||||||
|
// No box art on disk
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
|
||||||
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil);
|
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil);
|
||||||
|
|
||||||
size_t width = CGImageGetWidth(cgImage);
|
size_t width = CGImageGetWidth(cgImage);
|
||||||
@@ -953,11 +956,12 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void) updateBoxArtCacheForApp:(TemporaryApp*)app {
|
- (void) updateBoxArtCacheForApp:(TemporaryApp*)app {
|
||||||
if (app.image == nil) {
|
if ([_boxArtCache objectForKey:app] == nil) {
|
||||||
[_boxArtCache removeObjectForKey:app];
|
UIImage* image = [MainFrameViewController loadBoxArtForCaching:app];
|
||||||
}
|
if (image != nil) {
|
||||||
else if ([_boxArtCache objectForKey:app] == nil) {
|
// Add the image to our cache if it was present
|
||||||
[_boxArtCache setObject:[MainFrameViewController loadBoxArtForCaching:app] forKey:app];
|
[_boxArtCache setObject:image forKey:app];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,7 @@
|
|||||||
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
|
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
|
||||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
|
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
|
||||||
9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.2.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
|
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
|
||||||
9890CF6A203B7EE1006C4B06 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
|
9890CF6A203B7EE1006C4B06 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
|
||||||
98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "moonlight-common/moonlight-common.xcodeproj"; sourceTree = "<group>"; };
|
98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "moonlight-common/moonlight-common.xcodeproj"; sourceTree = "<group>"; };
|
||||||
@@ -1499,6 +1500,7 @@
|
|||||||
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
|
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
|
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */,
|
||||||
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */,
|
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */,
|
||||||
FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */,
|
FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */,
|
||||||
FBB460391B50ACE400F3099C /* Moonlight v1.0.xcdatamodel */,
|
FBB460391B50ACE400F3099C /* Moonlight v1.0.xcdatamodel */,
|
||||||
@@ -1506,7 +1508,7 @@
|
|||||||
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
|
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
|
||||||
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
|
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = 98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */;
|
currentVersion = 986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */;
|
||||||
path = Limelight.xcdatamodeld;
|
path = Limelight.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
|||||||
Reference in New Issue
Block a user