diff --git a/TweetNest.xcodeproj/project.pbxproj b/TweetNest.xcodeproj/project.pbxproj index 02221d4d..e1eb04e1 100644 --- a/TweetNest.xcodeproj/project.pbxproj +++ b/TweetNest.xcodeproj/project.pbxproj @@ -64,7 +64,7 @@ 192F008926FC270B000EC734 /* UserView+AllDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 192F008726FC270B000EC734 /* UserView+AllDataView.swift */; }; 192F008A26FC270B000EC734 /* UserView+AllDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 192F008726FC270B000EC734 /* UserView+AllDataView.swift */; }; 1932FCA626F4A06900837AA9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1932FCA826F4A06900837AA9 /* InfoPlist.strings */; }; - 19365984260978EB00865D40 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Preview Assets.xcassets */; }; + 19365984260978EB00865D40 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Assets.xcassets */; }; 193659AE260978F900865D40 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19933EB925E90ECD006AB4C0 /* MainView.swift */; }; 193659AF260978F900865D40 /* Session+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19933EE125E90F16006AB4C0 /* Session+Preview.swift */; }; 193659B0260978F900865D40 /* TweetNestAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1918DE56260802FE009FD0D8 /* TweetNestAppDelegate.swift */; }; @@ -78,7 +78,7 @@ 19365A6726097B4800865D40 /* TweetNestAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1918DE56260802FE009FD0D8 /* TweetNestAppDelegate.swift */; }; 19365A6826097B4800865D40 /* Session+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19933EE125E90F16006AB4C0 /* Session+Preview.swift */; }; 19365A6926097B4800865D40 /* WebAuthenticationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19933ED325E90EF9006AB4C0 /* WebAuthenticationView.swift */; }; - 19365A6F26097B4A00865D40 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Preview Assets.xcassets */; }; + 19365A6F26097B4A00865D40 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Assets.xcassets */; }; 19365AC926097C1A00865D40 /* TweetNestAppDelegate+NSApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1918DE6A2608032D009FD0D8 /* TweetNestAppDelegate+NSApplicationDelegate.swift */; }; 193AB25D26B6FD1200B18D45 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193AB25C26B6FD1200B18D45 /* URL.swift */; }; 193AFB5C26CA043A008BFF67 /* ManagedAccount+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193AFB5B26CA043A008BFF67 /* ManagedAccount+Preferences.swift */; }; @@ -162,7 +162,7 @@ 199C7E7428364253001D8F85 /* String+TweetNestKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 199C7E7328364253001D8F85 /* String+TweetNestKit.swift */; }; 199CAA5B26CA6C6F000CA732 /* TweetNestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 199CAA5A26CA6C6F000CA732 /* TweetNestError.swift */; }; 199CAA5C26CA6C6F000CA732 /* TweetNestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 199CAA5A26CA6C6F000CA732 /* TweetNestError.swift */; }; - 19A1F1B926F38D4300A28A49 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Preview Assets.xcassets */; }; + 19A1F1B926F38D4300A28A49 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19365983260978EB00865D40 /* Assets.xcassets */; }; 19A6FF1E26D6836000AB7B7E /* TweetNest WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 19A6FF1D26D6836000AB7B7E /* TweetNest WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 19A6FF2B26D6836000AB7B7E /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A6FF2A26D6836000AB7B7E /* ComplicationController.swift */; }; 19A6FF4826D6836100AB7B7E /* TweetNestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A6FF4726D6836100AB7B7E /* TweetNestTests.swift */; }; @@ -266,6 +266,12 @@ 663E96C12838F5D60037413E /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663E96C02838F5D60037413E /* Pasteboard.swift */; }; 663E96C22838F5D60037413E /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663E96C02838F5D60037413E /* Pasteboard.swift */; }; 663E96C32838F5D60037413E /* Pasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663E96C02838F5D60037413E /* Pasteboard.swift */; }; + 6697AF0A283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF09283EE26A00EE816D /* PersistentContainer+Preview.swift */; }; + 6697AF0B283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF09283EE26A00EE816D /* PersistentContainer+Preview.swift */; }; + 6697AF0C283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF09283EE26A00EE816D /* PersistentContainer+Preview.swift */; }; + 6697AF13283EE96C00EE816D /* PreviewManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF12283EE96C00EE816D /* PreviewManifest.swift */; }; + 6697AF14283EE96C00EE816D /* PreviewManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF12283EE96C00EE816D /* PreviewManifest.swift */; }; + 6697AF15283EE96C00EE816D /* PreviewManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6697AF12283EE96C00EE816D /* PreviewManifest.swift */; }; 66EE523826BF560200B721AC /* ShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66EE523726BF560200B721AC /* ShareView.swift */; }; 66EE523B26BF5C0E00B721AC /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66EE523A26BF5C0E00B721AC /* ShareButton.swift */; }; /* End PBXBuildFile section */ @@ -432,7 +438,7 @@ 1932FCA726F4A06900837AA9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 1932FCAA26F4A07100837AA9 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; 1936597A260978EA00865D40 /* TweetNest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TweetNest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 19365983260978EB00865D40 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 19365983260978EB00865D40 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 19365995260978EB00865D40 /* TweetNestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TweetNestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 19365A1426097A9500865D40 /* TweetNest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TweetNest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 19365A3526097A9700865D40 /* TweetNestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TweetNestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -567,6 +573,9 @@ 662BAFE628365924005A9CA1 /* ManagedUserDetail+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ManagedUserDetail+Preview.swift"; sourceTree = ""; }; 663AEBB028351C84004E3491 /* ManagedAccount+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ManagedAccount+Preview.swift"; sourceTree = ""; }; 663E96C02838F5D60037413E /* Pasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pasteboard.swift; sourceTree = ""; }; + 6697AF09283EE26A00EE816D /* PersistentContainer+Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PersistentContainer+Preview.swift"; sourceTree = ""; }; + 6697AF12283EE96C00EE816D /* PreviewManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewManifest.swift; sourceTree = ""; }; + 6697AF2A28400DD100EE816D /* PreviewManifest.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = PreviewManifest.json; path = Assets.xcassets/PreviewManifest.dataset/PreviewManifest.json; sourceTree = ""; }; 66EE523726BF560200B721AC /* ShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareView.swift; sourceTree = ""; }; 66EE523A26BF5C0E00B721AC /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -735,6 +744,7 @@ 193033E526C405E7000EFBC7 /* TweetNestKit */ = { isa = PBXGroup; children = ( + 6697AF09283EE26A00EE816D /* PersistentContainer+Preview.swift */, 663AEBB028351C84004E3491 /* ManagedAccount+Preview.swift */, 662BAFE228365876005A9CA1 /* ManagedUser+Preview.swift */, 662BAFE628365924005A9CA1 /* ManagedUserDetail+Preview.swift */, @@ -743,12 +753,15 @@ path = TweetNestKit; sourceTree = ""; }; - 19365982260978EB00865D40 /* Preview Content */ = { + 19365982260978EB00865D40 /* Previews */ = { isa = PBXGroup; children = ( - 19365983260978EB00865D40 /* Preview Assets.xcassets */, + 6697AF12283EE96C00EE816D /* PreviewManifest.swift */, + 6697AF0D283EE55D00EE816D /* Extensions */, + 19365983260978EB00865D40 /* Assets.xcassets */, + 6697AF2A28400DD100EE816D /* PreviewManifest.json */, ); - path = "Preview Content"; + path = Previews; sourceTree = ""; }; 19365998260978EB00865D40 /* iOS */ = { @@ -770,7 +783,6 @@ 193AB25926B6FCFA00B18D45 /* Extensions */ = { isa = PBXGroup; children = ( - 193033E526C405E7000EFBC7 /* TweetNestKit */, 193AB25B26B6FD0700B18D45 /* Foundation */, ); path = Extensions; @@ -908,7 +920,7 @@ 193AB25926B6FCFA00B18D45 /* Extensions */, 663E96C02838F5D60037413E /* Pasteboard.swift */, 19933D2E25E901F4006AB4C0 /* Assets.xcassets */, - 19365982260978EB00865D40 /* Preview Content */, + 19365982260978EB00865D40 /* Previews */, ); path = Shared; sourceTree = ""; @@ -1091,6 +1103,14 @@ path = Components; sourceTree = ""; }; + 6697AF0D283EE55D00EE816D /* Extensions */ = { + isa = PBXGroup; + children = ( + 193033E526C405E7000EFBC7 /* TweetNestKit */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1440,7 +1460,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 19365984260978EB00865D40 /* Preview Assets.xcassets in Resources */, + 19365984260978EB00865D40 /* Assets.xcassets in Resources */, 66002D7126BF897000B52E30 /* Localizable.strings in Resources */, 193659B3260978F900865D40 /* Assets.xcassets in Resources */, 66002D6E26BF896F00B52E30 /* InfoPlist.strings in Resources */, @@ -1459,7 +1479,7 @@ buildActionMask = 2147483647; files = ( 66002D7726BF897000B52E30 /* Localizable.strings in Resources */, - 19365A6F26097B4A00865D40 /* Preview Assets.xcassets in Resources */, + 19365A6F26097B4A00865D40 /* Assets.xcassets in Resources */, 19365A6626097B4800865D40 /* Assets.xcassets in Resources */, 66002D7426BF897000B52E30 /* InfoPlist.strings in Resources */, ); @@ -1486,7 +1506,7 @@ files = ( 19A6FF5C26D6844F00AB7B7E /* Assets.xcassets in Resources */, 1932FCA626F4A06900837AA9 /* InfoPlist.strings in Resources */, - 19A1F1B926F38D4300A28A49 /* Preview Assets.xcassets in Resources */, + 19A1F1B926F38D4300A28A49 /* Assets.xcassets in Resources */, 19C6570826DAB53100D1A272 /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1580,10 +1600,12 @@ 194EF57E26BBFD190020939F /* AppSidebarAccountsSections+Section.swift in Sources */, 662BAFE328365876005A9CA1 /* ManagedUser+Preview.swift in Sources */, 1908FBFD26F6EE0E0095AF77 /* BatchDeleteTweetsView.swift in Sources */, + 6697AF13283EE96C00EE816D /* PreviewManifest.swift in Sources */, 1970663B26E7043500D7F21D /* SettingsGeneralView.swift in Sources */, 193B995E280FADB200A59984 /* SettingsFetchNewDataView.swift in Sources */, 195DD4C226BEF91C008F0309 /* ProfileImage.swift in Sources */, 66EE523826BF560200B721AC /* ShareView.swift in Sources */, + 6697AF0A283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */, 1908FC0926F6F1D90095AF77 /* BatchDeleteTweetsFormView.swift in Sources */, 19FB868227E5B1E500AAC5CB /* FetchedResultsController.swift in Sources */, 1920D59727F82CAE0083C23A /* AppStatusView.swift in Sources */, @@ -1626,6 +1648,7 @@ 19DB9E4D27084E96000E5FA2 /* PDFView.swift in Sources */, 193AFB7026CA36F6008BFF67 /* SettingsAccountView.swift in Sources */, 663E96C22838F5D60037413E /* Pasteboard.swift in Sources */, + 6697AF14283EE96C00EE816D /* PreviewManifest.swift in Sources */, 662BAFE428365876005A9CA1 /* ManagedUser+Preview.swift in Sources */, 19FB868727E5D10C00AAC5CB /* SectionedFetchedResultsController.swift in Sources */, 19FB868327E5B1E500AAC5CB /* FetchedResultsController.swift in Sources */, @@ -1639,6 +1662,7 @@ 19436100280BFB83008C2764 /* UserView+FootnotesView.swift in Sources */, 19C3AAE426B567750093D223 /* AppSidebarNavigation.swift in Sources */, 19D60702270BECCB0070125E /* DateText.swift in Sources */, + 6697AF0B283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */, 190007F327E6EB4E005CFED3 /* UserRowsLabel.swift in Sources */, 19365A6726097B4800865D40 /* TweetNestAppDelegate.swift in Sources */, 1995B92D26B6F1ED00AFCC9E /* UserView.swift in Sources */, @@ -1672,6 +1696,7 @@ 19EABBCA26D6877600D3B003 /* AppSidebarNavigation.swift in Sources */, 1908FC0F26F6F1E40095AF77 /* BatchDeleteTweetsProgressView.swift in Sources */, 19EABBC626D6877600D3B003 /* TweetNestApp.swift in Sources */, + 6697AF0C283EE26A00EE816D /* PersistentContainer+Preview.swift in Sources */, 19EABBDA26D6877A00D3B003 /* SettingsAccountView.swift in Sources */, 19FB868427E5B1E500AAC5CB /* FetchedResultsController.swift in Sources */, 19EABBD626D6877600D3B003 /* UserDataAssetImage.swift in Sources */, @@ -1713,6 +1738,7 @@ 1970663D26E7043500D7F21D /* SettingsGeneralView.swift in Sources */, 19EABBD826D6877A00D3B003 /* SettingsMainView.swift in Sources */, 190007F027E6E4C8005CFED3 /* UsersDiffListSection.swift in Sources */, + 6697AF15283EE96C00EE816D /* PreviewManifest.swift in Sources */, 19D60703270BECCB0070125E /* DateText.swift in Sources */, 19EABBD126D6877600D3B003 /* AppSidebarAccountsSections+Section.swift in Sources */, 19EABBD426D6877600D3B003 /* AccountEnvironmentKey.swift in Sources */, @@ -2070,7 +2096,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = TweetNest/iOS/TweetNest.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2102,7 +2128,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = TweetNest/iOS/TweetNest.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2182,7 +2208,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -2214,7 +2240,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -2470,7 +2496,7 @@ ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_ENTITLEMENTS = TweetNest/watchOS/TweetNest.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -2498,7 +2524,7 @@ ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CODE_SIGN_ENTITLEMENTS = TweetNest/watchOS/TweetNest.entitlements; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "TweetNest/Shared/Preview\\ Content/Preview\\ Assets.xcassets"; + DEVELOPMENT_ASSET_PATHS = TweetNest/Shared/Previews/Assets.xcassets; DEVELOPMENT_TEAM = SYA56G458A; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/TweetNest/Shared/Extensions/TweetNestKit/Session+Preview.swift b/TweetNest/Shared/Extensions/TweetNestKit/Session+Preview.swift deleted file mode 100644 index 0fe4559a..00000000 --- a/TweetNest/Shared/Extensions/TweetNestKit/Session+Preview.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// Session+Preview.swift -// TweetNest -// -// Created by Jaehong Kang on 2021/02/23. -// - -#if DEBUG - -import CoreData -import Foundation -import TweetNestKit -import UniformTypeIdentifiers - -#if canImport(AppKit) -import AppKit -#elseif canImport(UIKit) -import UIKit -#else -#error("AppKit or UIKit required.") -#endif - -extension TweetNestKit.Session { - public static let preview: Session = { - let session = Session(inMemory: true) - do { - try session.insertPreviewDataToPersistentContainer() - } catch let error as NSError { - fatalError("Unresolved error \(error), \(error.userInfo)") - } - return session - }() -} - -extension TweetNestKit.Session { - nonisolated func insertPreviewDataToPersistentContainer() throws { - let context = persistentContainer.newBackgroundContext() - - try context.performAndWait { - insertPreviewTweetNestAccountToPersistentContainer(context: context) - insertPreviewTwitterUsersToPersistentContainer(context: context) - insertPreviewAppleUserToPersistentContainer(context: context) - - try context.save() - } - } - - nonisolated private func insertPreviewTweetNestAccountToPersistentContainer(context: NSManagedObjectContext) { - let now = Date() - - let tweetnestAccount = ManagedAccount(context: context) - tweetnestAccount.creationDate = Date(timeIntervalSince1970: 1628780400) - tweetnestAccount.userID = "1352231658661920770" - tweetnestAccount.preferences.fetchBlockingUsers = true - tweetnestAccount.preferences.fetchMutingUsers = true - - let tweetnestUser = ManagedUser(context: context) - tweetnestUser.creationDate = Date(timeIntervalSince1970: 1628780400) - tweetnestUser.lastUpdateStartDate = now - tweetnestUser.lastUpdateEndDate = now - tweetnestUser.id = tweetnestAccount.userID - - var baseTweetnestUserDetail: ManagedUserDetail { - let tweetnestUserDetail = ManagedUserDetail(context: context) - - tweetnestUserDetail.creationDate = Date(timeIntervalSince1970: 1628780400) - tweetnestUserDetail.followerUserIDs = ["783214", "17874544"] - tweetnestUserDetail.followerUsersCount = 2 - tweetnestUserDetail.followingUserIDs = ["783214", "17874544"] - tweetnestUserDetail.followingUsersCount = 2 - tweetnestUserDetail.blockingUserIDs = [] - tweetnestUserDetail.mutingUserIDs = [] - tweetnestUserDetail.userCreationDate = Date(timeIntervalSince1970: 1616357217) - tweetnestUserDetail.location = "대한민국 서울" - tweetnestUserDetail.name = "TweetNest" - tweetnestUserDetail.username = "TweetNest_App" - tweetnestUserDetail.profileImageURL = URL(string: "https://pbs.twimg.com/profile_images/1373878674903113729/JL3SGoch.png") - tweetnestUserDetail.url = URL(string: "https://www.tweetnest.com") - tweetnestUserDetail.userID = tweetnestUser.id - - return tweetnestUserDetail - } - - let tweetnestUserDetail1 = baseTweetnestUserDetail - - let tweetnestUserDetail2 = baseTweetnestUserDetail - tweetnestUserDetail2.creationDate = Date(timeIntervalSince1970: 1631709579) - tweetnestUserDetail2.followerUserIDs = ["783214"] - tweetnestUserDetail2.followerUsersCount = 1 - tweetnestUserDetail2.followingUserIDs = ["783214"] - tweetnestUserDetail2.followingUsersCount = 1 - - let tweetnestUserDetail3 = baseTweetnestUserDetail - tweetnestUserDetail3.creationDate = now - tweetnestUserDetail3.followerUserIDs = ["783214", "380749300"] - tweetnestUserDetail3.followerUsersCount = 2 - tweetnestUserDetail3.followingUserIDs = ["783214", "380749300"] - tweetnestUserDetail3.followingUsersCount = 2 - - let tweetnestProfileImageDataAsset = ManagedUserDataAsset(context: context) - tweetnestProfileImageDataAsset.url = tweetnestUserDetail1.profileImageURL - tweetnestProfileImageDataAsset.dataMIMEType = UTType.png.preferredMIMEType - tweetnestProfileImageDataAsset.data = NSDataAsset(name: "TweetNestProfileImageData")?.data - } - - nonisolated private func insertPreviewTwitterUsersToPersistentContainer(context: NSManagedObjectContext) { - let now = Date() - - let twitterUser = ManagedUser(context: context) - twitterUser.creationDate = now - twitterUser.lastUpdateStartDate = now - twitterUser.lastUpdateEndDate = now - twitterUser.id = "783214" - - let twitterUserDetail = ManagedUserDetail(context: context) - twitterUserDetail.creationDate = now - twitterUserDetail.followerUsersCount = 0 - twitterUserDetail.followingUsersCount = 0 - twitterUserDetail.userCreationDate = Date(timeIntervalSinceReferenceDate: 1171982154) - twitterUserDetail.location = "everywhere" - twitterUserDetail.name = "Twitter" - twitterUserDetail.username = "Twitter" - twitterUserDetail.profileImageURL = URL(string: "https://pbs.twimg.com/profile_images/1354479643882004483/Btnfm47p.jpg") - twitterUserDetail.url = URL(string: "https://about.twitter.com/") - twitterUserDetail.userID = twitterUser.id - twitterUserDetail.userAttributedDescription = NSAttributedString(string: "what’s happening?!") - - let twitterSupportUser = ManagedUser(context: context) - twitterSupportUser.creationDate = now - twitterSupportUser.lastUpdateStartDate = now - twitterSupportUser.lastUpdateEndDate = now - twitterSupportUser.id = "17874544" - - let twitterSupportUserDetail = ManagedUserDetail(context: context) - twitterSupportUserDetail.creationDate = now - twitterSupportUserDetail.followerUsersCount = 0 - twitterSupportUserDetail.followingUsersCount = 0 - twitterSupportUserDetail.userCreationDate = Date(timeIntervalSinceReferenceDate: 1228416717) - twitterSupportUserDetail.location = "Twitter HQ" - twitterSupportUserDetail.name = "Twitter Support" - twitterSupportUserDetail.username = "TwitterSupport" - twitterSupportUserDetail.profileImageURL = URL(string: "https://pbs.twimg.com/profile_images/1354479643882004483/Btnfm47p.jpg") - twitterSupportUserDetail.url = URL(string: "https://help.twitter.com") - twitterSupportUserDetail.userID = twitterSupportUser.id - twitterSupportUserDetail.userAttributedDescription = NSAttributedString(string: "Here to help. 💙") - - let twitterProfileImageDataAsset = ManagedUserDataAsset(context: context) - twitterProfileImageDataAsset.url = twitterUserDetail.profileImageURL - twitterProfileImageDataAsset.dataMIMEType = UTType.jpeg.preferredMIMEType - twitterProfileImageDataAsset.data = NSDataAsset(name: "TwitterProfileImageData")?.data - } - - nonisolated private func insertPreviewAppleUserToPersistentContainer(context: NSManagedObjectContext) { - let now = Date() - - let appleUser = ManagedUser(context: context) - appleUser.creationDate = now - appleUser.lastUpdateStartDate = now - appleUser.lastUpdateEndDate = now - appleUser.id = "380749300" - - let appleUserDetail = ManagedUserDetail(context: context) - appleUserDetail.creationDate = now - appleUserDetail.followerUsersCount = 0 - appleUserDetail.followingUsersCount = 0 - appleUserDetail.userCreationDate = Date(timeIntervalSinceReferenceDate: 1317099723) - appleUserDetail.location = "Cupertino, CA" - appleUserDetail.name = "Apple" - appleUserDetail.username = "Apple" - appleUserDetail.profileImageURL = URL(string: "https://pbs.twimg.com/profile_images/1283958620359516160/p7zz5dxZ.jpg") - appleUserDetail.url = nil - appleUserDetail.userID = appleUser.id - appleUserDetail.userAttributedDescription = NSAttributedString(string: "Apple.com", attributes: [.link: URL(string: "http://Apple.com")!]) - - let appleProfileImageDataAsset = ManagedUserDataAsset(context: context) - appleProfileImageDataAsset.url = appleUserDetail.profileImageURL - appleProfileImageDataAsset.dataMIMEType = UTType.jpeg.preferredMIMEType - appleProfileImageDataAsset.data = NSDataAsset(name: "AppleProfileImageData")?.data - } -} - -#endif diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/AppleProfileImageData.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/AppleProfileImageData.dataset/Contents.json similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/AppleProfileImageData.dataset/Contents.json rename to TweetNest/Shared/Previews/Assets.xcassets/AppleProfileImageData.dataset/Contents.json diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/AppleProfileImageData.dataset/p7zz5dxZ.jpg b/TweetNest/Shared/Previews/Assets.xcassets/AppleProfileImageData.dataset/p7zz5dxZ.jpg similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/AppleProfileImageData.dataset/p7zz5dxZ.jpg rename to TweetNest/Shared/Previews/Assets.xcassets/AppleProfileImageData.dataset/p7zz5dxZ.jpg diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/Contents.json similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/Contents.json rename to TweetNest/Shared/Previews/Assets.xcassets/Contents.json diff --git a/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/Contents.json new file mode 100644 index 00000000..3e8c22fb --- /dev/null +++ b/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/Contents.json @@ -0,0 +1,13 @@ +{ + "data" : [ + { + "filename" : "PreviewManifest.json", + "idiom" : "universal", + "universal-type-identifier" : "public.json" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/PreviewManifest.json b/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/PreviewManifest.json new file mode 100644 index 00000000..8f755773 --- /dev/null +++ b/TweetNest/Shared/Previews/Assets.xcassets/PreviewManifest.dataset/PreviewManifest.json @@ -0,0 +1,138 @@ +{ + "accounts": [ + "1352231658661920770" + ], + "data-assets": { + "783214": [ + { + "data-mime-type": "image/jpeg", + "data-resource-name": "TwitterProfileImageData", + "url": "https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF.jpg" + } + ], + "17874544": [ + { + "data-mime-type": "image/jpeg", + "data-resource-name": "TwitterSupportProfileImageData", + "url": "https://pbs.twimg.com/profile_images/1354479643882004483/Btnfm47p.jpg" + } + ], + "380749300": [ + { + "data-mime-type": "image/jpeg", + "data-resource-name": "AppleProfileImageData", + "url": "https://pbs.twimg.com/profile_images/1283958620359516160/p7zz5dxZ.jpg" + } + ], + "1352231658661920770": [ + { + "data-mime-type": "image/jpeg", + "data-resource-name": "TweetNestProfileHeaderImageData", + "url": "https://pbs.twimg.com/profile_banners/1373727821726580736/1632538699" + }, + { + "data-mime-type": "image/png", + "data-resource-name": "TweetNestProfileImageData", + "url": "https://pbs.twimg.com/profile_images/1373878674903113729/JL3SGoch.png" + } + ] + }, + "user-details": { + "783214": [ + { + "is-verified": true, + "location": "everywhere", + "name": "Twitter", + "profile-image-url": "https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF.jpg", + "url": "https://about.twitter.com/", + "user-attributed-description": "what’s happening?!", + "user-creation-date": "2007-02-20T14:35:54Z", + "user-id": "783214", + "username": "Twitter" + } + ], + "17874544": [ + { + "is-verified": true, + "location": "Twitter HQ", + "name": "Twitter Support", + "profile-image-url": "https://pbs.twimg.com/profile_images/1354479643882004483/Btnfm47p.jpg", + "url": "https://help.twitter.com", + "user-attributed-description": "Here to help. 💙", + "user-creation-date": "2008-12-04T18:51:57Z", + "user-id": "17874544", + "username": "TwitterSupport" + } + ], + "380749300": [ + { + "is-verified": true, + "location": "Cupertino, CA", + "name": "Apple", + "profile-image-url": "https://pbs.twimg.com/profile_images/1283958620359516160/p7zz5dxZ.jpg", + "user-attributed-description": [ + "Apple.com", + { + "NSLink": { + "relative": "http://Apple.com" + } + } + ], + "user-creation-date": "2011-09-27T05:02:03Z", + "user-id": "380749300", + "username": "Apple" + } + ], + "1352231658661920770": [ + { + "blocking-user-ids": [], + "creation-date": "2021-08-12T15:00:00Z", + "follower-user-ids": [ + "783214", + "17874544" + ], + "follower-users-count": 2, + "following-user-ids": [ + "783214", + "17874544" + ], + "following-users-count": 2, + "location": "대한민국 서울", + "muting-user-ids": [], + "name": "TweetNest", + "profile-header-image-url": "https://pbs.twimg.com/profile_banners/1373727821726580736/1632538699", + "profile-image-url": "https://pbs.twimg.com/profile_images/1373878674903113729/JL3SGoch.png", + "url": "https://app.tweetnest.com/?pt=123424250&ct=Twitter&mt=8", + "user-attributed-description": "A Twitter account manager for iOS/iPadOS/watchOS/macOS. Don’t hesitate to ask about TweetNest app.", + "user-creation-date": "2021-03-21T20:06:57Z", + "user-id": "1352231658661920770", + "username": "TweetNest_App" + }, + { + "creation-date": "2021-09-15T12:39:39Z", + "follower-user-ids": [ + "783214" + ], + "follower-users-count": 1, + "following-user-ids": [ + "783214" + ], + "following-users-count": 1, + "inherits": true + }, + { + "follower-user-ids": [ + "380749300", + "783214" + ], + "follower-users-count": 2, + "following-user-ids": [ + "380749300", + "783214" + ], + "following-users-count": 2, + "inherits": true + } + ] + } +} diff --git a/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/1632538699.jpeg b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/1632538699.jpeg new file mode 100644 index 00000000..cf7bdc9d Binary files /dev/null and b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/1632538699.jpeg differ diff --git a/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/Contents.json new file mode 100644 index 00000000..32efc13d --- /dev/null +++ b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileHeaderImageData.dataset/Contents.json @@ -0,0 +1,13 @@ +{ + "data" : [ + { + "filename" : "1632538699.jpeg", + "idiom" : "universal", + "universal-type-identifier" : "public.jpeg" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/TweetNestProfileImageData.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileImageData.dataset/Contents.json similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/TweetNestProfileImageData.dataset/Contents.json rename to TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileImageData.dataset/Contents.json diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/TweetNestProfileImageData.dataset/JL3SGoch.png b/TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileImageData.dataset/JL3SGoch.png similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/TweetNestProfileImageData.dataset/JL3SGoch.png rename to TweetNest/Shared/Previews/Assets.xcassets/TweetNestProfileImageData.dataset/JL3SGoch.png diff --git a/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/Contents.json new file mode 100644 index 00000000..2ae1ed64 --- /dev/null +++ b/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/Contents.json @@ -0,0 +1,13 @@ +{ + "data" : [ + { + "filename" : "u6qfBBkF.jpg", + "idiom" : "universal", + "universal-type-identifier" : "public.jpeg" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/u6qfBBkF.jpg b/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/u6qfBBkF.jpg new file mode 100644 index 00000000..b09664a3 Binary files /dev/null and b/TweetNest/Shared/Previews/Assets.xcassets/TwitterProfileImageData.dataset/u6qfBBkF.jpg differ diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/TwitterProfileImageData.dataset/Btnfm47p.jpg b/TweetNest/Shared/Previews/Assets.xcassets/TwitterSupportProfileImageData.dataset/Btnfm47p.jpg similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/TwitterProfileImageData.dataset/Btnfm47p.jpg rename to TweetNest/Shared/Previews/Assets.xcassets/TwitterSupportProfileImageData.dataset/Btnfm47p.jpg diff --git a/TweetNest/Shared/Preview Content/Preview Assets.xcassets/TwitterProfileImageData.dataset/Contents.json b/TweetNest/Shared/Previews/Assets.xcassets/TwitterSupportProfileImageData.dataset/Contents.json similarity index 100% rename from TweetNest/Shared/Preview Content/Preview Assets.xcassets/TwitterProfileImageData.dataset/Contents.json rename to TweetNest/Shared/Previews/Assets.xcassets/TwitterSupportProfileImageData.dataset/Contents.json diff --git a/TweetNest/Shared/Extensions/TweetNestKit/ManagedAccount+Preview.swift b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedAccount+Preview.swift similarity index 88% rename from TweetNest/Shared/Extensions/TweetNestKit/ManagedAccount+Preview.swift rename to TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedAccount+Preview.swift index 47d83d1d..7c046214 100644 --- a/TweetNest/Shared/Extensions/TweetNestKit/ManagedAccount+Preview.swift +++ b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedAccount+Preview.swift @@ -6,13 +6,13 @@ // #if DEBUG - import Foundation import TweetNestKit extension TweetNestKit.ManagedAccount { - public static var preview: ManagedAccount { + @usableFromInline + static var preview: ManagedAccount { let fetchRequest = ManagedAccount.fetchRequest() do { return try Session.preview.persistentContainer.viewContext.fetch(fetchRequest)[0] @@ -21,5 +21,4 @@ extension TweetNestKit.ManagedAccount { } } } - #endif diff --git a/TweetNest/Shared/Extensions/TweetNestKit/ManagedUser+Preview.swift b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUser+Preview.swift similarity index 75% rename from TweetNest/Shared/Extensions/TweetNestKit/ManagedUser+Preview.swift rename to TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUser+Preview.swift index 472cc33a..b671a287 100644 --- a/TweetNest/Shared/Extensions/TweetNestKit/ManagedUser+Preview.swift +++ b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUser+Preview.swift @@ -6,21 +6,19 @@ // #if DEBUG - import Foundation import TweetNestKit extension TweetNestKit.ManagedUser { @inlinable - public static var preview: ManagedUser { + static var preview: ManagedUser { previews.first! } @inlinable - public static var previews: [ManagedUser] { + static var previews: [ManagedUser] { ManagedAccount.preview.users! } } - #endif diff --git a/TweetNest/Shared/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift similarity index 74% rename from TweetNest/Shared/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift rename to TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift index 7480fd95..c20a17f5 100644 --- a/TweetNest/Shared/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift +++ b/TweetNest/Shared/Previews/Extensions/TweetNestKit/ManagedUserDetail+Preview.swift @@ -6,21 +6,19 @@ // #if DEBUG - import Foundation import TweetNestKit extension TweetNestKit.ManagedUserDetail { @inlinable - public static var preview: ManagedUserDetail { + static var preview: ManagedUserDetail { previews.first! } @inlinable - public static var previews: [ManagedUserDetail] { + static var previews: [ManagedUserDetail] { ManagedUser.preview.userDetails! } } - #endif diff --git a/TweetNest/Shared/Previews/Extensions/TweetNestKit/PersistentContainer+Preview.swift b/TweetNest/Shared/Previews/Extensions/TweetNestKit/PersistentContainer+Preview.swift new file mode 100644 index 00000000..c7908e62 --- /dev/null +++ b/TweetNest/Shared/Previews/Extensions/TweetNestKit/PersistentContainer+Preview.swift @@ -0,0 +1,263 @@ +// +// PersistentContainer+Preview.swift +// TweetNest +// +// Created by Mina Her on 2022/05/26. +// + +#if DEBUG +#if canImport(AppKit) +import AppKit +#elseif canImport(UIKit) +import UIKit +#else +#error("AppKit or UIKit required.") +#endif + +import CoreData +import Foundation +import TweetNestKit + +extension PersistentContainer { + + @inlinable + nonisolated func injectPreviewData( + context: NSManagedObjectContext? = nil, + save: Bool = false + ) { + injectPreviewData(manifest: "PreviewManifest", context: context, save: save) + } + + @usableFromInline + nonisolated func injectPreviewData( + manifest resourceName: String, + bundle: Bundle = .main, + context: NSManagedObjectContext? = nil, + save: Bool = false + ) { + guard context != nil || save else { + fatalError("Should provide context parameter or set save parameter true") + } + let context = context ?? newBackgroundContext() + guard let dataAsset = NSDataAsset(name: resourceName, bundle: bundle) else { + fatalError("Cannot find preview manifest asset with given name: \(resourceName)") + } + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let previewManifest: PreviewManifest + do { + previewManifest = try decoder.decode(PreviewManifest.self, from: dataAsset.data) + } catch let error as NSError { + fatalError("Error has occurred while decoding contents of the preview manifest asset: \(resourceName) (\(error), \(error.userInfo))") + } + let state = _PreviewDataInjectionState(bundle: bundle) + for (userID, previewUserDetails) in previewManifest.userDetails { + state.userDetail = nil + for previewUserDetail in previewUserDetails { + let userDetail = _injectPreviewUserDetail(previewUserDetail, state: state, context: context) + if state.userIDToUserDetail[userID] == nil { + state.userIDToUserDetail[userID] = userDetail + } + state.userDetail = userDetail + } + } + for (userID, userDetail) in state.userIDToUserDetail { + state.userDetail = userDetail + _injectPreviewUser(id: userID, state: state, context: context) + } + for userID in previewManifest.accounts { + guard let userDetail = state.userIDToUserDetail[userID] else { + continue + } + state.userDetail = userDetail + _injectPreviewAccount(userID: userID, state: state, context: context) + } + for (userID, previewUserDataAssets) in previewManifest.dataAssets { + guard let userDetail = state.userIDToUserDetail[userID] else { + continue + } + state.userDetail = userDetail + for previewUserDataAsset in previewUserDataAssets { + _injectPreviewUserDataAsset(previewUserDataAsset, state: state, context: context) + } + } + if save { + context.performAndWait { + do { + try context.save() + } catch let error as NSError { + fatalError("Cannot save context: \(context) (\(error), \(error.userInfo))") + } + } + } + } +} + +extension PersistentContainer { + + private final class _PreviewDataInjectionState { + + let bundle: Bundle + + var userDetail: ManagedUserDetail? + + var userIDToUserDetail: [String: ManagedUserDetail] = .init() + + init(bundle: Bundle) { + self.bundle = bundle + } + } + + @discardableResult + private nonisolated func _injectPreviewAccount( + userID: String, + state: _PreviewDataInjectionState, + context: NSManagedObjectContext? = nil, + save: Bool = false + ) -> ManagedAccount { + guard context != nil || save else { + fatalError("Should provide context parameter or set save parameter true") + } + let context = context ?? newBackgroundContext() + var account: ManagedAccount! + context.performAndWait { + account = ManagedAccount(context: context) + account.creationDate = state.userDetail?.creationDate ?? .now + account.preferences.fetchBlockingUsers = true + account.preferences.fetchMutingUsers = true + account.userID = userID + if save { + do { + try context.save() + } catch let error as NSError { + fatalError("Cannot save context: \(context) (\(error), \(error.userInfo))") + } + } + } + return account + } + + @discardableResult + private nonisolated func _injectPreviewUser( + id userID: String, + state: _PreviewDataInjectionState, + context: NSManagedObjectContext? = nil, + save: Bool = false + ) -> ManagedUser { + guard context != nil || save else { + fatalError("Should provide context parameter or set save parameter true") + } + let context = context ?? newBackgroundContext() + var user: ManagedUser! + context.performAndWait { + user = ManagedUser(context: context) + user.lastUpdateStartDate = .now + user.creationDate = state.userDetail?.creationDate ?? .now + user.id = userID + user.lastUpdateEndDate = .now + if save { + do { + try context.save() + } catch let error as NSError { + fatalError("Cannot save context: \(context) (\(error), \(error.userInfo))") + } + } + } + return user + } + + @discardableResult + private nonisolated func _injectPreviewUserDataAsset( + _ previewUserDataAsset: PreviewManifest.UserDataAsset, + state: _PreviewDataInjectionState, + context: NSManagedObjectContext? = nil, + save: Bool = false + ) -> ManagedUserDataAsset { + guard context != nil || save else { + fatalError("Should provide context parameter or set save parameter true") + } + let context = context ?? newBackgroundContext() + var userDataAsset: ManagedUserDataAsset! + context.performAndWait { + guard let dataAsset = NSDataAsset(name: previewUserDataAsset.dataResourceName, bundle: state.bundle) else { + fatalError("Cannot find provided asset with given name: \(previewUserDataAsset.dataResourceName)") + } + userDataAsset = ManagedUserDataAsset(context: context) + userDataAsset.creationDate = previewUserDataAsset.creationDate ?? .now + userDataAsset.data = dataAsset.data + userDataAsset.dataMIMEType = previewUserDataAsset.dataMIMEType + userDataAsset.url = previewUserDataAsset.url + if save { + do { + try context.save() + } catch let error as NSError { + fatalError("Cannot save context: \(context) (\(error), \(error.userInfo))") + } + } + } + return userDataAsset + } + + @discardableResult + private nonisolated func _injectPreviewUserDetail( + _ previewUserDetail: PreviewManifest.UserDetail, + state: _PreviewDataInjectionState, + context: NSManagedObjectContext? = nil, + save: Bool = false + ) -> ManagedUserDetail { + guard context != nil || save else { + fatalError("Should provide context parameter or set save parameter true") + } + let context = context ?? newBackgroundContext() + var userDetail: ManagedUserDetail! + context.performAndWait { + userDetail = ManagedUserDetail(context: context) + if previewUserDetail.inherits == true, let latestUserDetail = state.userDetail { + let attributeNames = ManagedUserDetail.entity().attributesByName.keys + userDetail.setValuesForKeys(.init(uniqueKeysWithValues: attributeNames.map({($0, latestUserDetail.value(forKey: $0) as Any)}))) + userDetail.creationDate = nil + } + func assign(_ value: T?, to keyPath: ReferenceWritableKeyPath) { + if let value = value { + userDetail[keyPath: keyPath] = value + } + } + func assign(_ value: T?, to keyPath: ReferenceWritableKeyPath, default: T? = nil) { + if let value = value { + userDetail[keyPath: keyPath] = value + } else if userDetail![keyPath: keyPath] == nil, let `default` = `default` { + userDetail[keyPath: keyPath] = `default` + } + } + assign(previewUserDetail.blockingUserIDs, to: \.blockingUserIDs) + assign(previewUserDetail.creationDate, to: \.creationDate, default: .now) + assign(previewUserDetail.followerUserIDs, to: \.followerUserIDs) + assign(previewUserDetail.followerUsersCount.flatMap({.init($0)}), to: \.followerUsersCount) + assign(previewUserDetail.followingUserIDs, to: \.followingUserIDs) + assign(previewUserDetail.followingUsersCount.flatMap({.init($0)}), to: \.followingUsersCount) + assign(previewUserDetail.isProtected, to: \.isProtected) + assign(previewUserDetail.isVerified, to: \.isVerified) + assign(previewUserDetail.listedCount.flatMap({.init($0)}), to: \.listedCount) + assign(previewUserDetail.location, to: \.location) + assign(previewUserDetail.mutingUserIDs, to: \.mutingUserIDs) + assign(previewUserDetail.name, to: \.name) + assign(previewUserDetail.profileHeaderImageURL, to: \.profileHeaderImageURL) + assign(previewUserDetail.profileImageURL, to: \.profileImageURL) + assign(previewUserDetail.tweetsCount.flatMap({.init($0)}), to: \.tweetsCount) + assign(previewUserDetail.url, to: \.url) + assign(previewUserDetail.userAttributedDescription.flatMap({.init($0)}), to: \.userAttributedDescription) + assign(previewUserDetail.userCreationDate, to: \.userCreationDate) + assign(previewUserDetail.userID, to: \.userID) + assign(previewUserDetail.username, to: \.username) + if save { + do { + try context.save() + } catch let error as NSError { + fatalError("Cannot save context: \(context) (\(error), \(error.userInfo))") + } + } + } + return userDetail + } +} +#endif diff --git a/TweetNest/Shared/Previews/Extensions/TweetNestKit/Session+Preview.swift b/TweetNest/Shared/Previews/Extensions/TweetNestKit/Session+Preview.swift new file mode 100644 index 00000000..ea8f9f38 --- /dev/null +++ b/TweetNest/Shared/Previews/Extensions/TweetNestKit/Session+Preview.swift @@ -0,0 +1,19 @@ +// +// Session+Preview.swift +// TweetNest +// +// Created by Jaehong Kang on 2021/02/23. +// + +#if DEBUG +import TweetNestKit + +extension TweetNestKit.Session { + + static let preview: Session = { + let session = Session(inMemory: true) + session.persistentContainer.injectPreviewData(save: true) + return session + }() +} +#endif diff --git a/TweetNest/Shared/Previews/PreviewManifest.swift b/TweetNest/Shared/Previews/PreviewManifest.swift new file mode 100644 index 00000000..05200825 --- /dev/null +++ b/TweetNest/Shared/Previews/PreviewManifest.swift @@ -0,0 +1,156 @@ +// +// PreviewManifest.swift +// TweetNest +// +// Created by Mina Her on 2022/05/26. +// + +#if DEBUG +import Foundation + +struct PreviewManifest { + + var accounts: [UserID] + + var dataAssets: [UserID: [UserDataAsset]] + + var userDetails: [UserID: [UserDetail]] +} + +extension PreviewManifest { + + typealias ResourceName = String + + typealias UserID = String + + struct UserDataAsset { + + var creationDate: Date? + + var dataResourceName: String + + var dataMIMEType: String? + + var url: URL? + } + + struct UserDetail { + + var blockingUserIDs: [UserID]? + + var creationDate: Date? + + var followerUserIDs: [UserID]? + + var followerUsersCount: Int? + + var followingUserIDs: [UserID]? + + var followingUsersCount: Int? + + var inherits: Bool? + + var isProtected: Bool? + + var isVerified: Bool? + + var listedCount: Int? + + var location: String? + + var mutingUserIDs: [UserID]? + + var name: String? + + var profileHeaderImageURL: URL? + + var profileImageURL: URL? + + var tweetsCount: Int? + + var url: URL? + + var userAttributedDescription: AttributedString? + + var userCreationDate: Date? + + var userID: UserID? + + var username: String? + } +} + +extension PreviewManifest: Codable { + + private enum CodingKeys: String, CodingKey { + + case accounts = "accounts" + + case dataAssets = "data-assets" + + case userDetails = "user-details" + } +} + +extension PreviewManifest.UserDataAsset: Codable { + + private enum CodingKeys: String, CodingKey { + + case creationDate = "creation-date" + + case dataMIMEType = "data-mime-type" + + case dataResourceName = "data-resource-name" + + case url = "url" + } +} + +extension PreviewManifest.UserDetail: Codable { + + private enum CodingKeys: String, CodingKey { + + case blockingUserIDs = "blocking-user-ids" + + case creationDate = "creation-date" + + case followerUserIDs = "follower-user-ids" + + case followerUsersCount = "follower-users-count" + + case followingUserIDs = "following-user-ids" + + case followingUsersCount = "following-users-count" + + case inherits = "inherits" + + case isProtected = "is-protected" + + case isVerified = "is-verified" + + case listedCount = "listed-count" + + case location = "location" + + case mutingUserIDs = "muting-user-ids" + + case name = "name" + + case profileHeaderImageURL = "profile-header-image-url" + + case profileImageURL = "profile-image-url" + + case tweetsCount = "tweets-count" + + case url = "url" + + case userAttributedDescription = "user-attributed-description" + + case userCreationDate = "user-creation-date" + + case userID = "user-id" + + case username = "username" + } +} +#endif diff --git a/TweetNest/Shared/TweetNestApp.swift b/TweetNest/Shared/TweetNestApp.swift index 2d48a2e1..f97043cb 100644 --- a/TweetNest/Shared/TweetNestApp.swift +++ b/TweetNest/Shared/TweetNestApp.swift @@ -68,8 +68,14 @@ struct TweetNestApp: App { case .active, .inactive: Self.session.resumeBackgroundTaskTimers() #if canImport(CoreSpotlight) + #if DEBUG + if !Self.isPreview { + Self.session.persistentContainer.usersSpotlightDelegate.startSpotlightIndexing() + } + #else Self.session.persistentContainer.usersSpotlightDelegate.startSpotlightIndexing() #endif + #endif case .background: Self.session.pauseBackgroundTaskTimers() #if (canImport(BackgroundTasks) && !os(macOS)) || canImport(WatchKit) @@ -78,8 +84,14 @@ struct TweetNestApp: App { } #endif #if canImport(CoreSpotlight) + #if DEBUG + if !Self.isPreview { + Self.session.persistentContainer.usersSpotlightDelegate.stopSpotlightIndexing() + } + #else Self.session.persistentContainer.usersSpotlightDelegate.stopSpotlightIndexing() #endif + #endif @unknown default: break }