Merge branch 'main' into multi-server-url

This commit is contained in:
Ethan Pippin 2021-11-10 22:55:06 -07:00
commit 1ee02dfcb0
164 changed files with 4922 additions and 626 deletions

1
.gitignore vendored
View File

@ -96,3 +96,4 @@ iOSInjectionProject/
.Trashes
ehthumbs.db
Thumbs.db
Shared/Generated/Strings.swift

View File

@ -20,7 +20,7 @@ struct MediaPlayButtonRowView: View {
NavigationLink(destination: VideoPlayerView(item: viewModel.item).ignoresSafeArea()) {
MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView)
}
Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : "Play")
Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : L10n.play)
.font(.caption)
}
VStack {

View File

@ -68,7 +68,7 @@ struct PortraitItemElement: View {
.font(.caption)
.fontWeight(.medium)
} else {
Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))")
Text(L10n.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0)))
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)

View File

@ -22,7 +22,7 @@ struct BasicAppSettingsView: View {
var body: some View {
Form {
Section {
Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) {
Picker(L10n.appearance, selection: $appAppearance) {
ForEach(self.viewModel.appearances, id: \.self) { appearance in
Text(appearance.localizedName).tag(appearance.rawValue)
}
@ -30,21 +30,21 @@ struct BasicAppSettingsView: View {
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = appAppearance.style
})
} header: {
Text("Accessibility")
L10n.accessibility.text
}
Button {
resetTapped = true
} label: {
Text("Reset")
L10n.reset.text
}
}
.alert("Reset", isPresented: $resetTapped, actions: {
.alert(L10n.reset, isPresented: $resetTapped, actions: {
Button(role: .destructive) {
viewModel.reset()
basicAppSettingsRouter.dismissCoordinator()
} label: {
Text("Reset")
L10n.reset.text
}
})
.navigationTitle("Settings")

View File

@ -17,7 +17,7 @@ struct ConnectToServerView: View {
var body: some View {
List {
Section {
TextField(NSLocalizedString("Server URL", comment: ""), text: $uri)
TextField(L10n.serverURL, text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
.keyboardType(.URL)
@ -25,7 +25,7 @@ struct ConnectToServerView: View {
viewModel.connectToServer(uri: uri)
} label: {
HStack {
Text("Connect")
L10n.connect.text
Spacer()
if viewModel.isLoading {
ProgressView()
@ -37,7 +37,7 @@ struct ConnectToServerView: View {
Text("Connect to a Jellyfin server")
}
Section(header: Text("Local Servers")) {
Section(header: L10n.localServers.text) {
if viewModel.searching {
ProgressView()
}
@ -68,6 +68,6 @@ struct ConnectToServerView: View {
message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"),
dismissButton: .cancel())
}
.navigationTitle("Connect")
.navigationTitle(L10n.connect)
}
}

View File

@ -20,7 +20,7 @@ struct ContinueWatchingView: View {
var body: some View {
VStack(alignment: .leading) {
if items.count > 0 {
Text("Continue Watching")
L10n.continueWatching.text
.font(.headline)
.fontWeight(.semibold)
.padding(.leading, 90)

View File

@ -38,7 +38,7 @@ struct HomeView: View {
self.homeRouter.route(to: \.modalLibrary, (.init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? ""))
} label: {
HStack {
Text("Latest \(library?.name ?? "")")
Text(L10n.latestWithString(library?.name ?? ""))
.font(.headline)
.fontWeight(.semibold)
Image(systemName: "chevron.forward.circle.fill")

View File

@ -76,7 +76,7 @@ struct EpisodeItemView: View {
HStack(alignment: .top) {
VStack(alignment: .trailing) {
if studio != nil {
Text("STUDIO")
L10n.studio.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -88,7 +88,7 @@ struct EpisodeItemView: View {
}
if director != nil {
Text("DIRECTOR")
L10n.director.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -100,7 +100,7 @@ struct EpisodeItemView: View {
}
if !actors.isEmpty {
Text("CAST")
L10n.cast.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -133,7 +133,7 @@ struct EpisodeItemView: View {
NavigationLink(destination: VideoPlayerView(item: viewModel.item).ignoresSafeArea()) {
MediaViewActionButton(icon: "play.fill")
}
Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : "Play")
Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : L10n.play)
.font(.caption)
}
VStack {
@ -152,7 +152,7 @@ struct EpisodeItemView: View {
}.padding(.top, 50)
if !viewModel.similarItems.isEmpty {
Text("More Like This")
L10n.moreLikeThis.text
.font(.headline)
.fontWeight(.semibold)
ScrollView(.horizontal) {

View File

@ -40,7 +40,7 @@ struct ItemView: View {
} else if item.type == "Episode" {
EpisodeItemView(viewModel: .init(item: item))
} else {
Text("Type: \(item.type ?? "") not implemented yet :(")
Text(L10n.notImplementedYetWithType(item.type ?? ""))
}
}
}

View File

@ -77,7 +77,7 @@ struct MovieItemView: View {
HStack {
VStack(alignment: .trailing) {
if studio != nil {
Text("STUDIO")
L10n.studio.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -89,7 +89,7 @@ struct MovieItemView: View {
}
if director != nil {
Text("DIRECTOR")
L10n.director.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -101,7 +101,7 @@ struct MovieItemView: View {
}
if !actors.isEmpty {
Text("CAST")
L10n.cast.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -133,7 +133,7 @@ struct MovieItemView: View {
}.padding(.top, 50)
if !viewModel.similarItems.isEmpty {
Text("More Like This")
L10n.moreLikeThis.text
.font(.headline)
.fontWeight(.semibold)
ScrollView(.horizontal) {

View File

@ -95,7 +95,7 @@ struct SeasonItemView: View {
}.padding(.top, 50)
if !viewModel.episodes.isEmpty {
Text("Episodes")
L10n.episodes.text
.font(.headline)
.fontWeight(.semibold)
ScrollView(.horizontal) {

View File

@ -79,7 +79,7 @@ struct SeriesItemView: View {
HStack {
VStack(alignment: .trailing) {
if studio != nil {
Text("STUDIO")
L10n.studio.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -91,7 +91,7 @@ struct SeriesItemView: View {
}
if director != nil {
Text("DIRECTOR")
L10n.director.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -103,7 +103,7 @@ struct SeriesItemView: View {
}
if !actors.isEmpty {
Text("CAST")
L10n.cast.text
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.primary)
@ -135,7 +135,7 @@ struct SeriesItemView: View {
}
}.padding(.top, 50)
if !viewModel.seasons.isEmpty {
Text("Seasons")
L10n.seasons.text
.font(.headline)
.fontWeight(.semibold)
ScrollView(.horizontal) {
@ -153,7 +153,7 @@ struct SeriesItemView: View {
}
if !viewModel.similarItems.isEmpty {
Text("More Like This")
L10n.moreLikeThis.text
.font(.headline)
.fontWeight(.semibold)
ScrollView(.horizontal) {

View File

@ -31,32 +31,32 @@ struct LibraryFilterView: View {
} else {
Form {
if viewModel.enabledFilterType.contains(.genre) {
MultiSelector(label: NSLocalizedString("Genres", comment: ""),
MultiSelector(label: L10n.genres,
options: viewModel.possibleGenres,
optionToString: { $0.name ?? "" },
selected: $viewModel.modifiedFilters.withGenres)
}
if viewModel.enabledFilterType.contains(.filter) {
MultiSelector(label: NSLocalizedString("Filters", comment: ""),
MultiSelector(label: L10n.filters,
options: viewModel.possibleItemFilters,
optionToString: { $0.localized },
selected: $viewModel.modifiedFilters.filters)
}
if viewModel.enabledFilterType.contains(.tag) {
MultiSelector(label: NSLocalizedString("Tags", comment: ""),
MultiSelector(label: L10n.tags,
options: viewModel.possibleTags,
optionToString: { $0 },
selected: $viewModel.modifiedFilters.tags)
}
if viewModel.enabledFilterType.contains(.sortBy) {
Picker(selection: $viewModel.selectedSortBy, label: Text("Sort by")) {
Picker(selection: $viewModel.selectedSortBy, label: L10n.sortBy.text) {
ForEach(viewModel.possibleSortBys, id: \.self) { so in
Text(so.localized).tag(so)
}
}
}
if viewModel.enabledFilterType.contains(.sortOrder) {
Picker(selection: $viewModel.selectedSortOrder, label: Text("Display order")) {
Picker(selection: $viewModel.selectedSortOrder, label: L10n.displayOrder.text) {
ForEach(viewModel.possibleSortOrders, id: \.self) { so in
Text(so.rawValue).tag(so)
}
@ -68,7 +68,7 @@ struct LibraryFilterView: View {
self.filters = viewModel.modifiedFilters
filterRouter.dismissCoordinator()
} label: {
Text("Reset")
L10n.reset.text
}
}
}
@ -86,7 +86,7 @@ struct LibraryFilterView: View {
self.filters = viewModel.modifiedFilters
filterRouter.dismissCoordinator()
} label: {
Text("Apply")
L10n.apply.text
}
}
}

View File

@ -44,7 +44,7 @@ struct LibrarySearchView: View {
var suggestionsListView: some View {
ScrollView {
LazyVStack(spacing: 8) {
Text("Suggestions")
L10n.suggestions.text
.font(.headline)
.fontWeight(.bold)
.foregroundColor(.primary)

View File

@ -88,7 +88,7 @@ struct LibraryView: View {
.ignoresSafeArea(.all)
} else {
VStack {
Text("No results.")
L10n.noResults.text
Button { } label: {
Text("Reload")
}

View File

@ -77,7 +77,7 @@ struct MovieLibrariesView: View {
.ignoresSafeArea(.all)
} else {
VStack {
Text("No results.")
L10n.noResults.text
Button {
print("movieLibraries reload")
} label: {

View File

@ -19,7 +19,7 @@ struct NextUpView: View {
var body: some View {
VStack(alignment: .leading) {
if items.count > 0 {
Text("Next Up")
L10n.nextUp.text
.font(.headline)
.fontWeight(.semibold)
.padding(.leading, 90)

View File

@ -72,7 +72,7 @@ struct ServerListView: View {
Button {
serverListRouter.route(to: \.connectToServer)
} label: {
Text("Connect")
L10n.connect.text
.bold()
.font(.callout)
}

View File

@ -22,7 +22,7 @@ struct SettingsView: View {
var body: some View {
Form {
Section(header: Text("Playback settings")) {
Section(header: L10n.playbackSettings.text) {
Picker("Default local quality", selection: $inNetworkStreamBitrate) {
ForEach(self.viewModel.bitrates, id: \.self) { bitrate in
Text(bitrate.name).tag(bitrate.value)
@ -36,7 +36,7 @@ struct SettingsView: View {
}
}
Section(header: Text("Accessibility")) {
Section(header: L10n.accessibility.text) {
Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles)
SearchablePicker(label: "Preferred subtitle language",
options: viewModel.langs,
@ -58,12 +58,12 @@ struct SettingsView: View {
Section(header: Text(SessionManager.main.currentLogin.server.name)) {
HStack {
Text("Signed in as \(SessionManager.main.currentLogin.user.username)").foregroundColor(.primary)
Text(L10n.signedInAsWithString(SessionManager.main.currentLogin.user.username)).foregroundColor(.primary)
Spacer()
Button {
SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil)
} label: {
Text("Switch user").font(.callout)
L10n.switchUser.text.font(.callout)
}
}
Button {

View File

@ -77,7 +77,7 @@ struct TVLibrariesView: View {
.ignoresSafeArea(.all)
} else {
VStack {
Text("No results.")
L10n.noResults.text
Button {
print("tvLibraries reload")
} label: {

View File

@ -20,11 +20,11 @@ struct UserSignInView: View {
Form {
Section {
TextField("Username", text: $username)
TextField(L10n.username, text: $username)
.disableAutocorrection(true)
.autocapitalization(.none)
SecureField("Password", text: $password)
SecureField(L10n.password, text: $password)
.disableAutocorrection(true)
.autocapitalization(.none)
@ -32,7 +32,7 @@ struct UserSignInView: View {
viewModel.login(username: username, password: password)
} label: {
HStack {
Text("Connect")
L10n.connect.text
Spacer()
if viewModel.isLoading {
ProgressView()

View File

@ -14,7 +14,7 @@ class AudioViewController: InfoTabViewController {
override func viewDidLoad() {
super.viewDidLoad()
tabBarItem.title = NSLocalizedString("Audio", comment: "")
tabBarItem.title = "Audio"
}

View File

@ -16,7 +16,7 @@ class MediaInfoViewController: InfoTabViewController {
override func viewDidLoad() {
super.viewDidLoad()
tabBarItem.title = NSLocalizedString("Info", comment: "")
tabBarItem.title = "Info"
}
func setMedia(item: BaseItemDto) {

View File

@ -14,7 +14,7 @@ class SubtitlesViewController: InfoTabViewController {
override func viewDidLoad() {
super.viewDidLoad()
tabBarItem.title = NSLocalizedString("Subtitles", comment: "")
tabBarItem.title = "Subtitles"
}

View File

@ -15,6 +15,7 @@
09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09389CC626819B4500AE350E /* VideoPlayerModel.swift */; };
09389CC826819B4600AE350E /* VideoPlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09389CC626819B4500AE350E /* VideoPlayerModel.swift */; };
0959A5FD2686D29800C7C9A9 /* VideoUpNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0959A5FC2686D29800C7C9A9 /* VideoUpNextView.swift */; };
363CADF08820D3B2055CF1D8 /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */; };
531069572684E7EE00CFFDBA /* InfoTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */; };
531069582684E7EE00CFFDBA /* MediaInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069512684E7EE00CFFDBA /* MediaInfoView.swift */; };
531069592684E7EE00CFFDBA /* SubtitlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069522684E7EE00CFFDBA /* SubtitlesView.swift */; };
@ -148,12 +149,11 @@
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; };
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; };
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E648263F725B00F67C6B /* MultiSelectorView.swift */; };
53EC6E1E267E80AC006DD26A /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */; };
53EC6E21267E80B1006DD26A /* Pods_JellyfinPlayer_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */; };
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; };
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */; };
53F866442687A45F00DCD1D7 /* PortraitItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F866432687A45F00DCD1D7 /* PortraitItemView.swift */; };
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; };
560CA59B3956A4CA13EDAC05 /* Pods_JellyfinPlayer_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */; };
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
621338932660107500A81A2A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
621338B32660A07800A81A2A /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338B22660A07800A81A2A /* LazyView.swift */; };
@ -175,6 +175,10 @@
625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
625CB5772678C34300530A6E /* ConnectToServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */; };
625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 625CB5792678C4A400530A6E /* ActivityIndicator */; };
6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; };
6264E88C273850380081A12A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6264E88B273850380081A12A /* Strings.swift */; };
6264E88D273850380081A12A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6264E88B273850380081A12A /* Strings.swift */; };
6264E88E273850380081A12A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6264E88B273850380081A12A /* Strings.swift */; };
62671DB327159C1800199D95 /* ItemCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */; };
6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; };
6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; };
@ -354,17 +358,17 @@
E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; };
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; };
E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; };
E1FCD09926C4F358007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; };
E1FCD09A26C4F35A007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; };
EABFD69FA6D5DBB248A494AA /* Pods_WidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
628B952B2670CABE0091AF3B /* PBXContainerItemProxy */ = {
6264E888273848760081A12A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5377CBE9263B596A003A4E83 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 628B951F2670CABD0091AF3B;
remoteInfo = WidgetExtensionExtension;
remoteInfo = WidgetExtension;
};
/* End PBXContainerItemProxy section */
@ -396,8 +400,10 @@
091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPBroadCastConnection.swift; sourceTree = "<group>"; };
09389CC626819B4500AE350E /* VideoPlayerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerModel.swift; sourceTree = "<group>"; };
0959A5FC2686D29800C7C9A9 /* VideoUpNextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUpNextView.swift; sourceTree = "<group>"; };
3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.debug.xcconfig"; sourceTree = "<group>"; };
3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = "<group>"; };
20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-WidgetExtension/Pods-WidgetExtension.debug.xcconfig"; sourceTree = "<group>"; };
4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoTabBarViewController.swift; sourceTree = "<group>"; };
531069512684E7EE00CFFDBA /* MediaInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaInfoView.swift; sourceTree = "<group>"; };
531069522684E7EE00CFFDBA /* SubtitlesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitlesView.swift; sourceTree = "<group>"; };
@ -495,6 +501,7 @@
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSettingsView.swift; sourceTree = "<group>"; };
53F866432687A45F00DCD1D7 /* PortraitItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemView.swift; sourceTree = "<group>"; };
53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = "<group>"; };
59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
@ -514,6 +521,7 @@
625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerViewModel.swift; sourceTree = "<group>"; };
625CB57B2678CE1000530A6E /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
625CB57D2678E81E00530A6E /* TVVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TVVLCKit.xcframework; path = Carthage/Build/TVVLCKit.xcframework; sourceTree = "<group>"; };
6264E88B273850380081A12A /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
6267B3D526710B8900A7371D /* CollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtensions.swift; sourceTree = "<group>"; };
6267B3D92671138200A7371D /* ImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageExtensions.swift; sourceTree = "<group>"; };
628B95202670CABD0091AF3B /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@ -540,8 +548,10 @@
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.debug.xcconfig"; sourceTree = "<group>"; };
86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = "<group>"; };
B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = "<group>"; };
C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesLibrariesCoordinator.swift; sourceTree = "<group>"; };
C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesViewModel.swift; sourceTree = "<group>"; };
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesView.swift; sourceTree = "<group>"; };
@ -551,8 +561,6 @@
C4BE076D2720FEA8003F4AD1 /* PortraitItemElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemElement.swift; sourceTree = "<group>"; };
C4E508172703E8190045C9AB /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = "<group>"; };
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinAPIError.swift; sourceTree = "<group>"; };
E11D224127378428003F9CB3 /* ServerDetailCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailCoordinator.swift; sourceTree = "<group>"; };
@ -609,7 +617,7 @@
E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerJumpLength.swift; sourceTree = "<group>"; };
E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = "<group>"; };
EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-WidgetExtension/Pods-WidgetExtension.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -619,7 +627,6 @@
files = (
53649AAF269CFAF600A2D8B7 /* Puppy in Frameworks */,
E1218C9E271A2CD600EA0737 /* CombineExt in Frameworks */,
53EC6E1E267E80AC006DD26A /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */,
E1218CA0271A2CF200EA0737 /* Nuke in Frameworks */,
6220D0C926D63F3700B8E046 /* Stinsen in Frameworks */,
53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */,
@ -630,6 +637,7 @@
E13DD3CD27164CA7009D4DAF /* CoreStore in Frameworks */,
E12186DE2718F1C50010884C /* Defaults in Frameworks */,
53ABFDED26799D7700886593 /* ActivityIndicator in Frameworks */,
363CADF08820D3B2055CF1D8 /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -641,7 +649,6 @@
53649AAD269CFAEA00A2D8B7 /* Puppy in Frameworks */,
62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */,
E1A99999271A3429008E78C0 /* SwiftUICollection in Frameworks */,
53EC6E21267E80B1006DD26A /* Pods_JellyfinPlayer_iOS.framework in Frameworks */,
E1218C9A271A26BA00EA0737 /* Nuke in Frameworks */,
E1B6DCEA271A23880015B715 /* SwiftyJSON in Frameworks */,
53352571265EA0A0006CCA86 /* Introspect in Frameworks */,
@ -649,6 +656,7 @@
625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */,
E1B6DCE8271A23780015B715 /* CombineExt in Frameworks */,
53A431BD266B0FF20016769F /* JellyfinAPI in Frameworks */,
560CA59B3956A4CA13EDAC05 /* Pods_JellyfinPlayer_iOS.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -664,6 +672,7 @@
E13DD3CF27164E1F009D4DAF /* CoreStore in Frameworks */,
628B95352670CAEA0091AF3B /* JellyfinAPI in Frameworks */,
E1218C9C271A26C400EA0737 /* Nuke in Frameworks */,
EABFD69FA6D5DBB248A494AA /* Pods_WidgetExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -794,6 +803,7 @@
535870752669D60C00D05A09 /* Shared */ = {
isa = PBXGroup;
children = (
6286F09F271C0AA500C40ED5 /* Generated */,
62C29E9D26D0FE5900C1D2E7 /* Coordinators */,
E1FCD08E26C466F3007C8DCF /* Errors */,
621338912660106C00A81A2A /* Extensions */,
@ -1029,8 +1039,9 @@
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */,
628B95212670CABD0091AF3B /* WidgetKit.framework */,
628B95232670CABD0091AF3B /* SwiftUI.framework */,
3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */,
EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */,
86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */,
4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */,
59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -1062,6 +1073,14 @@
path = Extensions;
sourceTree = "<group>";
};
6286F09F271C0AA500C40ED5 /* Generated */ = {
isa = PBXGroup;
children = (
6264E88B273850380081A12A /* Strings.swift */,
);
path = Generated;
sourceTree = "<group>";
};
628B95252670CABD0091AF3B /* WidgetExtension */ = {
isa = PBXGroup;
children = (
@ -1128,10 +1147,12 @@
C78797A232E2B8774099D1E9 /* Pods */ = {
isa = PBXGroup;
children = (
3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */,
BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */,
DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */,
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */,
772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */,
14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */,
B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */,
20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */,
4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */,
FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -1330,11 +1351,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 535870712669D21700D05A09 /* Build configuration list for PBXNativeTarget "JellyfinPlayer tvOS" */;
buildPhases = (
E7370E1AA68C6CB254E46F2C /* [CP] Check Pods Manifest.lock */,
3D0F2756C71CDF6B9EEBD4E0 /* [CP] Check Pods Manifest.lock */,
6286F0A3271C0ABA00C40ED5 /* R.swift */,
5358705C2669D21600D05A09 /* Sources */,
5358705D2669D21600D05A09 /* Frameworks */,
5358705E2669D21600D05A09 /* Resources */,
6AB6F1DD2C8AD942F71C8A32 /* [CP] Embed Pods Frameworks */,
879C22C1CCC48E68C86E904C /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -1362,19 +1384,20 @@
isa = PBXNativeTarget;
buildConfigurationList = 5377CC1B263B596B003A4E83 /* Build configuration list for PBXNativeTarget "JellyfinPlayer iOS" */;
buildPhases = (
6435C3C2E610FE34AD537AC1 /* [CP] Check Pods Manifest.lock */,
1C7487D3432E90546DA855B5 /* [CP] Check Pods Manifest.lock */,
6286F09E271C093000C40ED5 /* R.swift */,
5377CBED263B596A003A4E83 /* Sources */,
5377CBEE263B596A003A4E83 /* Frameworks */,
5377CBEF263B596A003A4E83 /* Resources */,
5302F8322658B74800647A2E /* CopyFiles */,
628B95312670CABE0091AF3B /* Embed App Extensions */,
E8DDF21F62DFCE8CE76666BA /* [CP] Embed Pods Frameworks */,
83FD120CA10FD0E91DAD83C9 /* [CP] Copy Pods Resources */,
8D1E0C963DCE6C6F328B3EBB /* [CP] Embed Pods Frameworks */,
DB8CA7C37DF78BEDCE4E37C1 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
628B952C2670CABE0091AF3B /* PBXTargetDependency */,
6264E889273848760081A12A /* PBXTargetDependency */,
);
name = "JellyfinPlayer iOS";
packageProductDependencies = (
@ -1398,6 +1421,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 628B952E2670CABE0091AF3B /* Build configuration list for PBXNativeTarget "WidgetExtension" */;
buildPhases = (
D4D3981ADF75BCD341D590C0 /* [CP] Check Pods Manifest.lock */,
628B951C2670CABD0091AF3B /* Sources */,
628B951D2670CABD0091AF3B /* Frameworks */,
628B951E2670CABD0091AF3B /* Resources */,
@ -1572,7 +1596,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
6435C3C2E610FE34AD537AC1 /* [CP] Check Pods Manifest.lock */ = {
1C7487D3432E90546DA855B5 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1594,41 +1618,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6AB6F1DD2C8AD942F71C8A32 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
83FD120CA10FD0E91DAD83C9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources.sh\"\n";
showEnvVarsInLog = 0;
};
E7370E1AA68C6CB254E46F2C /* [CP] Check Pods Manifest.lock */ = {
3D0F2756C71CDF6B9EEBD4E0 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1650,7 +1640,60 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
E8DDF21F62DFCE8CE76666BA /* [CP] Embed Pods Frameworks */ = {
6286F09E271C093000C40ED5 /* R.swift */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = R.swift;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PROJECT_DIR}/bin/swiftgen\"\n";
};
6286F0A3271C0ABA00C40ED5 /* R.swift */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = R.swift;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PROJECT_DIR}/bin/swiftgen\"\n";
};
879C22C1CCC48E68C86E904C /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
8D1E0C963DCE6C6F328B3EBB /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1667,6 +1710,45 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D4D3981ADF75BCD341D590C0 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-WidgetExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DB8CA7C37DF78BEDCE4E37C1 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -1770,6 +1852,7 @@
5321753E2671DE9C005491E6 /* Typings.swift in Sources */,
E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */,
53ABFDEB2679753200886593 /* ConnectToServerView.swift in Sources */,
6264E88D273850380081A12A /* Strings.swift in Sources */,
536D3D76267BA9BB0004248C /* MainTabViewModel.swift in Sources */,
E193D5512719432400900D82 /* ServerDetailViewModel.swift in Sources */,
5310695C2684E7EE00CFFDBA /* VideoPlayerViewController.swift in Sources */,
@ -1841,6 +1924,7 @@
E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */,
53892770263C25230035E14B /* NextUpView.swift in Sources */,
E11D224227378428003F9CB3 /* ServerDetailCoordinator.swift in Sources */,
6264E88C273850380081A12A /* Strings.swift in Sources */,
C4BE0766271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */,
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */,
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
@ -1929,6 +2013,7 @@
files = (
53649AB3269D3F5B00A2D8B7 /* LogManager.swift in Sources */,
E13DD3CB27164BA8009D4DAF /* UIDeviceExtensions.swift in Sources */,
6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */,
E19169D0272514760085832A /* HTTPScheme.swift in Sources */,
6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */,
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */,
@ -1936,6 +2021,7 @@
E1AD105926D9A543003E4A08 /* LazyView.swift in Sources */,
E11B1B6E2718CDBA006DA3E8 /* JellyfinAPIError.swift in Sources */,
628B95372670CB800091AF3B /* JellyfinWidget.swift in Sources */,
6264E88E273850380081A12A /* Strings.swift in Sources */,
E1AD105426D97161003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
E1FCD09A26C4F35A007C8DCF /* ErrorMessage.swift in Sources */,
628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */,
@ -1943,7 +2029,6 @@
E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */,
6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */,
E13DD3D7271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
E1FCD09926C4F358007C8DCF /* NetworkError.swift in Sources */,
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */,
E13DD3CA27164B80009D4DAF /* SwiftfinStore.swift in Sources */,
62EC353226766849000E9F2D /* SessionManager.swift in Sources */,
@ -1955,10 +2040,10 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
628B952C2670CABE0091AF3B /* PBXTargetDependency */ = {
6264E889273848760081A12A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 628B951F2670CABD0091AF3B /* WidgetExtension */;
targetProxy = 628B952B2670CABE0091AF3B /* PBXContainerItemProxy */;
targetProxy = 6264E888273848760081A12A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@ -2096,7 +2181,7 @@
/* Begin XCBuildConfiguration section */
535870722669D21700D05A09 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */;
baseConfigurationReference = B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "Dev App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@ -2126,7 +2211,7 @@
};
535870732669D21700D05A09 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */;
baseConfigurationReference = 20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@ -2276,7 +2361,7 @@
};
5377CC1C263B596B003A4E83 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */;
baseConfigurationReference = 772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Dev";
@ -2312,7 +2397,7 @@
};
5377CC1D263B596B003A4E83 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */;
baseConfigurationReference = 14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@ -2348,6 +2433,7 @@
};
628B952F2670CABE0091AF3B /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
@ -2374,6 +2460,7 @@
};
628B95302670CABE0091AF3B /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DisableBuildSystemDeprecationDiagnostic</key>
<true/>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -13,10 +13,10 @@ import UIKit
class AppDelegate: NSObject, UIApplicationDelegate {
static var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// Lazily initialize datastack
let _ = SwiftfinStore.dataStack
_ = SwiftfinStore.dataStack
return true
}

View File

@ -77,8 +77,7 @@ extension AppURLHandler {
// /Users/{UserID}/Items/{ItemID}
if url.pathComponents[safe: 2]?.lowercased() == "items",
let userID = url.pathComponents[safe: 1],
let itemID = url.pathComponents[safe: 3]
{
let itemID = url.pathComponents[safe: 3] {
// It would be nice if the ItemViewModel could be initialized to id later.
getItem(userID: userID, itemID: itemID) { item in
guard let item = item else { return }

View File

@ -75,7 +75,7 @@ struct PortraitItemView: View {
.font(.caption)
.fontWeight(.medium)
} else {
Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))")
Text(L10n.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0)))
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.medium)

View File

@ -23,7 +23,7 @@ struct BasicAppSettingsView: View {
var body: some View {
Form {
Section {
Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) {
Picker(L10n.appearance, selection: $appAppearance) {
ForEach(self.viewModel.appearances, id: \.self) { appearance in
Text(appearance.localizedName).tag(appearance.rawValue)
}
@ -31,7 +31,7 @@ struct BasicAppSettingsView: View {
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = appAppearance.style
})
} header: {
Text("Accessibility")
L10n.accessibility.text
}
Section {
@ -47,15 +47,15 @@ struct BasicAppSettingsView: View {
Button {
resetTapped = true
} label: {
Text("Reset")
L10n.reset.text
}
}
.alert("Reset", isPresented: $resetTapped, actions: {
.alert(L10n.reset, isPresented: $resetTapped, actions: {
Button(role: .destructive) {
viewModel.reset()
basicAppSettingsRouter.dismissCoordinator()
} label: {
Text("Reset")
L10n.reset.text
}
})
.navigationBarTitle("Settings", displayMode: .inline)

View File

@ -20,7 +20,7 @@ struct ConnectToServerView: View {
var body: some View {
List {
Section {
TextField(NSLocalizedString("Server URL", comment: ""), text: $uri)
TextField(L10n.serverURL, text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
.keyboardType(.URL)
@ -40,7 +40,7 @@ struct ConnectToServerView: View {
Button {
viewModel.connectToServer(uri: uri)
} label: {
Text("Connect")
L10n.connect.text
}
.disabled(uri.isEmpty)
}
@ -88,7 +88,7 @@ struct ConnectToServerView: View {
}
} header: {
HStack {
Text("Local Servers")
L10n.localServers.text
Spacer()
Button {
@ -114,7 +114,7 @@ struct ConnectToServerView: View {
}),
secondaryButton: .cancel())
}
.navigationTitle("Connect")
.navigationTitle(L10n.connect)
.onAppear {
viewModel.discoverServers()
AppURLHandler.shared.appURLState = .allowedInLogin

View File

@ -34,7 +34,7 @@ struct HomeView: View {
ForEach(viewModel.libraries, id: \.self) { library in
HStack {
Text("Latest \(library.name ?? "")")
Text(L10n.latestWithString(library.name ?? ""))
.font(.title2)
.fontWeight(.bold)
Spacer()
@ -45,7 +45,7 @@ struct HomeView: View {
title: library.name ?? ""))
} label: {
HStack {
Text("See All").font(.subheadline).fontWeight(.bold)
L10n.seeAll.text.font(.subheadline).fontWeight(.bold)
Image(systemName: "chevron.right").font(Font.subheadline.bold())
}
}
@ -70,7 +70,7 @@ struct HomeView: View {
var body: some View {
innerBody
.navigationTitle(NSLocalizedString("Home", comment: ""))
.navigationTitle(L10n.home)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {

View File

@ -28,7 +28,7 @@ struct ItemNavigationView: View {
}
}
fileprivate struct ItemView: View {
private struct ItemView: View {
@EnvironmentObject var itemRouter: ItemCoordinator.Router
@State private var videoIsLoading: Bool = false // This variable is only changed by the underlying VLC view.

View File

@ -29,7 +29,7 @@ struct ItemViewBody: View {
PortraitImageHStackView(items: seriesViewModel.seasons,
maxWidth: 150,
topBarView: {
Text("Seasons")
L10n.seasons.text
.font(.callout)
.fontWeight(.semibold)
.padding(.top, 3)
@ -41,7 +41,7 @@ struct ItemViewBody: View {
// MARK: Genres
PillHStackView(title: "Genres",
PillHStackView(title: L10n.genres,
items: viewModel.item.genreItems ?? [],
selectedAction: { genre in
itemRouter.route(to: \.library, (viewModel: .init(genre: genre), title: genre.title))
@ -50,7 +50,7 @@ struct ItemViewBody: View {
// MARK: Studios
if let studios = viewModel.item.studios {
PillHStackView(title: "Studios",
PillHStackView(title: L10n.studios,
items: studios) { studio in
itemRouter.route(to: \.library, (viewModel: .init(studio: studio), title: studio.name ?? ""))
}
@ -79,7 +79,7 @@ struct ItemViewBody: View {
PortraitImageHStackView(items: viewModel.similarItems,
maxWidth: 150,
topBarView: {
Text("More Like This")
L10n.moreLikeThis.text
.font(.callout)
.fontWeight(.semibold)
.padding(.top, 3)

View File

@ -31,32 +31,32 @@ struct LibraryFilterView: View {
} else {
Form {
if viewModel.enabledFilterType.contains(.genre) {
MultiSelector(label: NSLocalizedString("Genres", comment: ""),
MultiSelector(label: L10n.genres,
options: viewModel.possibleGenres,
optionToString: { $0.name ?? "" },
selected: $viewModel.modifiedFilters.withGenres)
}
if viewModel.enabledFilterType.contains(.filter) {
MultiSelector(label: NSLocalizedString("Filters", comment: ""),
MultiSelector(label: L10n.filters,
options: viewModel.possibleItemFilters,
optionToString: { $0.localized },
selected: $viewModel.modifiedFilters.filters)
}
if viewModel.enabledFilterType.contains(.tag) {
MultiSelector(label: NSLocalizedString("Tags", comment: ""),
MultiSelector(label: L10n.tags,
options: viewModel.possibleTags,
optionToString: { $0 },
selected: $viewModel.modifiedFilters.tags)
}
if viewModel.enabledFilterType.contains(.sortBy) {
Picker(selection: $viewModel.selectedSortBy, label: Text("Sort by")) {
Picker(selection: $viewModel.selectedSortBy, label: L10n.sortBy.text) {
ForEach(viewModel.possibleSortBys, id: \.self) { so in
Text(so.localized).tag(so)
}
}
}
if viewModel.enabledFilterType.contains(.sortOrder) {
Picker(selection: $viewModel.selectedSortOrder, label: Text("Display order")) {
Picker(selection: $viewModel.selectedSortOrder, label: L10n.displayOrder.text) {
ForEach(viewModel.possibleSortOrders, id: \.self) { so in
Text(so.rawValue).tag(so)
}
@ -68,11 +68,11 @@ struct LibraryFilterView: View {
self.filters = viewModel.modifiedFilters
filterRouter.dismissCoordinator()
} label: {
Text("Reset")
L10n.reset.text
}
}
}
.navigationBarTitle(NSLocalizedString("Filter Results", comment: ""), displayMode: .inline)
.navigationBarTitle(L10n.filterResults, displayMode: .inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button {
@ -87,7 +87,7 @@ struct LibraryFilterView: View {
self.filters = viewModel.modifiedFilters
filterRouter.dismissCoordinator()
} label: {
Text("Apply")
L10n.apply.text
}
}
}

View File

@ -23,7 +23,7 @@ struct LibraryListView: View {
ZStack {
HStack {
Spacer()
Text("Your Favorites")
L10n.yourFavorites.text
.foregroundColor(.black)
.font(.subheadline)
.fontWeight(.semibold)
@ -39,12 +39,12 @@ struct LibraryListView: View {
.padding(.bottom, 5)
NavigationLink(destination: LazyView {
Text("WIP")
L10n.wip.text
}) {
ZStack {
HStack {
Spacer()
Text("All Genres")
L10n.allGenres.text
.foregroundColor(.black)
.font(.subheadline)
.fontWeight(.semibold)
@ -98,7 +98,7 @@ struct LibraryListView: View {
.padding(.trailing, 16)
.padding(.top, 8)
}
.navigationTitle(NSLocalizedString("All Media", comment: ""))
.navigationTitle(L10n.allMedia)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {

View File

@ -46,7 +46,7 @@ struct LibrarySearchView: View {
var suggestionsListView: some View {
ScrollView {
LazyVStack(spacing: 8) {
Text("Suggestions")
L10n.suggestions.text
.font(.headline)
.fontWeight(.bold)
.foregroundColor(.primary)

View File

@ -55,7 +55,7 @@ struct LibraryView: View {
Image(systemName: "chevron.left")
.font(.system(size: 25))
}.disabled(!viewModel.hasPreviousPage)
Text("Page \(String(viewModel.currentPage + 1)) of \(String(viewModel.totalPages))")
Text(L10n.pageOfWithNumbers(String(viewModel.currentPage + 1), String(viewModel.totalPages)))
.font(.subheadline)
.fontWeight(.medium)
Button {
@ -72,7 +72,7 @@ struct LibraryView: View {
}
}
} else {
Text("No results.")
L10n.noResults.text
}
}
.navigationBarTitle(title, displayMode: .inline)

View File

@ -32,7 +32,7 @@ struct LoadingView<Content>: View where Content: View {
// indicator, with some text underneath showing what we are doing
HStack {
ProgressView()
Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60)
Text(text ?? L10n.loading).fontWeight(.semibold).font(.callout).offset(x: 60)
Spacer()
}
.padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 10))
@ -70,7 +70,7 @@ struct LoadingViewNoBlur<Content>: View where Content: View {
// indicator, with some text underneath showing what we are doing
HStack {
ProgressView()
Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60)
Text(text ?? L10n.loading).fontWeight(.semibold).font(.callout).offset(x: 60)
Spacer()
}
.padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 10))

View File

@ -17,7 +17,7 @@ struct NextUpView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Next Up")
L10n.nextUp.text
.font(.title2)
.fontWeight(.bold)
.padding(.leading, 16)

View File

@ -81,7 +81,7 @@ struct ServerListView: View {
.padding(.horizontal, 30)
.padding([.top, .bottom], 20)
Text("Connect")
L10n.connect.text
.foregroundColor(Color.white)
.bold()
}

View File

@ -108,7 +108,7 @@ struct SettingsView: View {
}
}
Section(header: Text("Accessibility")) {
Section(header: L10n.accessibility.text) {
Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles)
SearchablePicker(label: "Preferred subtitle language",
options: viewModel.langs,
@ -129,7 +129,7 @@ struct SettingsView: View {
.auto
},
set: { autoSelectAudioLangcode = $0.isoCode }))
Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) {
Picker(L10n.appearance, selection: $appAppearance) {
ForEach(self.viewModel.appearances, id: \.self) { appearance in
Text(appearance.localizedName).tag(appearance.rawValue)
}

View File

@ -20,11 +20,11 @@ struct UserSignInView: View {
Form {
Section {
TextField("Username", text: $username)
TextField(L10n.username, text: $username)
.disableAutocorrection(true)
.autocapitalization(.none)
SecureField("Password", text: $password)
SecureField(L10n.password, text: $password)
.disableAutocorrection(true)
.autocapitalization(.none)

View File

@ -153,7 +153,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
sendProgressReport(eventName: "unpause")
} else {
sendJellyfinCommand(command: "Seek", options: [
"position": Int(secondsScrubbedTo),
"position": Int(secondsScrubbedTo)
])
}
}
@ -421,7 +421,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
if manifest.type == "Movie" {
titleLabel.text = manifest.name ?? ""
} else {
titleLabel.text = "S\(String(manifest.parentIndexNumber ?? 0)):E\(String(manifest.indexNumber ?? 0))\(manifest.name ?? "")"
titleLabel.text = "\(L10n.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0)))\(manifest.name ?? "")"
setupNextUpView()
upNextViewModel.delegate = self
@ -664,8 +664,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
subtitleTrackArray.forEach { subtitle in
if Defaults[.isAutoSelectSubtitles] {
if Defaults[.autoSelectSubtitlesLangCode] == "Auto",
subtitle.languageCode.contains(Locale.current.languageCode ?? "")
{
subtitle.languageCode.contains(Locale.current.languageCode ?? "") {
selectedCaptionTrack = subtitle.id
mediaPlayer.currentVideoSubTitleIndex = subtitle.id
} else if subtitle.languageCode.contains(Defaults[.autoSelectSubtitlesLangCode]) {
@ -816,7 +815,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
shouldShowLoadingScreen = true
videoControlsView.isHidden = true
titleLabel.text = "S\(String(manifest.parentIndexNumber ?? 0)):E\(String(manifest.indexNumber ?? 0))\(manifest.name ?? "")"
titleLabel.text = "\(L10n.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0)))\(manifest.name ?? "")"
setupMediaPlayer()
getNextEpisode()
@ -854,7 +853,7 @@ extension PlayerViewController: GCKGenericChannelDelegate {
if hasSentRemoteSeek == false {
hasSentRemoteSeek = true
sendJellyfinCommand(command: "Seek", options: [
"position": Int(Float(manifest.runTimeTicks! / 10_000_000) * mediaPlayer.position),
"position": Int(Float(manifest.runTimeTicks! / 10_000_000) * mediaPlayer.position)
])
}
}
@ -880,7 +879,7 @@ extension PlayerViewController: GCKGenericChannelDelegate {
"serverId": SessionManager.main.currentLogin.server.id,
"serverVersion": "10.8.0",
"receiverName": castSessionManager.currentCastSession!.device.friendlyName!,
"subtitleBurnIn": false,
"subtitleBurnIn": false
]
let jsonData = JSON(payload)
@ -935,8 +934,8 @@ extension PlayerViewController: GCKSessionManagerListener {
"Name": manifest.name!,
"Type": manifest.type!,
"MediaType": manifest.mediaType!,
"IsFolder": manifest.isFolder!,
]],
"IsFolder": manifest.isFolder!
]]
]
sendJellyfinCommand(command: "PlayNow", options: playNowOptions)
}
@ -1104,8 +1103,7 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
typealias UIViewControllerType = PlayerViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<VLCPlayerWithControls>) -> VLCPlayerWithControls
.UIViewControllerType
{
.UIViewControllerType {
let storyboard = UIStoryboard(name: "VideoPlayer", bundle: nil)
let customViewController = storyboard.instantiateViewController(withIdentifier: "VideoPlayer") as! PlayerViewController
customViewController.manifest = item

View File

@ -56,7 +56,7 @@ struct VideoPlayerCastDeviceSelector: View {
delegate?.castPopoverDismissed()
} label: {
HStack {
Text("Connect")
L10n.connect.text
.font(.caption)
.fontWeight(.medium)
Image(systemName: "bonjour")
@ -66,14 +66,14 @@ struct VideoPlayerCastDeviceSelector: View {
}
}
} else {
Text("No Cast devices found..")
L10n.noCastdevicesfound.text
.foregroundColor(.secondary)
.font(.subheadline)
.fontWeight(.medium)
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(NSLocalizedString("Select Cast Destination", comment: ""))
.navigationTitle(L10n.selectCastDestination)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
if UIDevice.current.userInterfaceIdiom == .phone {
@ -82,7 +82,7 @@ struct VideoPlayerCastDeviceSelector: View {
} label: {
HStack {
Image(systemName: "chevron.left")
Text("Back").font(.callout)
L10n.back.text.font(.callout)
}
}
}

View File

@ -40,7 +40,7 @@ struct VideoPlayerSettings: View {
var body: some View {
Form {
Picker(NSLocalizedString("Closed Captions", comment: ""), selection: $captionTrack) {
Picker(L10n.closedCaptions, selection: $captionTrack) {
ForEach(delegate.subtitleTrackArray, id: \.id) { caption in
Text(caption.name).tag(caption.id)
}
@ -48,14 +48,14 @@ struct VideoPlayerSettings: View {
.onChange(of: captionTrack) { track in
self.delegate.subtitleTrackChanged(newTrackID: track)
}
Picker(NSLocalizedString("Audio Track", comment: ""), selection: $audioTrack) {
Picker(L10n.audioTrack, selection: $audioTrack) {
ForEach(delegate.audioTrackArray, id: \.id) { caption in
Text(caption.name).tag(caption.id).lineLimit(1)
}
}.onChange(of: audioTrack) { track in
self.delegate.audioTrackChanged(newTrackID: track)
}
Picker(NSLocalizedString("Playback Speed", comment: ""), selection: $playbackSpeedSelection) {
Picker(L10n.playbackSpeed, selection: $playbackSpeedSelection) {
ForEach(delegate.playbackSpeeds.indices, id: \.self) { speedIndex in
let speed = delegate.playbackSpeeds[speedIndex]
Text("\(String(speed))x").tag(speedIndex)
@ -65,7 +65,7 @@ struct VideoPlayerSettings: View {
self.delegate.playbackSpeedChanged(index: index)
})
}.navigationBarTitleDisplayMode(.inline)
.navigationTitle(NSLocalizedString("Audio & Captions", comment: ""))
.navigationTitle(L10n.audioAndCaptions)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
if UIDevice.current.userInterfaceIdiom == .phone {
@ -74,7 +74,7 @@ struct VideoPlayerSettings: View {
} label: {
HStack {
Image(systemName: "chevron.left")
Text("Back").font(.callout)
L10n.back.text.font(.callout)
}
}
}

View File

@ -32,7 +32,7 @@ struct VideoUpNextView: View {
} label: {
HStack {
VStack {
Text("Play Next")
L10n.playNext.text
.foregroundColor(.white)
.font(.subheadline)
.fontWeight(.semibold)

13
Podfile
View File

@ -1,11 +1,20 @@
use_frameworks!
inhibit_all_warnings!
def shared_pods
pod 'SwiftGen'
end
target 'JellyfinPlayer iOS' do
platform :ios, '14.0'
use_frameworks!
shared_pods
pod 'google-cast-sdk'
pod 'MobileVLCKit'
end
target 'JellyfinPlayer tvOS' do
platform :tvos, '14.0'
use_frameworks!
shared_pods
pod 'TVVLCKit'
end
target 'WidgetExtension' do
shared_pods
end

View File

@ -26,7 +26,7 @@ final class MainTabCoordinator: TabCoordinatable {
@ViewBuilder func makeHomeTab(isActive: Bool) -> some View {
Image(systemName: "house")
Text("Home")
L10n.home.text
}
func makeAllMedia() -> NavigationViewCoordinator<LibraryListCoordinator> {
@ -35,7 +35,7 @@ final class MainTabCoordinator: TabCoordinatable {
@ViewBuilder func makeAllMediaTab(isActive: Bool) -> some View {
Image(systemName: "folder")
Text("All Media")
L10n.allMedia.text
}
@ViewBuilder func customize(_ view: AnyView) -> some View {

View File

@ -33,7 +33,7 @@ final class MainTabCoordinator: TabCoordinatable {
@ViewBuilder func makeHomeTab(isActive: Bool) -> some View {
HStack {
Image(systemName: "house")
Text("Home")
L10n.home.text
}
}

View File

@ -85,13 +85,13 @@ enum NetworkError: Error {
logMessage = "Cannot connect to host."
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: err._code,
title: "Error",
title: L10n.error,
displayMessage: displayMessage,
logConstructor: logConstructor)
default:
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: err._code,
title: "Error",
title: L10n.error,
displayMessage: displayMessage,
logConstructor: logConstructor)
}
@ -111,7 +111,7 @@ enum NetworkError: Error {
case .error:
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: 0,
title: "Error",
title: L10n.error,
displayMessage: displayMessage,
logConstructor: logConstructor)
}
@ -140,7 +140,7 @@ enum NetworkError: Error {
default:
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: code,
title: "Error",
title: L10n.error,
displayMessage: displayMessage,
logConstructor: logConstructor)
}

View File

@ -80,7 +80,7 @@ public extension BaseItemDto {
func getEpisodeLocator() -> String? {
if let seasonNo = parentIndexNumber, let episodeNo = indexNumber {
return "S\(seasonNo):E\(episodeNo)"
return L10n.seasonAndEpisode(String(seasonNo), String(episodeNo))
}
return nil
}

View File

@ -29,7 +29,6 @@ extension BaseItemPerson {
return imageBlurHashes?.primary?[imgTag] ?? "001fC^"
}
// MARK: First Role
// Jellyfin will grab all roles the person played in the show which makes the role

View File

@ -6,6 +6,7 @@
*/
import Foundation
import SwiftUI
extension String {
func removeRegexMatches(pattern: String, replaceWith: String = "") -> String {
@ -31,4 +32,8 @@ extension String {
return "\(padString)\(self)"
}
var text: Text {
Text(self)
}
}

View File

@ -0,0 +1,184 @@
// swiftlint:disable all
// Generated using SwiftGen https://github.com/SwiftGen/SwiftGen
import Foundation
// swiftlint:disable superfluous_disable_command file_length implicit_return
// MARK: - Strings
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
internal enum L10n {
/// Accessibility
internal static let accessibility = L10n.tr("Localizable", "accessibility")
/// All Genres
internal static let allGenres = L10n.tr("Localizable", "allGenres")
/// All Media
internal static let allMedia = L10n.tr("Localizable", "allMedia")
/// Appearance
internal static let appearance = L10n.tr("Localizable", "appearance")
/// Apply
internal static let apply = L10n.tr("Localizable", "apply")
/// Audio & Captions
internal static let audioAndCaptions = L10n.tr("Localizable", "audioAndCaptions")
/// Audio Track
internal static let audioTrack = L10n.tr("Localizable", "audioTrack")
/// Back
internal static let back = L10n.tr("Localizable", "back")
/// CAST
internal static let cast = L10n.tr("Localizable", "cast")
/// Change Server
internal static let changeServer = L10n.tr("Localizable", "changeServer")
/// Closed Captions
internal static let closedCaptions = L10n.tr("Localizable", "closedCaptions")
/// Connect
internal static let connect = L10n.tr("Localizable", "connect")
/// Connect Manually
internal static let connectManually = L10n.tr("Localizable", "connectManually")
/// Connect to Jellyfin
internal static let connectToJellyfin = L10n.tr("Localizable", "connectToJellyfin")
/// Connect to Server
internal static let connectToServer = L10n.tr("Localizable", "connectToServer")
/// Continue Watching
internal static let continueWatching = L10n.tr("Localizable", "continueWatching")
/// Dark
internal static let dark = L10n.tr("Localizable", "dark")
/// DIRECTOR
internal static let director = L10n.tr("Localizable", "director")
/// Discovered Servers
internal static let discoveredServers = L10n.tr("Localizable", "discoveredServers")
/// Display order
internal static let displayOrder = L10n.tr("Localizable", "displayOrder")
/// Empty Next Up
internal static let emptyNextUp = L10n.tr("Localizable", "emptyNextUp")
/// Episodes
internal static let episodes = L10n.tr("Localizable", "episodes")
/// Error
internal static let error = L10n.tr("Localizable", "error")
/// Filter Results
internal static let filterResults = L10n.tr("Localizable", "filterResults")
/// Filters
internal static let filters = L10n.tr("Localizable", "filters")
/// Genres
internal static let genres = L10n.tr("Localizable", "genres")
/// Home
internal static let home = L10n.tr("Localizable", "home")
/// Latest %@
internal static func latestWithString(_ p1: Any) -> String {
return L10n.tr("Localizable", "latestWithString", String(describing: p1))
}
/// Library
internal static let library = L10n.tr("Localizable", "library")
/// Light
internal static let light = L10n.tr("Localizable", "light")
/// Loading
internal static let loading = L10n.tr("Localizable", "loading")
/// Local Servers
internal static let localServers = L10n.tr("Localizable", "localServers")
/// Login
internal static let login = L10n.tr("Localizable", "login")
/// Login to %@
internal static func loginToWithString(_ p1: Any) -> String {
return L10n.tr("Localizable", "loginToWithString", String(describing: p1))
}
/// More Like This
internal static let moreLikeThis = L10n.tr("Localizable", "moreLikeThis")
/// Next Up
internal static let nextUp = L10n.tr("Localizable", "nextUp")
/// No Cast devices found..
internal static let noCastdevicesfound = L10n.tr("Localizable", "noCastdevicesfound")
/// No results.
internal static let noResults = L10n.tr("Localizable", "noResults")
/// Type: %@ not implemented yet :(
internal static func notImplementedYetWithType(_ p1: Any) -> String {
return L10n.tr("Localizable", "notImplementedYetWithType", String(describing: p1))
}
/// Ok
internal static let ok = L10n.tr("Localizable", "ok")
/// Other User
internal static let otherUser = L10n.tr("Localizable", "otherUser")
/// Page %1$@ of %2$@
internal static func pageOfWithNumbers(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "pageOfWithNumbers", String(describing: p1), String(describing: p2))
}
/// Password
internal static let password = L10n.tr("Localizable", "password")
/// Play
internal static let play = L10n.tr("Localizable", "play")
/// Playback settings
internal static let playbackSettings = L10n.tr("Localizable", "playbackSettings")
/// Playback Speed
internal static let playbackSpeed = L10n.tr("Localizable", "playbackSpeed")
/// Play Next
internal static let playNext = L10n.tr("Localizable", "playNext")
/// Reset
internal static let reset = L10n.tr("Localizable", "reset")
/// Search...
internal static let search = L10n.tr("Localizable", "search")
/// S%1$@:E%2$@
internal static func seasonAndEpisode(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "seasonAndEpisode", String(describing: p1), String(describing: p2))
}
/// Seasons
internal static let seasons = L10n.tr("Localizable", "seasons")
/// See All
internal static let seeAll = L10n.tr("Localizable", "seeAll")
/// Select Cast Destination
internal static let selectCastDestination = L10n.tr("Localizable", "selectCastDestination")
/// Server Information
internal static let serverInformation = L10n.tr("Localizable", "serverInformation")
/// Server URL
internal static let serverURL = L10n.tr("Localizable", "serverURL")
/// Signed in as %@
internal static func signedInAsWithString(_ p1: Any) -> String {
return L10n.tr("Localizable", "signedInAsWithString", String(describing: p1))
}
/// Sort by
internal static let sortBy = L10n.tr("Localizable", "sortBy")
/// STUDIO
internal static let studio = L10n.tr("Localizable", "studio")
/// Studios
internal static let studios = L10n.tr("Localizable", "studios")
/// Suggestions
internal static let suggestions = L10n.tr("Localizable", "suggestions")
/// Switch user
internal static let switchUser = L10n.tr("Localizable", "switchUser")
/// System
internal static let system = L10n.tr("Localizable", "system")
/// Tags
internal static let tags = L10n.tr("Localizable", "tags")
/// Try again
internal static let tryAgain = L10n.tr("Localizable", "tryAgain")
/// Username
internal static let username = L10n.tr("Localizable", "username")
/// Who's watching?
internal static let whosWatching = L10n.tr("Localizable", "WhosWatching")
/// WIP
internal static let wip = L10n.tr("Localizable", "wip")
/// Your Favorites
internal static let yourFavorites = L10n.tr("Localizable", "yourFavorites")
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
// MARK: - Implementation Details
extension L10n {
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
let format = BundleToken.bundle.localizedString(forKey: key, value: nil, table: table)
return String(format: format, locale: Locale.current, arguments: args)
}
}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type

View File

@ -16,7 +16,14 @@ enum AppAppearance: String, CaseIterable, Defaults.Serializable {
case light
var localizedName: String {
return NSLocalizedString(self.rawValue.capitalized, comment: "")
switch self {
case .system:
return L10n.system
case .dark:
return L10n.dark
case .light:
return L10n.light
}
}
var style: UIUserInterfaceStyle {

View File

@ -22,7 +22,4 @@ struct DetailItem {
let baseItem: BaseItemDto
let type: DetailItemType
}

View File

@ -77,7 +77,7 @@ enum ItemType: String {
var localized: String {
switch self {
case .episode:
return "Episodes"
return L10n.episodes
case .movie:
return "Movies"
case .series:

View File

@ -35,7 +35,7 @@ class ItemViewModel: ViewModel {
}
func playButtonText() -> String {
return item.getItemProgressString() == "" ? "Play" : item.getItemProgressString()
return item.getItemProgressString() == "" ? L10n.play : item.getItemProgressString()
}
func getItemDisplayName() -> String {

View File

@ -64,7 +64,6 @@ final class LibraryViewModel: ViewModel {
self.columns = columns
super.init()
$filters
.sink(receiveValue: requestItems(with:))
.store(in: &cancellables)

View File

@ -24,8 +24,8 @@ final class SeasonItemViewModel: ItemViewModel {
}
override func playButtonText() -> String {
guard let playButtonItem = playButtonItem else { return "Play" }
guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return "Play" }
guard let playButtonItem = playButtonItem else { return L10n.play }
guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return L10n.play }
return episodeLocator
}

View File

@ -23,8 +23,8 @@ final class SeriesItemViewModel: ItemViewModel {
}
override func playButtonText() -> String {
guard let playButtonItem = playButtonItem else { return "Play" }
guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return "Play" }
guard let playButtonItem = playButtonItem else { return L10n.play }
guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return L10n.play }
return episodeLocator
}

View File

@ -16,7 +16,7 @@ struct SearchBar: View {
var body: some View {
HStack(spacing: 8) {
TextField(NSLocalizedString("Search...", comment: ""), text: $text)
TextField(L10n.search, text: $text)
.padding(8)
.padding(.horizontal, 16)
#if os(iOS)

View File

@ -24,7 +24,6 @@ struct NextUpWidgetProvider: TimelineProvider {
}
func getSnapshot(in context: Context, completion: @escaping (NextUpEntry) -> Void) {
guard let currentLogin = SessionManager.main.currentLogin else { return }
let currentDate = Date()
@ -68,7 +67,6 @@ struct NextUpWidgetProvider: TimelineProvider {
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
guard let currentLogin = SessionManager.main.currentLogin else { return }
let currentDate = Date()
@ -137,7 +135,7 @@ struct NextUpEntryView: View {
}
.background(Color.blue)
} else if entry.items.isEmpty {
Text("Empty Next Up")
L10n.emptyNextUp.text
.font(.body)
.bold()
.foregroundColor(.primary)
@ -216,7 +214,7 @@ extension NextUpEntryView {
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
Text("\(item.0.name ?? "") · \(L10n.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(item.0.indexNumber ?? 0)))")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
@ -243,7 +241,8 @@ extension NextUpEntryView {
.fontWeight(.semibold)
.foregroundColor(.primary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
Text("\(item.0.name ?? "") · \(L10n.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(item.0.indexNumber ?? 0)))")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
@ -305,7 +304,7 @@ extension NextUpEntryView {
.fontWeight(.semibold)
.foregroundColor(.white)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Text("\(firstItem.0.name ?? "") · S\(firstItem.0.parentIndexNumber ?? 0):E\(firstItem.0.indexNumber ?? 0)")
Text("\(firstItem.0.name ?? "") · \(L10n.seasonAndEpisode(String(firstItem.0.parentIndexNumber ?? 0), String(firstItem.0.indexNumber ?? 0)))")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.gray)
@ -347,7 +346,7 @@ struct NextUpWidget: Widget {
provider: NextUpWidgetProvider()) { entry in
NextUpEntryView(entry: entry)
}
.configurationDisplayName("Next Up")
.configurationDisplayName(L10n.nextUp)
.description("Keep watching where you left off or see what's up next.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
@ -366,7 +365,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -377,7 +376,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -392,7 +391,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -404,7 +403,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -417,7 +416,7 @@ struct NextUpWidget_Previews: PreviewProvider {
NextUpEntryView(entry: .init(date: Date(),
items: [
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -427,7 +426,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string></string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>SwiftGen_SwiftGenCLI</string>
<key>CFBundleIdentifier</key>
<string>SwiftGen.SwiftGenCLI.resources</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>SwiftGen_SwiftGenCLI</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>13A233</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>11.3</string>
<key>DTSDKBuild</key>
<string>20E214</string>
<key>DTSDKName</key>
<string>macosx11.3</string>
<key>DTXcode</key>
<string>1300</string>
<key>DTXcodeBuild</key>
<string>13A233</string>
<key>LSMinimumSystemVersion</key>
<string>10.11</string>
</dict>
</plist>

View File

@ -0,0 +1,43 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if palettes %}
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
#if os(macOS)
import AppKit
{% if enumName != 'NSColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit
{% if enumName != 'UIColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
#endif
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Colors
// swiftlint:disable identifier_name line_length type_body_length
{{accessModifier}} extension {{enumName}} {
{% macro h2f hex %}{{hex|hexToInt|int255toFloat}}{% endmacro %}
{% macro enumBlock colors accessPrefix %}
{% for color in colors %}
/// 0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}} (r: {{color.red|hexToInt}}, g: {{color.green|hexToInt}}, b: {{color.blue|hexToInt}}, a: {{color.alpha|hexToInt}})
{{accessPrefix}}static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = #colorLiteral(red: {% call h2f color.red %}, green: {% call h2f color.green %}, blue: {% call h2f color.blue %}, alpha: {% call h2f color.alpha %})
{% endfor %}
{% endmacro %}
{% if palettes.count > 1 or param.forceFileNameEnum %}
{% set accessPrefix %}{{accessModifier}} {% endset %}
{% for palette in palettes %}
enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call enumBlock palette.colors accessPrefix %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call enumBlock palettes.first.colors "" %}
{% endif %}
}
// swiftlint:enable identifier_name line_length type_body_length
{% else %}
// No color found
{% endif %}

View File

@ -0,0 +1,43 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if palettes %}
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
#if os(macOS)
import AppKit
{% if enumName != 'NSColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit
{% if enumName != 'UIColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
#endif
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Colors
// swiftlint:disable identifier_name line_length type_body_length
{{accessModifier}} extension {{enumName}} {
{% macro h2f hex %}{{hex|hexToInt|int255toFloat}}{% endmacro %}
{% macro enumBlock colors accessPrefix %}
{% for color in colors %}
/// 0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}} (r: {{color.red|hexToInt}}, g: {{color.green|hexToInt}}, b: {{color.blue|hexToInt}}, a: {{color.alpha|hexToInt}})
{{accessPrefix}}static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = #colorLiteral(red: {% call h2f color.red %}, green: {% call h2f color.green %}, blue: {% call h2f color.blue %}, alpha: {% call h2f color.alpha %})
{% endfor %}
{% endmacro %}
{% if palettes.count > 1 or param.forceFileNameEnum %}
{% set accessPrefix %}{{accessModifier}} {% endset %}
{% for palette in palettes %}
enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call enumBlock palette.colors accessPrefix %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call enumBlock palettes.first.colors "" %}
{% endif %}
}
// swiftlint:enable identifier_name line_length type_body_length
{% else %}
// No color found
{% endif %}

View File

@ -0,0 +1,84 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if palettes %}
{% set colorAlias %}{{param.colorAliasName|default:"Color"}}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
#if os(macOS)
import AppKit.NSColor
{{accessModifier}} typealias {{colorAlias}} = NSColor
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit.UIColor
{{accessModifier}} typealias {{colorAlias}} = UIColor
#endif
// swiftlint:disable superfluous_disable_command file_length implicit_return
// MARK: - Colors
// swiftlint:disable identifier_name line_length type_body_length
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
{{accessModifier}} struct {{enumName}} {
{{accessModifier}} let rgbaValue: UInt32
{{accessModifier}} var color: {{colorAlias}} { return {{colorAlias}}(named: self) }
{% macro rgbaValue color %}0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}}{% endmacro %}
{% macro enumBlock colors %}
{% for color in colors %}
/// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#{{color.red}}{{color.green}}{{color.blue}}"></span>
/// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}} <br/> (0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}})
{{accessModifier}} static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}(rgbaValue: {% call rgbaValue color %})
{% endfor %}
{% endmacro %}
{% if palettes.count > 1 or param.forceFileNameEnum %}
{% for palette in palettes %}
{{accessModifier}} enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call enumBlock palette.colors %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call enumBlock palettes.first.colors %}
{% endif %}
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
internal extension {{colorAlias}} {
convenience init(rgbaValue: UInt32) {
let components = RGBAComponents(rgbaValue: rgbaValue).normalized
self.init(red: components[0], green: components[1], blue: components[2], alpha: components[3])
}
}
private struct RGBAComponents {
let rgbaValue: UInt32
private var shifts: [UInt32] {
[
rgbaValue >> 24, // red
rgbaValue >> 16, // green
rgbaValue >> 8, // blue
rgbaValue // alpha
]
}
private var components: [CGFloat] {
shifts.map {
CGFloat($0 & 0xff)
}
}
var normalized: [CGFloat] {
components.map { $0 / 255.0 }
}
}
{{accessModifier}} extension {{colorAlias}} {
convenience init(named color: {{enumName}}) {
self.init(rgbaValue: color.rgbaValue)
}
}
{% else %}
// No color found
{% endif %}

View File

@ -0,0 +1,84 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if palettes %}
{% set colorAlias %}{{param.colorAliasName|default:"Color"}}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
#if os(macOS)
import AppKit.NSColor
{{accessModifier}} typealias {{colorAlias}} = NSColor
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit.UIColor
{{accessModifier}} typealias {{colorAlias}} = UIColor
#endif
// swiftlint:disable superfluous_disable_command file_length implicit_return
// MARK: - Colors
// swiftlint:disable identifier_name line_length type_body_length
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
{{accessModifier}} struct {{enumName}} {
{{accessModifier}} let rgbaValue: UInt32
{{accessModifier}} var color: {{colorAlias}} { return {{colorAlias}}(named: self) }
{% macro rgbaValue color %}0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}}{% endmacro %}
{% macro enumBlock colors %}
{% for color in colors %}
/// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#{{color.red}}{{color.green}}{{color.blue}}"></span>
/// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}} <br/> (0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}})
{{accessModifier}} static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}(rgbaValue: {% call rgbaValue color %})
{% endfor %}
{% endmacro %}
{% if palettes.count > 1 or param.forceFileNameEnum %}
{% for palette in palettes %}
{{accessModifier}} enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call enumBlock palette.colors %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call enumBlock palettes.first.colors %}
{% endif %}
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
internal extension {{colorAlias}} {
convenience init(rgbaValue: UInt32) {
let components = RGBAComponents(rgbaValue: rgbaValue).normalized
self.init(red: components[0], green: components[1], blue: components[2], alpha: components[3])
}
}
private struct RGBAComponents {
let rgbaValue: UInt32
private var shifts: [UInt32] {
[
rgbaValue >> 24, // red
rgbaValue >> 16, // green
rgbaValue >> 8, // blue
rgbaValue // alpha
]
}
private var components: [CGFloat] {
shifts.map {
CGFloat($0 & 0xff)
}
}
var normalized: [CGFloat] {
components.map { $0 / 255.0 }
}
}
{{accessModifier}} extension {{colorAlias}} {
convenience init(named color: {{enumName}}) {
self.init(rgbaValue: color.rgbaValue)
}
}
{% else %}
// No color found
{% endif %}

View File

@ -0,0 +1,211 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
// swiftlint:disable superfluous_disable_command implicit_return
// swiftlint:disable sorted_imports
import CoreData
import Foundation
{% for import in param.extraImports %}
import {{ import }}
{% empty %}
{# If extraImports is a single String instead of an array, `for` considers it empty but we still have to check if there's a single String value #}
{% if param.extraImports %}import {{ param.extraImports }}{% endif %}
{% endfor %}
// swiftlint:disable attributes file_length vertical_whitespace_closing_braces
// swiftlint:disable identifier_name line_length type_body_length
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% for model in models %}
{% for name, entity in model.entities %}
{% set superclass %}{{ model.entities[entity.superEntity].className|default:"NSManagedObject" }}{% endset %}
{% set entityClassName %}{{ entity.className|default:"NSManagedObject" }}{% endset %}
// MARK: - {{ entity.name }}
{% if not entity.shouldGenerateCode %}
// Note: '{{ entity.name }}' has codegen enabled for Xcode, skipping code generation.
{% elif entityClassName|contains:"." %}
// Warning: '{{ entityClassName }}' cannot be a valid type name, skipping code generation.
{% else %}
{% if param.generateObjcName %}
@objc({{ entityClassName }})
{% endif %}
{{ accessModifier }} class {{ entityClassName }}: {{ superclass }} {
{% set override %}{% if superclass != "NSManagedObject" %}override {% endif %}{% endset %}
{{ override }}{{ accessModifier }} class var entityName: String {
return "{{ entity.name }}"
}
{{ override }}{{ accessModifier }} class func entity(in managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext)
}
@available(*, deprecated, renamed: "makeFetchRequest", message: "To avoid collisions with the less concrete method in `NSManagedObject`, please use `makeFetchRequest()` instead.")
@nonobjc {{ accessModifier }} class func fetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
}
@nonobjc {{ accessModifier }} class func makeFetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
}
// swiftlint:disable discouraged_optional_boolean discouraged_optional_collection
{% for attribute in entity.attributes %}
{% if attribute.userInfo.RawType %}
{% set rawType attribute.userInfo.RawType %}
{% set unwrapOptional attribute.userInfo.unwrapOptional %}
{{ accessModifier }} var {{ attribute.name }}: {{ rawType }}{% if not unwrapOptional %}?{% endif %} {
get {
let key = "{{ attribute.name }}"
willAccessValue(forKey: key)
defer { didAccessValue(forKey: key) }
{% if unwrapOptional %}
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue,
let result = {{ rawType }}(rawValue: value) else {
fatalError("Could not convert value for key '\(key)' to type '{{ rawType }}'")
}
return result
{% else %}
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue else {
return nil
}
return {{ rawType }}(rawValue: value)
{% endif %}
}
set {
let key = "{{ attribute.name }}"
willChangeValue(forKey: key)
defer { didChangeValue(forKey: key) }
setPrimitiveValue(newValue{% if not unwrapOptional %}?{% endif %}.rawValue, forKey: key)
}
}
{% elif attribute.usesScalarValueType and attribute.isOptional %}
{{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}? {
get {
let key = "{{ attribute.name }}"
willAccessValue(forKey: key)
defer { didAccessValue(forKey: key) }
return primitiveValue(forKey: key) as? {{ attribute.typeName }}
}
set {
let key = "{{ attribute.name }}"
willChangeValue(forKey: key)
defer { didChangeValue(forKey: key) }
setPrimitiveValue(newValue, forKey: key)
}
}
{% else %}
@NSManaged {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}{% if attribute.isOptional %}?{% endif %}
{% endif %}
{% endfor %}
{% for relationship in entity.relationships %}
{% if relationship.isToMany %}
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}>{% endif %}{% if relationship.isOptional %}?{% endif %}
{% else %}
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% if relationship.isOptional %}?{% endif %}
{% endif %}
{% endfor %}
{% for fetchedProperty in entity.fetchedProperties %}
@NSManaged {{ accessModifier }} var {{ fetchedProperty.name }}: [{{ fetchedProperty.fetchRequest.entity }}]
{% endfor %}
// swiftlint:enable discouraged_optional_boolean discouraged_optional_collection
}
{% for relationship in entity.relationships where relationship.isToMany %}
{% set destinationEntityClassName %}{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% endset %}
{% set collectionClassName %}{% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ destinationEntityClassName }}>{% endif %}{% endset %}
{% set relationshipName %}{{ relationship.name | upperFirstLetter }}{% endset %}
// MARK: Relationship {{ relationshipName }}
extension {{ entityClassName }} {
{% if relationship.isOrdered %}
@objc(insertObject:in{{ relationshipName }}AtIndex:)
@NSManaged public func insertInto{{ relationshipName }}(_ value: {{ destinationEntityClassName }}, at idx: Int)
@objc(removeObjectFrom{{ relationshipName }}AtIndex:)
@NSManaged public func removeFrom{{ relationshipName }}(at idx: Int)
@objc(insert{{ relationshipName }}:atIndexes:)
@NSManaged public func insertInto{{ relationshipName }}(_ values: [{{ destinationEntityClassName }}], at indexes: NSIndexSet)
@objc(remove{{ relationshipName }}AtIndexes:)
@NSManaged public func removeFrom{{ relationshipName }}(at indexes: NSIndexSet)
@objc(replaceObjectIn{{ relationshipName }}AtIndex:withObject:)
@NSManaged public func replace{{ relationshipName }}(at idx: Int, with value: {{ destinationEntityClassName }})
@objc(replace{{ relationshipName }}AtIndexes:with{{ relationshipName }}:)
@NSManaged public func replace{{ relationshipName }}(at indexes: NSIndexSet, with values: [{{ destinationEntityClassName }}])
{% endif %}
@objc(add{{ relationshipName }}Object:)
@NSManaged public func addTo{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
@objc(remove{{ relationshipName }}Object:)
@NSManaged public func removeFrom{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
@objc(add{{ relationshipName }}:)
@NSManaged public func addTo{{ relationshipName }}(_ values: {{ collectionClassName }})
@objc(remove{{ relationshipName }}:)
@NSManaged public func removeFrom{{ relationshipName }}(_ values: {{ collectionClassName }})
}
{% endfor %}
{% if model.fetchRequests[entity.name].count > 0 %}
// MARK: Fetch Requests
extension {{ entityClassName }} {
{% for fetchRequest in model.fetchRequests[entity.name] %}
{% set resultTypeName %}{% filter removeNewlines:"leading" %}
{% if fetchRequest.resultType == "Object" %}
{{ entityClassName }}
{% elif fetchRequest.resultType == "Object ID" %}
NSManagedObjectID
{% elif fetchRequest.resultType == "Dictionary" %}
[String: Any]
{% endif %}
{% endfilter %}{% endset %}
class func fetch{{ fetchRequest.name | upperFirstLetter }}({% filter removeNewlines:"leading" %}
managedObjectContext: NSManagedObjectContext
{% for variableName, variableType in fetchRequest.substitutionVariables %}
, {{ variableName | lowerFirstWord }}: {{ variableType }}
{% endfor %}
{% endfilter %}) throws -> [{{ resultTypeName }}] {
guard let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator else {
fatalError("Managed object context has no persistent store coordinator for getting fetch request templates")
}
let model = persistentStoreCoordinator.managedObjectModel
let substitutionVariables: [String: Any] = [
{% for variableName, variableType in fetchRequest.substitutionVariables %}
"{{ variableName }}": {{ variableName | lowerFirstWord }}{{ "," if not forloop.last }}
{% empty %}
:
{% endfor %}
]
guard let fetchRequest = model.fetchRequestFromTemplate(withName: "{{ fetchRequest.name }}", substitutionVariables: substitutionVariables) else {
fatalError("No fetch request template named '{{ fetchRequest.name }}' found.")
}
guard let result = try managedObjectContext.fetch(fetchRequest) as? [{{ resultTypeName }}] else {
fatalError("Unable to cast fetch result to correct result type.")
}
return result
}
{% endfor %}
}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
// swiftlint:enable identifier_name line_length type_body_length

View File

@ -0,0 +1,211 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
// swiftlint:disable superfluous_disable_command implicit_return
// swiftlint:disable sorted_imports
import CoreData
import Foundation
{% for import in param.extraImports %}
import {{ import }}
{% empty %}
{# If extraImports is a single String instead of an array, `for` considers it empty but we still have to check if there's a single String value #}
{% if param.extraImports %}import {{ param.extraImports }}{% endif %}
{% endfor %}
// swiftlint:disable attributes file_length vertical_whitespace_closing_braces
// swiftlint:disable identifier_name line_length type_body_length
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% for model in models %}
{% for name, entity in model.entities %}
{% set superclass %}{{ model.entities[entity.superEntity].className|default:"NSManagedObject" }}{% endset %}
{% set entityClassName %}{{ entity.className|default:"NSManagedObject" }}{% endset %}
// MARK: - {{ entity.name }}
{% if not entity.shouldGenerateCode %}
// Note: '{{ entity.name }}' has codegen enabled for Xcode, skipping code generation.
{% elif entityClassName|contains:"." %}
// Warning: '{{ entityClassName }}' cannot be a valid type name, skipping code generation.
{% else %}
{% if param.generateObjcName %}
@objc({{ entityClassName }})
{% endif %}
{{ accessModifier }} class {{ entityClassName }}: {{ superclass }} {
{% set override %}{% if superclass != "NSManagedObject" %}override {% endif %}{% endset %}
{{ override }}{{ accessModifier }} class var entityName: String {
return "{{ entity.name }}"
}
{{ override }}{{ accessModifier }} class func entity(in managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext)
}
@available(*, deprecated, renamed: "makeFetchRequest", message: "To avoid collisions with the less concrete method in `NSManagedObject`, please use `makeFetchRequest()` instead.")
@nonobjc {{ accessModifier }} class func fetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
}
@nonobjc {{ accessModifier }} class func makeFetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
}
// swiftlint:disable discouraged_optional_boolean discouraged_optional_collection
{% for attribute in entity.attributes %}
{% if attribute.userInfo.RawType %}
{% set rawType attribute.userInfo.RawType %}
{% set unwrapOptional attribute.userInfo.unwrapOptional %}
{{ accessModifier }} var {{ attribute.name }}: {{ rawType }}{% if not unwrapOptional %}?{% endif %} {
get {
let key = "{{ attribute.name }}"
willAccessValue(forKey: key)
defer { didAccessValue(forKey: key) }
{% if unwrapOptional %}
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue,
let result = {{ rawType }}(rawValue: value) else {
fatalError("Could not convert value for key '\(key)' to type '{{ rawType }}'")
}
return result
{% else %}
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue else {
return nil
}
return {{ rawType }}(rawValue: value)
{% endif %}
}
set {
let key = "{{ attribute.name }}"
willChangeValue(forKey: key)
defer { didChangeValue(forKey: key) }
setPrimitiveValue(newValue{% if not unwrapOptional %}?{% endif %}.rawValue, forKey: key)
}
}
{% elif attribute.usesScalarValueType and attribute.isOptional %}
{{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}? {
get {
let key = "{{ attribute.name }}"
willAccessValue(forKey: key)
defer { didAccessValue(forKey: key) }
return primitiveValue(forKey: key) as? {{ attribute.typeName }}
}
set {
let key = "{{ attribute.name }}"
willChangeValue(forKey: key)
defer { didChangeValue(forKey: key) }
setPrimitiveValue(newValue, forKey: key)
}
}
{% else %}
@NSManaged {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}{% if attribute.isOptional %}?{% endif %}
{% endif %}
{% endfor %}
{% for relationship in entity.relationships %}
{% if relationship.isToMany %}
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}>{% endif %}{% if relationship.isOptional %}?{% endif %}
{% else %}
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% if relationship.isOptional %}?{% endif %}
{% endif %}
{% endfor %}
{% for fetchedProperty in entity.fetchedProperties %}
@NSManaged {{ accessModifier }} var {{ fetchedProperty.name }}: [{{ fetchedProperty.fetchRequest.entity }}]
{% endfor %}
// swiftlint:enable discouraged_optional_boolean discouraged_optional_collection
}
{% for relationship in entity.relationships where relationship.isToMany %}
{% set destinationEntityClassName %}{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% endset %}
{% set collectionClassName %}{% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ destinationEntityClassName }}>{% endif %}{% endset %}
{% set relationshipName %}{{ relationship.name | upperFirstLetter }}{% endset %}
// MARK: Relationship {{ relationshipName }}
extension {{ entityClassName }} {
{% if relationship.isOrdered %}
@objc(insertObject:in{{ relationshipName }}AtIndex:)
@NSManaged public func insertInto{{ relationshipName }}(_ value: {{ destinationEntityClassName }}, at idx: Int)
@objc(removeObjectFrom{{ relationshipName }}AtIndex:)
@NSManaged public func removeFrom{{ relationshipName }}(at idx: Int)
@objc(insert{{ relationshipName }}:atIndexes:)
@NSManaged public func insertInto{{ relationshipName }}(_ values: [{{ destinationEntityClassName }}], at indexes: NSIndexSet)
@objc(remove{{ relationshipName }}AtIndexes:)
@NSManaged public func removeFrom{{ relationshipName }}(at indexes: NSIndexSet)
@objc(replaceObjectIn{{ relationshipName }}AtIndex:withObject:)
@NSManaged public func replace{{ relationshipName }}(at idx: Int, with value: {{ destinationEntityClassName }})
@objc(replace{{ relationshipName }}AtIndexes:with{{ relationshipName }}:)
@NSManaged public func replace{{ relationshipName }}(at indexes: NSIndexSet, with values: [{{ destinationEntityClassName }}])
{% endif %}
@objc(add{{ relationshipName }}Object:)
@NSManaged public func addTo{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
@objc(remove{{ relationshipName }}Object:)
@NSManaged public func removeFrom{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
@objc(add{{ relationshipName }}:)
@NSManaged public func addTo{{ relationshipName }}(_ values: {{ collectionClassName }})
@objc(remove{{ relationshipName }}:)
@NSManaged public func removeFrom{{ relationshipName }}(_ values: {{ collectionClassName }})
}
{% endfor %}
{% if model.fetchRequests[entity.name].count > 0 %}
// MARK: Fetch Requests
extension {{ entityClassName }} {
{% for fetchRequest in model.fetchRequests[entity.name] %}
{% set resultTypeName %}{% filter removeNewlines:"leading" %}
{% if fetchRequest.resultType == "Object" %}
{{ entityClassName }}
{% elif fetchRequest.resultType == "Object ID" %}
NSManagedObjectID
{% elif fetchRequest.resultType == "Dictionary" %}
[String: Any]
{% endif %}
{% endfilter %}{% endset %}
class func fetch{{ fetchRequest.name | upperFirstLetter }}({% filter removeNewlines:"leading" %}
managedObjectContext: NSManagedObjectContext
{% for variableName, variableType in fetchRequest.substitutionVariables %}
, {{ variableName | lowerFirstWord }}: {{ variableType }}
{% endfor %}
{% endfilter %}) throws -> [{{ resultTypeName }}] {
guard let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator else {
fatalError("Managed object context has no persistent store coordinator for getting fetch request templates")
}
let model = persistentStoreCoordinator.managedObjectModel
let substitutionVariables: [String: Any] = [
{% for variableName, variableType in fetchRequest.substitutionVariables %}
"{{ variableName }}": {{ variableName | lowerFirstWord }}{{ "," if not forloop.last }}
{% empty %}
:
{% endfor %}
]
guard let fetchRequest = model.fetchRequestFromTemplate(withName: "{{ fetchRequest.name }}", substitutionVariables: substitutionVariables) else {
fatalError("No fetch request template named '{{ fetchRequest.name }}' found.")
}
guard let result = try managedObjectContext.fetch(fetchRequest) as? [{{ resultTypeName }}] else {
fatalError("Unable to cast fetch result to correct result type.")
}
return result
}
{% endfor %}
}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
// swiftlint:enable identifier_name line_length type_body_length

View File

@ -0,0 +1,103 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if groups.count > 0 %}
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
// MARK: - Files
{% macro groupBlock group %}
{% for file in group.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in group.directories %}
{% call dirBlock dir %}
{% endfor %}
{% endmacro %}
{% macro fileBlock file %}
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
{% endmacro %}
{% macro dirBlock directory %}
{% for file in directory.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in directory.directories %}
{% call dirBlock dir %}
{% endfor %}
{% endmacro %}
// swiftlint:disable explicit_type_interface identifier_name
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
{{accessModifier}} enum {{enumName}} {
{% if groups.count > 1 or param.forceFileNameEnum %}
{% for group in groups %}
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call groupBlock groups.first %}
{% endif %}
}
// swiftlint:enable explicit_type_interface identifier_name
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
// MARK: - Implementation Details
{{accessModifier}} struct {{resourceType}} {
{{accessModifier}} let name: String
{{accessModifier}} let ext: String?
{{accessModifier}} let relativePath: String
{{accessModifier}} let mimeType: String
{{accessModifier}} var url: URL {
return url(locale: nil)
}
{{accessModifier}} func url(locale: Locale?) -> URL {
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
let url = bundle.url(
forResource: name,
withExtension: ext,
subdirectory: relativePath,
localization: locale?.identifier
)
guard let result = url else {
let file = name + (ext.flatMap { ".\($0)" } ?? "")
fatalError("Could not locate file named \(file)")
}
return result
}
{{accessModifier}} var path: String {
return path(locale: nil)
}
{{accessModifier}} func path(locale: Locale?) -> String {
return url(locale: locale).path
}
}
{% if not param.bundle %}
// swiftlint:disable convenience_type explicit_type_interface
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type explicit_type_interface
{% endif %}
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,103 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if groups.count > 0 %}
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
// MARK: - Files
{% macro groupBlock group %}
{% for file in group.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in group.directories %}
{% call dirBlock dir %}
{% endfor %}
{% endmacro %}
{% macro fileBlock file %}
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
{% endmacro %}
{% macro dirBlock directory %}
{% for file in directory.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in directory.directories %}
{% call dirBlock dir %}
{% endfor %}
{% endmacro %}
// swiftlint:disable explicit_type_interface identifier_name
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
{{accessModifier}} enum {{enumName}} {
{% if groups.count > 1 or param.forceFileNameEnum %}
{% for group in groups %}
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call groupBlock groups.first %}
{% endif %}
}
// swiftlint:enable explicit_type_interface identifier_name
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
// MARK: - Implementation Details
{{accessModifier}} struct {{resourceType}} {
{{accessModifier}} let name: String
{{accessModifier}} let ext: String?
{{accessModifier}} let relativePath: String
{{accessModifier}} let mimeType: String
{{accessModifier}} var url: URL {
return url(locale: nil)
}
{{accessModifier}} func url(locale: Locale?) -> URL {
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
let url = bundle.url(
forResource: name,
withExtension: ext,
subdirectory: relativePath,
localization: locale?.identifier
)
guard let result = url else {
let file = name + (ext.flatMap { ".\($0)" } ?? "")
fatalError("Could not locate file named \(file)")
}
return result
}
{{accessModifier}} var path: String {
return path(locale: nil)
}
{{accessModifier}} func path(locale: Locale?) -> String {
return url(locale: locale).path
}
}
{% if not param.bundle %}
// swiftlint:disable convenience_type explicit_type_interface
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type explicit_type_interface
{% endif %}
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,107 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if groups.count > 0 %}
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
// MARK: - Files
{% macro groupBlock group %}
{% for file in group.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in group.directories %}
{% call dirBlock dir "" %}
{% endfor %}
{% endmacro %}
{% macro fileBlock file %}
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
{% endmacro %}
{% macro dirBlock directory parent %}
{% set fullDir %}{{parent}}{{directory.name}}/{% endset %}
/// {{ fullDir }}
{{accessModifier}} enum {{directory.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% for file in directory.files %}
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
{% endfor %}
{% for dir in directory.directories %}
{% filter indent:2 %}{% call dirBlock dir fullDir %}{% endfilter %}
{% endfor %}
}
{% endmacro %}
// swiftlint:disable explicit_type_interface identifier_name
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
{{accessModifier}} enum {{enumName}} {
{% if groups.count > 1 or param.forceFileNameEnum %}
{% for group in groups %}
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call groupBlock groups.first %}
{% endif %}
}
// swiftlint:enable explicit_type_interface identifier_name
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
// MARK: - Implementation Details
{{accessModifier}} struct {{resourceType}} {
{{accessModifier}} let name: String
{{accessModifier}} let ext: String?
{{accessModifier}} let relativePath: String
{{accessModifier}} let mimeType: String
{{accessModifier}} var url: URL {
return url(locale: nil)
}
{{accessModifier}} func url(locale: Locale?) -> URL {
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
let url = bundle.url(
forResource: name,
withExtension: ext,
subdirectory: relativePath,
localization: locale?.identifier
)
guard let result = url else {
let file = name + (ext.flatMap { ".\($0)" } ?? "")
fatalError("Could not locate file named \(file)")
}
return result
}
{{accessModifier}} var path: String {
return path(locale: nil)
}
{{accessModifier}} func path(locale: Locale?) -> String {
return url(locale: locale).path
}
}
{% if not param.bundle %}
// swiftlint:disable convenience_type explicit_type_interface
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type explicit_type_interface
{% endif %}
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,107 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if groups.count > 0 %}
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
// MARK: - Files
{% macro groupBlock group %}
{% for file in group.files %}
{% call fileBlock file %}
{% endfor %}
{% for dir in group.directories %}
{% call dirBlock dir "" %}
{% endfor %}
{% endmacro %}
{% macro fileBlock file %}
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
{% endmacro %}
{% macro dirBlock directory parent %}
{% set fullDir %}{{parent}}{{directory.name}}/{% endset %}
/// {{ fullDir }}
{{accessModifier}} enum {{directory.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% for file in directory.files %}
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
{% endfor %}
{% for dir in directory.directories %}
{% filter indent:2 %}{% call dirBlock dir fullDir %}{% endfilter %}
{% endfor %}
}
{% endmacro %}
// swiftlint:disable explicit_type_interface identifier_name
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
{{accessModifier}} enum {{enumName}} {
{% if groups.count > 1 or param.forceFileNameEnum %}
{% for group in groups %}
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call groupBlock groups.first %}
{% endif %}
}
// swiftlint:enable explicit_type_interface identifier_name
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
// MARK: - Implementation Details
{{accessModifier}} struct {{resourceType}} {
{{accessModifier}} let name: String
{{accessModifier}} let ext: String?
{{accessModifier}} let relativePath: String
{{accessModifier}} let mimeType: String
{{accessModifier}} var url: URL {
return url(locale: nil)
}
{{accessModifier}} func url(locale: Locale?) -> URL {
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
let url = bundle.url(
forResource: name,
withExtension: ext,
subdirectory: relativePath,
localization: locale?.identifier
)
guard let result = url else {
let file = name + (ext.flatMap { ".\($0)" } ?? "")
fatalError("Could not locate file named \(file)")
}
return result
}
{{accessModifier}} var path: String {
return path(locale: nil)
}
{{accessModifier}} func path(locale: Locale?) -> String {
return url(locale: locale).path
}
}
{% if not param.bundle %}
// swiftlint:disable convenience_type explicit_type_interface
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type explicit_type_interface
{% endif %}
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,110 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if families %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set fontType %}{{param.fontTypeName|default:"FontConvertible"}}{% endset %}
#if os(macOS)
import AppKit.NSFont
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit.UIFont
#endif
// Deprecated typealiases
@available(*, deprecated, renamed: "{{fontType}}.Font", message: "This typealias will be removed in SwiftGen 7.0")
{{accessModifier}} typealias {{param.fontAliasName|default:"Font"}} = {{fontType}}.Font
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// swiftlint:disable implicit_return
// MARK: - Fonts
// swiftlint:disable identifier_name line_length type_body_length
{% macro transformPath path %}{% filter removeNewlines %}
{% if param.preservePath %}
{{path}}
{% else %}
{{path|basename}}
{% endif %}
{% endfilter %}{% endmacro %}
{{accessModifier}} enum {{param.enumName|default:"FontFamily"}} {
{% for family in families %}
{{accessModifier}} enum {{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% for font in family.fonts %}
{{accessModifier}} static let {{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{fontType}}(name: "{{font.name}}", family: "{{family.name}}", path: "{% call transformPath font.path %}")
{% endfor %}
{{accessModifier}} static let all: [{{fontType}}] = [{% for font in family.fonts %}{{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{{ ", " if not forloop.last }}{% endfor %}]
}
{% endfor %}
{{accessModifier}} static let allCustomFonts: [{{fontType}}] = [{% for family in families %}{{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.all{{ ", " if not forloop.last }}{% endfor %}].flatMap { $0 }
{{accessModifier}} static func registerAllCustomFonts() {
allCustomFonts.forEach { $0.register() }
}
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
{{accessModifier}} struct {{fontType}} {
{{accessModifier}} let name: String
{{accessModifier}} let family: String
{{accessModifier}} let path: String
#if os(macOS)
{{accessModifier}} typealias Font = NSFont
#elseif os(iOS) || os(tvOS) || os(watchOS)
{{accessModifier}} typealias Font = UIFont
#endif
{{accessModifier}} func font(size: CGFloat) -> Font! {
return Font(font: self, size: size)
}
{{accessModifier}} func register() {
// swiftlint:disable:next conditional_returns_on_newline
guard let url = url else { return }
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
}
fileprivate var url: URL? {
{% if param.lookupFunction %}
return {{param.lookupFunction}}(name, family, path)
{% else %}
return {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil)
{% endif %}
}
}
{{accessModifier}} extension {{fontType}}.Font {
convenience init?(font: {{fontType}}, size: CGFloat) {
#if os(iOS) || os(tvOS) || os(watchOS)
if !UIFont.fontNames(forFamilyName: font.family).contains(font.name) {
font.register()
}
#elseif os(macOS)
if let url = font.url, CTFontManagerGetScopeForURL(url as CFURL) == .none {
font.register()
}
#endif
self.init(name: font.name, size: size)
}
}
{% if not param.bundle and not param.lookupFunction %}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% else %}
// No fonts found
{% endif %}

View File

@ -0,0 +1,113 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if families %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set fontType %}{{param.fontTypeName|default:"FontConvertible"}}{% endset %}
#if os(macOS)
import AppKit.NSFont
#elseif os(iOS) || os(tvOS) || os(watchOS)
import UIKit.UIFont
#endif
// Deprecated typealiases
@available(*, deprecated, renamed: "{{fontType}}.Font", message: "This typealias will be removed in SwiftGen 7.0")
{{accessModifier}} typealias {{param.fontAliasName|default:"Font"}} = {{fontType}}.Font
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Fonts
// swiftlint:disable identifier_name line_length type_body_length
{% macro transformPath path %}{% filter removeNewlines %}
{% if param.preservePath %}
{{path}}
{% else %}
{{path|basename}}
{% endif %}
{% endfilter %}{% endmacro %}
{{accessModifier}} enum {{param.enumName|default:"FontFamily"}} {
{% for family in families %}
{{accessModifier}} enum {{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% for font in family.fonts %}
{{accessModifier}} static let {{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{fontType}}(name: "{{font.name}}", family: "{{family.name}}", path: "{% call transformPath font.path %}")
{% endfor %}
{{accessModifier}} static let all: [{{fontType}}] = [{% for font in family.fonts %}{{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{{ ", " if not forloop.last }}{% endfor %}]
}
{% endfor %}
{{accessModifier}} static let allCustomFonts: [{{fontType}}] = [{% for family in families %}{{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.all{{ ", " if not forloop.last }}{% endfor %}].flatMap { $0 }
{{accessModifier}} static func registerAllCustomFonts() {
allCustomFonts.forEach { $0.register() }
}
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
{{accessModifier}} struct {{fontType}} {
{{accessModifier}} let name: String
{{accessModifier}} let family: String
{{accessModifier}} let path: String
#if os(macOS)
{{accessModifier}} typealias Font = NSFont
#elseif os(iOS) || os(tvOS) || os(watchOS)
{{accessModifier}} typealias Font = UIFont
#endif
{{accessModifier}} func font(size: CGFloat) -> Font {
guard let font = Font(font: self, size: size) else {
fatalError("Unable to initialize font '\(name)' (\(family))")
}
return font
}
{{accessModifier}} func register() {
// swiftlint:disable:next conditional_returns_on_newline
guard let url = url else { return }
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
}
fileprivate var url: URL? {
// swiftlint:disable:next implicit_return
{% if param.lookupFunction %}
return {{param.lookupFunction}}(name, family, path)
{% else %}
return {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil)
{% endif %}
}
}
{{accessModifier}} extension {{fontType}}.Font {
convenience init?(font: {{fontType}}, size: CGFloat) {
#if os(iOS) || os(tvOS) || os(watchOS)
if !UIFont.fontNames(forFamilyName: font.family).contains(font.name) {
font.register()
}
#elseif os(macOS)
if let url = font.url, CTFontManagerGetScopeForURL(url as CFURL) == .none {
font.register()
}
#endif
self.init(name: font.name, size: size)
}
}
{% if not param.bundle and not param.lookupFunction %}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% else %}
// No fonts found
{% endif %}

View File

@ -0,0 +1,157 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if platform and storyboards %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
{% set prefix %}{% if isAppKit %}NS{% else %}UI{% endif %}{% endset %}
{% set controller %}{% if isAppKit %}Controller{% else %}ViewController{% endif %}{% endset %}
// swiftlint:disable sorted_imports
import Foundation
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
import {{module}}
{% endfor %}
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length implicit_return
// MARK: - Storyboard Scenes
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
{% macro moduleName item %}{% filter removeNewlines %}
{% if item.moduleIsPlaceholder %}
{{ env.PRODUCT_MODULE_NAME|default:param.module }}
{% else %}
{{ item.module }}
{% endif %}
{% endfilter %}{% endmacro %}
{% macro className item %}{% filter removeNewlines %}
{% set module %}{% call moduleName item %}{% endset %}
{% if module and ( not param.ignoreTargetModule or module != env.PRODUCT_MODULE_NAME and module != param.module ) %}
{{module}}.
{% endif %}
{{item.type}}
{% endfilter %}{% endmacro %}
{{accessModifier}} enum {{param.enumName|default:"StoryboardScene"}} {
{% for storyboard in storyboards %}
{% set storyboardName %}{{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}{% endset %}
{{accessModifier}} enum {{storyboardName}}: StoryboardType {
{{accessModifier}} static let storyboardName = "{{storyboard.name}}"
{% if storyboard.initialScene %}
{% set sceneClass %}{% call className storyboard.initialScene %}{% endset %}
{{accessModifier}} static let initialScene = InitialSceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self)
{% endif %}
{% for scene in storyboard.scenes %}
{% set sceneID %}{{scene.identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
{% set sceneClass %}{% call className scene %}{% endset %}
{{accessModifier}} static let {{sceneID}} = SceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self, identifier: "{{scene.identifier}}")
{% endfor %}
}
{% endfor %}
}
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
// MARK: - Implementation Details
{{accessModifier}} protocol StoryboardType {
static var storyboardName: String { get }
}
{{accessModifier}} extension StoryboardType {
static var storyboard: {{prefix}}Storyboard {
let name = {% if isAppKit %}NSStoryboard.Name({% endif %}self.storyboardName{% if isAppKit %}){% endif %}
{% if param.lookupFunction %}
return {{param.lookupFunction}}(name)
{% else %}
return {{prefix}}Storyboard(name: name, bundle: {{param.bundle|default:"BundleToken.bundle"}})
{% endif %}
}
}
{{accessModifier}} struct SceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
{{accessModifier}} let storyboard: StoryboardType.Type
{{accessModifier}} let identifier: String
{{accessModifier}} func instantiate() -> T {
let identifier = {% if isAppKit %}NSStoryboard.SceneIdentifier({% endif %}self.identifier{% if isAppKit %}){% endif %}
guard let controller = storyboard.storyboard.instantiate{{controller}}(withIdentifier: identifier) as? T else {
fatalError("{{controller}} '\(identifier)' is not of the expected class \(T.self).")
}
return controller
}
{% if isAppKit %}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
{% else %}
@available(iOS 13.0, tvOS 13.0, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
{% endif %}
}
{{accessModifier}} struct InitialSceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
{{accessModifier}} let storyboard: StoryboardType.Type
{{accessModifier}} func instantiate() -> T {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}() as? T else {
fatalError("{{controller}} is not of the expected class \(T.self).")
}
return controller
}
{% if isAppKit %}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
{% else %}
@available(iOS 13.0, tvOS 13.0, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
{% endif %}
}
{% if not param.bundle and not param.lookupFunction %}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% elif storyboards %}
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
{% else %}
// No storyboard found
{% endif %}

View File

@ -0,0 +1,159 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if platform and storyboards %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
{% set prefix %}{% if isAppKit %}NS{% else %}UI{% endif %}{% endset %}
{% set controller %}{% if isAppKit %}Controller{% else %}ViewController{% endif %}{% endset %}
// swiftlint:disable sorted_imports
import Foundation
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
import {{module}}
{% endfor %}
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length implicit_return
// MARK: - Storyboard Scenes
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
{% macro moduleName item %}{% filter removeNewlines %}
{% if item.moduleIsPlaceholder %}
{{ env.PRODUCT_MODULE_NAME|default:param.module }}
{% else %}
{{ item.module }}
{% endif %}
{% endfilter %}{% endmacro %}
{% macro className item %}{% filter removeNewlines %}
{% set module %}{% call moduleName item %}{% endset %}
{% if module and ( not param.ignoreTargetModule or module != env.PRODUCT_MODULE_NAME and module != param.module ) %}
{{module}}.
{% endif %}
{{item.type}}
{% endfilter %}{% endmacro %}
{{accessModifier}} enum {{param.enumName|default:"StoryboardScene"}} {
{% for storyboard in storyboards %}
{% set storyboardName %}{{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}{% endset %}
{{accessModifier}} enum {{storyboardName}}: StoryboardType {
{{accessModifier}} static let storyboardName = "{{storyboard.name}}"
{% if storyboard.initialScene %}
{% set sceneClass %}{% call className storyboard.initialScene %}{% endset %}
{{accessModifier}} static let initialScene = InitialSceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self)
{% endif %}
{% for scene in storyboard.scenes %}
{% set sceneID %}{{scene.identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
{% set sceneClass %}{% call className scene %}{% endset %}
{{accessModifier}} static let {{sceneID}} = SceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self, identifier: "{{scene.identifier}}")
{% endfor %}
}
{% endfor %}
}
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
// MARK: - Implementation Details
{{accessModifier}} protocol StoryboardType {
static var storyboardName: String { get }
}
{{accessModifier}} extension StoryboardType {
static var storyboard: {{prefix}}Storyboard {
let name = {% if isAppKit %}NSStoryboard.Name({% endif %}self.storyboardName{% if isAppKit %}){% endif %}
{% if param.lookupFunction %}
return {{param.lookupFunction}}(name)
{% else %}
return {{prefix}}Storyboard(name: name, bundle: {{param.bundle|default:"BundleToken.bundle"}})
{% endif %}
}
}
{{accessModifier}} struct SceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
{{accessModifier}} let storyboard: StoryboardType.Type
{{accessModifier}} let identifier: String
{{accessModifier}} func instantiate() -> T {
let identifier = {% if isAppKit %}NSStoryboard.SceneIdentifier({% endif %}self.identifier{% if isAppKit %}){% endif %}
guard let controller = storyboard.storyboard.instantiate{{controller}}(withIdentifier: identifier) as? T else {
fatalError("{{controller}} '\(identifier)' is not of the expected class \(T.self).")
}
return controller
}
{% if isAppKit %}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
let identifier = NSStoryboard.SceneIdentifier(self.identifier)
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
let identifier = NSStoryboard.SceneIdentifier(self.identifier)
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
{% else %}
@available(iOS 13.0, tvOS 13.0, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
}
{% endif %}
}
{{accessModifier}} struct InitialSceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
{{accessModifier}} let storyboard: StoryboardType.Type
{{accessModifier}} func instantiate() -> T {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}() as? T else {
fatalError("{{controller}} is not of the expected class \(T.self).")
}
return controller
}
{% if isAppKit %}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
@available(macOS 10.15, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
{% else %}
@available(iOS 13.0, tvOS 13.0, *)
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
}
return controller
}
{% endif %}
}
{% if not param.bundle and not param.lookupFunction %}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% elif storyboards %}
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
{% else %}
// No storyboard found
{% endif %}

View File

@ -0,0 +1,60 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if platform and storyboards %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
// swiftlint:disable sorted_imports
import Foundation
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
import {{module}}
{% endfor %}
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Storyboard Segues
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
{{accessModifier}} enum {{param.enumName|default:"StoryboardSegue"}} {
{% for storyboard in storyboards where storyboard.segues %}
{{accessModifier}} enum {{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}: String, SegueType {
{% for segue in storyboard.segues %}
{% set segueID %}{{segue.identifier|swiftIdentifier:"pretty"|lowerFirstWord}}{% endset %}
case {{segueID|escapeReservedKeywords}}{% if segueID != segue.identifier %} = "{{segue.identifier}}"{% endif %}
{% endfor %}
}
{% endfor %}
}
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
// MARK: - Implementation Details
{{accessModifier}} protocol SegueType: RawRepresentable {}
{{accessModifier}} extension {% if isAppKit %}NSSeguePerforming{% else %}UIViewController{% endif %} {
func perform<S: SegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
let identifier = {% if isAppKit %}NSStoryboardSegue.Identifier({% endif %}segue.rawValue{% if isAppKit %}){% endif %}
performSegue{% if isAppKit %}?{% endif %}(withIdentifier: identifier, sender: sender)
}
}
{{accessModifier}} extension SegueType where RawValue == String {
init?(_ segue: {% if isAppKit %}NS{% else %}UI{% endif %}StoryboardSegue) {
{% if isAppKit %}
#if swift(>=4.2)
guard let identifier = segue.identifier else { return nil }
#else
guard let identifier = segue.identifier?.rawValue else { return nil }
#endif
{% else %}
guard let identifier = segue.identifier else { return nil }
{% endif %}
self.init(rawValue: identifier)
}
}
{% elif storyboards %}
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
{% else %}
// No storyboard found
{% endif %}

View File

@ -0,0 +1,60 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if platform and storyboards %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
// swiftlint:disable sorted_imports
import Foundation
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
import {{module}}
{% endfor %}
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Storyboard Segues
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
{{accessModifier}} enum {{param.enumName|default:"StoryboardSegue"}} {
{% for storyboard in storyboards where storyboard.segues %}
{{accessModifier}} enum {{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}: String, SegueType {
{% for segue in storyboard.segues %}
{% set segueID %}{{segue.identifier|swiftIdentifier:"pretty"|lowerFirstWord}}{% endset %}
case {{segueID|escapeReservedKeywords}}{% if segueID != segue.identifier %} = "{{segue.identifier}}"{% endif %}
{% endfor %}
}
{% endfor %}
}
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
// MARK: - Implementation Details
{{accessModifier}} protocol SegueType: RawRepresentable {}
{{accessModifier}} extension {% if isAppKit %}NSSeguePerforming{% else %}UIViewController{% endif %} {
func perform<S: SegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
let identifier = {% if isAppKit %}NSStoryboardSegue.Identifier({% endif %}segue.rawValue{% if isAppKit %}){% endif %}
performSegue{% if isAppKit %}?{% endif %}(withIdentifier: identifier, sender: sender)
}
}
{{accessModifier}} extension SegueType where RawValue == String {
init?(_ segue: {% if isAppKit %}NS{% else %}UI{% endif %}StoryboardSegue) {
{% if isAppKit %}
#if swift(>=4.2)
guard let identifier = segue.identifier else { return nil }
#else
guard let identifier = segue.identifier?.rawValue else { return nil }
#endif
{% else %}
guard let identifier = segue.identifier else { return nil }
{% endif %}
self.init(rawValue: identifier)
}
}
{% elif storyboards %}
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
{% else %}
// No storyboard found
{% endif %}

View File

@ -0,0 +1,82 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if files %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - JSON Files
{% macro fileBlock file %}
{% call documentBlock file file.document %}
{% endmacro %}
{% macro documentBlock file document %}
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
{% if document.metadata.type == "Array" %}
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
{% elif document.metadata.type == "Dictionary" %}
{% for key,value in document.metadata.properties %}
{{accessModifier}} {% call propertyBlock key value document.data %}
{% endfor %}
{% else %}
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
{% endif %}
{% endmacro %}
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
{% if metadata.type == "Array" %}
[{% call typeBlock metadata.element %}]
{% elif metadata.type == "Dictionary" %}
[String: Any]
{% elif metadata.type == "Optional" %}
Any?
{% else %}
{{metadata.type}}
{% endif %}
{% endfilter %}{% endmacro %}
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
{% endfilter %}{% endmacro %}
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
{% if metadata.type == "String" %}
"{{ value }}"
{% elif metadata.type == "Optional" %}
nil
{% elif metadata.type == "Array" and value %}
[{% for value in value %}
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
{{ ", " if not forloop.last }}
{% endfor %}]
{% elif metadata.type == "Dictionary" %}
[{% for key,value in value %}
"{{key}}": {% call valueBlock value metadata.properties[key] %}
{{ ", " if not forloop.last }}
{% empty %}
:
{% endfor %}]
{% elif metadata.type == "Bool" %}
{% if value %}true{% else %}false{% endif %}
{% else %}
{{ value }}
{% endif %}
{% endfilter %}{% endmacro %}
// swiftlint:disable identifier_name line_length number_separator type_body_length
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
{% if files.count > 1 or param.forceFileNameEnum %}
{% for file in files %}
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call fileBlock files.first %}
{% endif %}
}
// swiftlint:enable identifier_name line_length number_separator type_body_length
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,82 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if files %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - JSON Files
{% macro fileBlock file %}
{% call documentBlock file file.document %}
{% endmacro %}
{% macro documentBlock file document %}
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
{% if document.metadata.type == "Array" %}
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
{% elif document.metadata.type == "Dictionary" %}
{% for key,value in document.metadata.properties %}
{{accessModifier}} {% call propertyBlock key value document.data %}
{% endfor %}
{% else %}
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
{% endif %}
{% endmacro %}
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
{% if metadata.type == "Array" %}
[{% call typeBlock metadata.element %}]
{% elif metadata.type == "Dictionary" %}
[String: Any]
{% elif metadata.type == "Optional" %}
Any?
{% else %}
{{metadata.type}}
{% endif %}
{% endfilter %}{% endmacro %}
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
{% endfilter %}{% endmacro %}
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
{% if metadata.type == "String" %}
"{{ value }}"
{% elif metadata.type == "Optional" %}
nil
{% elif metadata.type == "Array" and value %}
[{% for value in value %}
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
{{ ", " if not forloop.last }}
{% endfor %}]
{% elif metadata.type == "Dictionary" %}
[{% for key,value in value %}
"{{key}}": {% call valueBlock value metadata.properties[key] %}
{{ ", " if not forloop.last }}
{% empty %}
:
{% endfor %}]
{% elif metadata.type == "Bool" %}
{% if value %}true{% else %}false{% endif %}
{% else %}
{{ value }}
{% endif %}
{% endfilter %}{% endmacro %}
// swiftlint:disable identifier_name line_length number_separator type_body_length
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
{% if files.count > 1 or param.forceFileNameEnum %}
{% for file in files %}
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call fileBlock files.first %}
{% endif %}
}
// swiftlint:enable identifier_name line_length number_separator type_body_length
{% else %}
// No files found
{% endif %}

View File

@ -0,0 +1,112 @@
// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if files %}
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
import Foundation
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - JSON Files
{% macro fileBlock file %}
{% call documentBlock file file.document %}
{% endmacro %}
{% macro documentBlock file document %}
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
{% if document.metadata.type == "Array" %}
{{accessModifier}} static let items: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
{% elif document.metadata.type == "Dictionary" %}
private static let _document = JSONDocument(path: "{% call transformPath file.path %}")
{% for key,value in document.metadata.properties %}
{{accessModifier}} {% call propertyBlock key value %}
{% endfor %}
{% else %}
{{accessModifier}} static let value: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
{% endif %}
{% endmacro %}
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
{% if metadata.type == "Array" %}
[{% call typeBlock metadata.element %}]
{% elif metadata.type == "Dictionary" %}
[String: Any]
{% elif metadata.type == "Optional" %}
Any?
{% else %}
{{metadata.type}}
{% endif %}
{% endfilter %}{% endmacro %}
{% macro propertyBlock key metadata %}{% filter removeNewlines:"leading" %}
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
static let {{propertyName}}: {{propertyType}} = _document["{{key}}"]
{% endfilter %}{% endmacro %}
{% macro transformPath path %}{% filter removeNewlines %}
{% if param.preservePath %}
{{path}}
{% else %}
{{path|basename}}
{% endif %}
{% endfilter %}{% endmacro %}
// swiftlint:disable identifier_name line_length type_body_length
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
{% if files.count > 1 or param.forceFileNameEnum %}
{% for file in files %}
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
}
{% endfor %}
{% else %}
{% call fileBlock files.first %}
{% endif %}
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
private func objectFromJSON<T>(at path: String) -> T {
{% if param.lookupFunction %}
guard let url = {{param.lookupFunction}}(path),
{% else %}
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
{% endif %}
let json = try? JSONSerialization.jsonObject(with: Data(contentsOf: url), options: []),
let result = json as? T else {
fatalError("Unable to load JSON at path: \(path)")
}
return result
}
private struct JSONDocument {
let data: [String: Any]
init(path: String) {
self.data = objectFromJSON(at: path)
}
subscript<T>(key: String) -> T {
guard let result = data[key] as? T else {
fatalError("Property '\(key)' is not of type \(T.self)")
}
return result
}
}
{% if not param.bundle and not param.lookupFunction %}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% else %}
// No files found
{% endif %}

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