diff --git a/.gitignore b/.gitignore index 3c02fdc5..ad40381a 100644 --- a/.gitignore +++ b/.gitignore @@ -96,4 +96,4 @@ iOSInjectionProject/ .Trashes ehthumbs.db Thumbs.db -Shared/Generated/R.generated.swift +Shared/Generated/Strings.swift diff --git a/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift b/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift index ae4cd05d..4e6ef971 100644 --- a/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift +++ b/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift @@ -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" : R.string.localizable.play()) + Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : L10n.play) .font(.caption) } VStack { diff --git a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift index a3fc3127..1a7ec3c6 100644 --- a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift +++ b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift @@ -68,7 +68,7 @@ struct PortraitItemElement: View { .font(.caption) .fontWeight(.medium) } else { - Text(R.string.localizable.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0))) + Text(L10n.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0))) .foregroundColor(.secondary) .font(.caption) .fontWeight(.medium) diff --git a/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift b/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift index 6be6811f..7adcc3f0 100644 --- a/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift +++ b/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift @@ -22,7 +22,7 @@ struct BasicAppSettingsView: View { var body: some View { Form { Section { - Picker(R.string.localizable.appearance(), 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: { - R.string.localizable.accessibility.text + L10n.accessibility.text } Button { resetTapped = true } label: { - R.string.localizable.reset.text + L10n.reset.text } } - .alert(R.string.localizable.reset(), isPresented: $resetTapped, actions: { + .alert(L10n.reset, isPresented: $resetTapped, actions: { Button(role: .destructive) { viewModel.reset() basicAppSettingsRouter.dismissCoordinator() } label: { - R.string.localizable.reset.text + L10n.reset.text } }) .navigationTitle("Settings") diff --git a/JellyfinPlayer tvOS/Views/ConnectToServerView.swift b/JellyfinPlayer tvOS/Views/ConnectToServerView.swift index e3f3fb70..dbffe5f9 100644 --- a/JellyfinPlayer tvOS/Views/ConnectToServerView.swift +++ b/JellyfinPlayer tvOS/Views/ConnectToServerView.swift @@ -17,7 +17,7 @@ struct ConnectToServerView: View { var body: some View { List { Section { - TextField(R.string.localizable.serverURL(), 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 { - R.string.localizable.connect.text + L10n.connect.text Spacer() if viewModel.isLoading { ProgressView() @@ -37,7 +37,7 @@ struct ConnectToServerView: View { Text("Connect to a Jellyfin server") } - Section(header: R.string.localizable.localServers.text) { + 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(R.string.localizable.connect()) + .navigationTitle(L10n.connect) } } diff --git a/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift b/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift index 466adbad..f9ccf27a 100644 --- a/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift +++ b/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift @@ -20,7 +20,7 @@ struct ContinueWatchingView: View { var body: some View { VStack(alignment: .leading) { if items.count > 0 { - R.string.localizable.continueWatching.text + L10n.continueWatching.text .font(.headline) .fontWeight(.semibold) .padding(.leading, 90) diff --git a/JellyfinPlayer tvOS/Views/HomeView.swift b/JellyfinPlayer tvOS/Views/HomeView.swift index 6a85a821..46956a2e 100644 --- a/JellyfinPlayer tvOS/Views/HomeView.swift +++ b/JellyfinPlayer tvOS/Views/HomeView.swift @@ -38,7 +38,7 @@ struct HomeView: View { self.homeRouter.route(to: \.modalLibrary, (.init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? "")) } label: { HStack { - Text(R.string.localizable.latestWithString(library?.name ?? "")) + Text(L10n.latestWithString(library?.name ?? "")) .font(.headline) .fontWeight(.semibold) Image(systemName: "chevron.forward.circle.fill") diff --git a/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift index 88f66b49..d0632bf8 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift @@ -76,7 +76,7 @@ struct EpisodeItemView: View { HStack(alignment: .top) { VStack(alignment: .trailing) { if studio != nil { - R.string.localizable.studio.text + L10n.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -88,7 +88,7 @@ struct EpisodeItemView: View { } if director != nil { - R.string.localizable.director.text + L10n.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -100,7 +100,7 @@ struct EpisodeItemView: View { } if !actors.isEmpty { - R.string.localizable.cast.text + 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" : R.string.localizable.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 { - R.string.localizable.moreLikeThis.text + L10n.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift index db2a37e4..5f8353cf 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift @@ -40,7 +40,7 @@ struct ItemView: View { } else if item.type == "Episode" { EpisodeItemView(viewModel: .init(item: item)) } else { - Text(R.string.localizable.notImplementedYetWithType(item.type ?? "")) + Text(L10n.notImplementedYetWithType(item.type ?? "")) } } } diff --git a/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift index e9976be7..378c40da 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift @@ -77,7 +77,7 @@ struct MovieItemView: View { HStack { VStack(alignment: .trailing) { if studio != nil { - R.string.localizable.studio.text + L10n.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -89,7 +89,7 @@ struct MovieItemView: View { } if director != nil { - R.string.localizable.director.text + L10n.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -101,7 +101,7 @@ struct MovieItemView: View { } if !actors.isEmpty { - R.string.localizable.cast.text + L10n.cast.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -133,7 +133,7 @@ struct MovieItemView: View { }.padding(.top, 50) if !viewModel.similarItems.isEmpty { - R.string.localizable.moreLikeThis.text + L10n.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift index 64fe58cc..475102fd 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift @@ -95,7 +95,7 @@ struct SeasonItemView: View { }.padding(.top, 50) if !viewModel.episodes.isEmpty { - R.string.localizable.episodes.text + L10n.episodes.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift index 200f2e65..6a3a2ee3 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift @@ -79,7 +79,7 @@ struct SeriesItemView: View { HStack { VStack(alignment: .trailing) { if studio != nil { - R.string.localizable.studio.text + L10n.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -91,7 +91,7 @@ struct SeriesItemView: View { } if director != nil { - R.string.localizable.director.text + L10n.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -103,7 +103,7 @@ struct SeriesItemView: View { } if !actors.isEmpty { - R.string.localizable.cast.text + L10n.cast.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -135,7 +135,7 @@ struct SeriesItemView: View { } }.padding(.top, 50) if !viewModel.seasons.isEmpty { - R.string.localizable.seasons.text + L10n.seasons.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { @@ -153,7 +153,7 @@ struct SeriesItemView: View { } if !viewModel.similarItems.isEmpty { - R.string.localizable.moreLikeThis.text + L10n.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/LibraryFilterView.swift b/JellyfinPlayer tvOS/Views/LibraryFilterView.swift index 5e0503eb..a10a0cdc 100644 --- a/JellyfinPlayer tvOS/Views/LibraryFilterView.swift +++ b/JellyfinPlayer tvOS/Views/LibraryFilterView.swift @@ -31,32 +31,32 @@ struct LibraryFilterView: View { } else { Form { if viewModel.enabledFilterType.contains(.genre) { - MultiSelector(label: R.string.localizable.genres(), + MultiSelector(label: L10n.genres, options: viewModel.possibleGenres, optionToString: { $0.name ?? "" }, selected: $viewModel.modifiedFilters.withGenres) } if viewModel.enabledFilterType.contains(.filter) { - MultiSelector(label: R.string.localizable.filters(), + MultiSelector(label: L10n.filters, options: viewModel.possibleItemFilters, optionToString: { $0.localized }, selected: $viewModel.modifiedFilters.filters) } if viewModel.enabledFilterType.contains(.tag) { - MultiSelector(label: R.string.localizable.tags(), + MultiSelector(label: L10n.tags, options: viewModel.possibleTags, optionToString: { $0 }, selected: $viewModel.modifiedFilters.tags) } if viewModel.enabledFilterType.contains(.sortBy) { - Picker(selection: $viewModel.selectedSortBy, label: R.string.localizable.sortBy.text) { + 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: R.string.localizable.displayOrder.text) { + 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: { - R.string.localizable.reset.text + L10n.reset.text } } } @@ -86,7 +86,7 @@ struct LibraryFilterView: View { self.filters = viewModel.modifiedFilters filterRouter.dismissCoordinator() } label: { - R.string.localizable.apply.text + L10n.apply.text } } } diff --git a/JellyfinPlayer tvOS/Views/LibrarySearchView.swift b/JellyfinPlayer tvOS/Views/LibrarySearchView.swift index 85a7d8cd..2cc5c706 100644 --- a/JellyfinPlayer tvOS/Views/LibrarySearchView.swift +++ b/JellyfinPlayer tvOS/Views/LibrarySearchView.swift @@ -44,7 +44,7 @@ struct LibrarySearchView: View { var suggestionsListView: some View { ScrollView { LazyVStack(spacing: 8) { - R.string.localizable.suggestions.text + L10n.suggestions.text .font(.headline) .fontWeight(.bold) .foregroundColor(.primary) diff --git a/JellyfinPlayer tvOS/Views/LibraryView.swift b/JellyfinPlayer tvOS/Views/LibraryView.swift index d7fe3980..0f500499 100644 --- a/JellyfinPlayer tvOS/Views/LibraryView.swift +++ b/JellyfinPlayer tvOS/Views/LibraryView.swift @@ -88,7 +88,7 @@ struct LibraryView: View { .ignoresSafeArea(.all) } else { VStack { - R.string.localizable.noResults.text + L10n.noResults.text Button { } label: { Text("Reload") } diff --git a/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift b/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift index 22742484..86298af2 100644 --- a/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift +++ b/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift @@ -77,7 +77,7 @@ struct MovieLibrariesView: View { .ignoresSafeArea(.all) } else { VStack { - R.string.localizable.noResults.text + L10n.noResults.text Button { print("movieLibraries reload") } label: { diff --git a/JellyfinPlayer tvOS/Views/NextUpView.swift b/JellyfinPlayer tvOS/Views/NextUpView.swift index e60cfb50..6ad02d5e 100644 --- a/JellyfinPlayer tvOS/Views/NextUpView.swift +++ b/JellyfinPlayer tvOS/Views/NextUpView.swift @@ -19,7 +19,7 @@ struct NextUpView: View { var body: some View { VStack(alignment: .leading) { if items.count > 0 { - R.string.localizable.nextUp.text + L10n.nextUp.text .font(.headline) .fontWeight(.semibold) .padding(.leading, 90) diff --git a/JellyfinPlayer tvOS/Views/ServerListView.swift b/JellyfinPlayer tvOS/Views/ServerListView.swift index 4bb23f4b..cb653b84 100644 --- a/JellyfinPlayer tvOS/Views/ServerListView.swift +++ b/JellyfinPlayer tvOS/Views/ServerListView.swift @@ -72,7 +72,7 @@ struct ServerListView: View { Button { serverListRouter.route(to: \.connectToServer) } label: { - R.string.localizable.connect.text + L10n.connect.text .bold() .font(.callout) } diff --git a/JellyfinPlayer tvOS/Views/SettingsView.swift b/JellyfinPlayer tvOS/Views/SettingsView.swift index b63b31e6..394d6ac2 100644 --- a/JellyfinPlayer tvOS/Views/SettingsView.swift +++ b/JellyfinPlayer tvOS/Views/SettingsView.swift @@ -22,7 +22,7 @@ struct SettingsView: View { var body: some View { Form { - Section(header: R.string.localizable.playbackSettings()) { + Section(header: L10n.playbackSettings) { 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: R.string.localizable.accessibility.text) { + 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(R.string.localizable.signedInAsWithString(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: { - R.string.localizable.switchUser.text.font(.callout) + L10n.switchUser.text.font(.callout) } } Button { diff --git a/JellyfinPlayer tvOS/Views/TVLibrariesView.swift b/JellyfinPlayer tvOS/Views/TVLibrariesView.swift index c48a389b..fbf58d20 100644 --- a/JellyfinPlayer tvOS/Views/TVLibrariesView.swift +++ b/JellyfinPlayer tvOS/Views/TVLibrariesView.swift @@ -77,7 +77,7 @@ struct TVLibrariesView: View { .ignoresSafeArea(.all) } else { VStack { - R.string.localizable.noResults.text + L10n.noResults.text Button { print("tvLibraries reload") } label: { diff --git a/JellyfinPlayer tvOS/Views/UserSignInView.swift b/JellyfinPlayer tvOS/Views/UserSignInView.swift index e07baeb9..9780beb0 100644 --- a/JellyfinPlayer tvOS/Views/UserSignInView.swift +++ b/JellyfinPlayer tvOS/Views/UserSignInView.swift @@ -20,11 +20,11 @@ struct UserSignInView: View { Form { Section { - TextField(R.string.localizable.username(), text: $username) + TextField(L10n.username, text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField(R.string.localizable.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 { - R.string.localizable.connect.text + L10n.connect.text Spacer() if viewModel.isLoading { ProgressView() diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index 76f16358..eec8530e 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -157,7 +157,6 @@ 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 */; }; - 62167B882738411700167ECE /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A0271C0AA500C40ED5 /* R.generated.swift */; }; 6220D0AD26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; 6220D0AE26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; 6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; @@ -177,6 +176,9 @@ 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 */; }; @@ -184,10 +186,6 @@ 6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; }; 6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; }; 6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; }; - 6286F0A1271C0AA500C40ED5 /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A0271C0AA500C40ED5 /* R.generated.swift */; }; - 6286F0A2271C0AA500C40ED5 /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A0271C0AA500C40ED5 /* R.generated.swift */; }; - 6286F0A6271C0EB700C40ED5 /* R.swift+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A5271C0EB700C40ED5 /* R.swift+SwiftUI.swift */; }; - 6286F0A7271C0EB700C40ED5 /* R.swift+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A5271C0EB700C40ED5 /* R.swift+SwiftUI.swift */; }; 628B95242670CABD0091AF3B /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 628B95232670CABD0091AF3B /* SwiftUI.framework */; }; 628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 628B95262670CABD0091AF3B /* NextUpWidget.swift */; }; 628B95292670CABE0091AF3B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 628B95282670CABE0091AF3B /* Assets.xcassets */; }; @@ -521,10 +519,9 @@ 625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerViewModel.swift; sourceTree = ""; }; 625CB57B2678CE1000530A6E /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 625CB57D2678E81E00530A6E /* TVVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TVVLCKit.xcframework; path = Carthage/Build/TVVLCKit.xcframework; sourceTree = ""; }; + 6264E88B273850380081A12A /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 6267B3D526710B8900A7371D /* CollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtensions.swift; sourceTree = ""; }; 6267B3D92671138200A7371D /* ImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageExtensions.swift; sourceTree = ""; }; - 6286F0A0271C0AA500C40ED5 /* R.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = R.generated.swift; sourceTree = ""; }; - 6286F0A5271C0EB700C40ED5 /* R.swift+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "R.swift+SwiftUI.swift"; sourceTree = ""; }; 628B95202670CABD0091AF3B /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 628B95212670CABD0091AF3B /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; 628B95232670CABD0091AF3B /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; @@ -1069,7 +1066,6 @@ 621338922660107500A81A2A /* StringExtensions.swift */, E13DD3C727164B1E009D4DAF /* UIDeviceExtensions.swift */, 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */, - 6286F0A5271C0EB700C40ED5 /* R.swift+SwiftUI.swift */, ); path = Extensions; sourceTree = ""; @@ -1077,7 +1073,7 @@ 6286F09F271C0AA500C40ED5 /* Generated */ = { isa = PBXGroup; children = ( - 6286F0A0271C0AA500C40ED5 /* R.generated.swift */, + 6264E88B273850380081A12A /* Strings.swift */, ); path = Generated; sourceTree = ""; @@ -1648,17 +1644,15 @@ inputFileListPaths = ( ); inputPaths = ( - "$TEMP_DIR/rswift-lastrun", ); name = R.swift; outputFileListPaths = ( ); outputPaths = ( - $SRCROOT/Shared/Generated/R.generated.swift, ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ $ACTION != \"indexbuild\" ]; then\n \"$PODS_ROOT/R.swift/rswift\" generate \"$SRCROOT/Shared/Generated/R.generated.swift\" --generators string\nfi\n"; + 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; @@ -1668,17 +1662,15 @@ inputFileListPaths = ( ); inputPaths = ( - "$TEMP_DIR/rswift-lastrun", ); name = R.swift; outputFileListPaths = ( ); outputPaths = ( - $SRCROOT/Shared/Generated/R.generated.swift, ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ $ACTION != \"indexbuild\" ]; then\n \"$PODS_ROOT/R.swift/rswift\" generate \"$SRCROOT/Shared/Generated/R.generated.swift\" --generators string\nfi\n"; + 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; @@ -1786,7 +1778,6 @@ 53CD2A40268A49C2002ABD4E /* ItemView.swift in Sources */, 53CD2A42268A4B38002ABD4E /* MovieItemView.swift in Sources */, 536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */, - 6286F0A7271C0EB700C40ED5 /* R.swift+SwiftUI.swift in Sources */, 091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */, E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */, 531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */, @@ -1819,7 +1810,6 @@ E1D4BF852719D25A00A11E64 /* TrackLanguage.swift in Sources */, 53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */, 531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */, - 6286F0A2271C0AA500C40ED5 /* R.generated.swift in Sources */, E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */, E193D5502719430400900D82 /* ServerDetailView.swift in Sources */, E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */, @@ -1857,6 +1847,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 */, @@ -1927,6 +1918,7 @@ 625CB56F2678C23300530A6E /* HomeView.swift in Sources */, E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */, 53892770263C25230035E14B /* NextUpView.swift in Sources */, + 6264E88C273850380081A12A /* Strings.swift in Sources */, C4BE0766271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */, 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */, 535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */, @@ -1940,7 +1932,6 @@ 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */, E188460026DECB9E00B0C5B7 /* ItemLandscapeTopBarView.swift in Sources */, 091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */, - 6286F0A1271C0AA500C40ED5 /* R.generated.swift in Sources */, 6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */, E13DD3F5271793BB009D4DAF /* UserSignInView.swift in Sources */, E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */, @@ -1962,7 +1953,6 @@ E1D4BF812719D22800A11E64 /* AppAppearance.swift in Sources */, 621338B32660A07800A81A2A /* LazyView.swift in Sources */, 6220D0B126D5EC9900B8E046 /* SettingsCoordinator.swift in Sources */, - 6286F0A6271C0EB700C40ED5 /* R.swift+SwiftUI.swift in Sources */, 62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */, 531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */, E13DD3E927177ED6009D4DAF /* ServerListCoordinator.swift in Sources */, @@ -2025,10 +2015,10 @@ 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 */, - 62167B882738411700167ECE /* R.generated.swift in Sources */, E13DD3F72717E87D009D4DAF /* SwiftfinNotificationCenter.swift in Sources */, E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */, 6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */, diff --git a/JellyfinPlayer.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/JellyfinPlayer.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..21ba9472 --- /dev/null +++ b/JellyfinPlayer.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + DisableBuildSystemDeprecationDiagnostic + + PreviewsEnabled + + + diff --git a/JellyfinPlayer/Components/PortraitItemView.swift b/JellyfinPlayer/Components/PortraitItemView.swift index 2bd276b5..cf638bad 100644 --- a/JellyfinPlayer/Components/PortraitItemView.swift +++ b/JellyfinPlayer/Components/PortraitItemView.swift @@ -75,7 +75,7 @@ struct PortraitItemView: View { .font(.caption) .fontWeight(.medium) } else { - Text(R.string.localizable.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0)) + Text(L10n.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0))) .foregroundColor(.secondary) .font(.caption) .fontWeight(.medium) diff --git a/JellyfinPlayer/Views/BasicAppSettingsView.swift b/JellyfinPlayer/Views/BasicAppSettingsView.swift index 72e0ccca..0c57dd42 100644 --- a/JellyfinPlayer/Views/BasicAppSettingsView.swift +++ b/JellyfinPlayer/Views/BasicAppSettingsView.swift @@ -23,7 +23,7 @@ struct BasicAppSettingsView: View { var body: some View { Form { Section { - Picker(R.string.localizable.appearance(), 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: { - R.string.localizable.accessibility.text + L10n.accessibility.text } Section { @@ -47,15 +47,15 @@ struct BasicAppSettingsView: View { Button { resetTapped = true } label: { - R.string.localizable.reset.text + L10n.reset.text } } - .alert(R.string.localizable.reset(), isPresented: $resetTapped, actions: { + .alert(L10n.reset, isPresented: $resetTapped, actions: { Button(role: .destructive) { viewModel.reset() basicAppSettingsRouter.dismissCoordinator() } label: { - R.string.localizable.reset.text + L10n.reset.text } }) .navigationBarTitle("Settings", displayMode: .inline) diff --git a/JellyfinPlayer/Views/ConnectToServerView.swift b/JellyfinPlayer/Views/ConnectToServerView.swift index 0d5a4e69..f91b9d85 100644 --- a/JellyfinPlayer/Views/ConnectToServerView.swift +++ b/JellyfinPlayer/Views/ConnectToServerView.swift @@ -20,7 +20,7 @@ struct ConnectToServerView: View { var body: some View { List { Section { - TextField(R.string.localizable.serverURL(), 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: { - R.string.localizable.connect.text + L10n.connect.text } .disabled(uri.isEmpty) } @@ -88,7 +88,7 @@ struct ConnectToServerView: View { } } header: { HStack { - R.string.localizable.localServers.text + L10n.localServers.text Spacer() Button { @@ -106,7 +106,7 @@ struct ConnectToServerView: View { message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"), dismissButton: .cancel()) } - .navigationTitle(R.string.localizable.connect()) + .navigationTitle(L10n.connect) .onAppear { viewModel.discoverServers() AppURLHandler.shared.appURLState = .allowedInLogin diff --git a/JellyfinPlayer/Views/HomeView.swift b/JellyfinPlayer/Views/HomeView.swift index 58997777..f5fb2da5 100644 --- a/JellyfinPlayer/Views/HomeView.swift +++ b/JellyfinPlayer/Views/HomeView.swift @@ -34,7 +34,7 @@ struct HomeView: View { ForEach(viewModel.libraries, id: \.self) { library in HStack { - Text(R.string.localizable.latestWithString(library.name ?? "")) + Text(L10n.latestWithString(library.name ?? "")) .font(.title2) .fontWeight(.bold) Spacer() @@ -45,7 +45,7 @@ struct HomeView: View { title: library.name ?? "")) } label: { HStack { - R.string.localizable.seeAll.text.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(R.string.localizable.home()) + .navigationTitle(L10n.home) .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { Button { diff --git a/JellyfinPlayer/Views/ItemView/ItemViewBody.swift b/JellyfinPlayer/Views/ItemView/ItemViewBody.swift index 3b973fa5..3df1d559 100644 --- a/JellyfinPlayer/Views/ItemView/ItemViewBody.swift +++ b/JellyfinPlayer/Views/ItemView/ItemViewBody.swift @@ -29,7 +29,7 @@ struct ItemViewBody: View { PortraitImageHStackView(items: seriesViewModel.seasons, maxWidth: 150, topBarView: { - R.string.localizable.seasons.text + L10n.seasons.text .font(.callout) .fontWeight(.semibold) .padding(.top, 3) @@ -41,7 +41,7 @@ struct ItemViewBody: View { // MARK: Genres - PillHStackView(title: R.string.localizable.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: R.string.localizable.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: { - R.string.localizable.moreLikeThis.text + L10n.moreLikeThis.text .font(.callout) .fontWeight(.semibold) .padding(.top, 3) diff --git a/JellyfinPlayer/Views/LibraryFilterView.swift b/JellyfinPlayer/Views/LibraryFilterView.swift index 228efd35..532b9e95 100644 --- a/JellyfinPlayer/Views/LibraryFilterView.swift +++ b/JellyfinPlayer/Views/LibraryFilterView.swift @@ -31,32 +31,32 @@ struct LibraryFilterView: View { } else { Form { if viewModel.enabledFilterType.contains(.genre) { - MultiSelector(label: R.string.localizable.genres(), + MultiSelector(label: L10n.genres, options: viewModel.possibleGenres, optionToString: { $0.name ?? "" }, selected: $viewModel.modifiedFilters.withGenres) } if viewModel.enabledFilterType.contains(.filter) { - MultiSelector(label: R.string.localizable.filters(), + MultiSelector(label: L10n.filters, options: viewModel.possibleItemFilters, optionToString: { $0.localized }, selected: $viewModel.modifiedFilters.filters) } if viewModel.enabledFilterType.contains(.tag) { - MultiSelector(label: R.string.localizable.tags(), + MultiSelector(label: L10n.tags, options: viewModel.possibleTags, optionToString: { $0 }, selected: $viewModel.modifiedFilters.tags) } if viewModel.enabledFilterType.contains(.sortBy) { - Picker(selection: $viewModel.selectedSortBy, label: R.string.localizable.sortBy.text) { + 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: R.string.localizable.displayOrder.text) { + 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: { - R.string.localizable.reset.text + L10n.reset.text } } } - .navigationBarTitle(R.string.localizable.filterResults(), 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: { - R.string.localizable.apply.text + L10n.apply.text } } } diff --git a/JellyfinPlayer/Views/LibraryListView.swift b/JellyfinPlayer/Views/LibraryListView.swift index 6861eef7..83bbd080 100644 --- a/JellyfinPlayer/Views/LibraryListView.swift +++ b/JellyfinPlayer/Views/LibraryListView.swift @@ -23,7 +23,7 @@ struct LibraryListView: View { ZStack { HStack { Spacer() - R.string.localizable.yourFavorites.text + L10n.yourFavorites.text .foregroundColor(.black) .font(.subheadline) .fontWeight(.semibold) @@ -39,12 +39,12 @@ struct LibraryListView: View { .padding(.bottom, 5) NavigationLink(destination: LazyView { - R.string.localizable.wip.text + L10n.wip.text }) { ZStack { HStack { Spacer() - R.string.localizable.allGenres.text + L10n.allGenres.text .foregroundColor(.black) .font(.subheadline) .fontWeight(.semibold) @@ -98,7 +98,7 @@ struct LibraryListView: View { .padding(.trailing, 16) .padding(.top, 8) } - .navigationTitle(R.string.localizable.allMedia()) + .navigationTitle(L10n.allMedia) .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { Button { diff --git a/JellyfinPlayer/Views/LibrarySearchView.swift b/JellyfinPlayer/Views/LibrarySearchView.swift index 54b13ce0..632a77ef 100644 --- a/JellyfinPlayer/Views/LibrarySearchView.swift +++ b/JellyfinPlayer/Views/LibrarySearchView.swift @@ -46,7 +46,7 @@ struct LibrarySearchView: View { var suggestionsListView: some View { ScrollView { LazyVStack(spacing: 8) { - R.string.localizable.suggestions.text + L10n.suggestions.text .font(.headline) .fontWeight(.bold) .foregroundColor(.primary) diff --git a/JellyfinPlayer/Views/LibraryView.swift b/JellyfinPlayer/Views/LibraryView.swift index 1cd93a2f..62883cf0 100644 --- a/JellyfinPlayer/Views/LibraryView.swift +++ b/JellyfinPlayer/Views/LibraryView.swift @@ -55,7 +55,7 @@ struct LibraryView: View { Image(systemName: "chevron.left") .font(.system(size: 25)) }.disabled(!viewModel.hasPreviousPage) - Text(R.string.localizable.pageOfWithNumbers(String(viewModel.currentPage + 1), 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 { - R.string.localizable.noResults.text + L10n.noResults.text } } .navigationBarTitle(title, displayMode: .inline) diff --git a/JellyfinPlayer/Views/LoadingView.swift b/JellyfinPlayer/Views/LoadingView.swift index 850aa90b..be7b67a2 100644 --- a/JellyfinPlayer/Views/LoadingView.swift +++ b/JellyfinPlayer/Views/LoadingView.swift @@ -32,7 +32,7 @@ struct LoadingView: View where Content: View { // indicator, with some text underneath showing what we are doing HStack { ProgressView() - Text(text ?? R.string.localizable.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: View where Content: View { // indicator, with some text underneath showing what we are doing HStack { ProgressView() - Text(text ?? R.string.localizable.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)) diff --git a/JellyfinPlayer/Views/NextUpView.swift b/JellyfinPlayer/Views/NextUpView.swift index d871fc8e..0a556eb0 100644 --- a/JellyfinPlayer/Views/NextUpView.swift +++ b/JellyfinPlayer/Views/NextUpView.swift @@ -17,7 +17,7 @@ struct NextUpView: View { var body: some View { VStack(alignment: .leading) { - R.string.localizable.nextUp.text + L10n.nextUp.text .font(.title2) .fontWeight(.bold) .padding(.leading, 16) diff --git a/JellyfinPlayer/Views/ServerListView.swift b/JellyfinPlayer/Views/ServerListView.swift index e62d3f6c..07fa6af9 100644 --- a/JellyfinPlayer/Views/ServerListView.swift +++ b/JellyfinPlayer/Views/ServerListView.swift @@ -81,7 +81,7 @@ struct ServerListView: View { .padding(.horizontal, 30) .padding([.top, .bottom], 20) - R.string.localizable.connect.text + L10n.connect.text .foregroundColor(Color.white) .bold() } diff --git a/JellyfinPlayer/Views/SettingsView.swift b/JellyfinPlayer/Views/SettingsView.swift index db429f9e..ef9a0916 100644 --- a/JellyfinPlayer/Views/SettingsView.swift +++ b/JellyfinPlayer/Views/SettingsView.swift @@ -108,7 +108,7 @@ struct SettingsView: View { } } - Section(header: R.string.localizable.accessibility.text) { + 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(R.string.localizable.appearance(), selection: $appAppearance) { + Picker(L10n.appearance, selection: $appAppearance) { ForEach(self.viewModel.appearances, id: \.self) { appearance in Text(appearance.localizedName).tag(appearance.rawValue) } diff --git a/JellyfinPlayer/Views/UserSignInView.swift b/JellyfinPlayer/Views/UserSignInView.swift index 777fa6e2..84544533 100644 --- a/JellyfinPlayer/Views/UserSignInView.swift +++ b/JellyfinPlayer/Views/UserSignInView.swift @@ -20,11 +20,11 @@ struct UserSignInView: View { Form { Section { - TextField(R.string.localizable.username(), text: $username) + TextField(L10n.username, text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField(R.string.localizable.password(), text: $password) + SecureField(L10n.password, text: $password) .disableAutocorrection(true) .autocapitalization(.none) diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift index 6094d417..e5c36f61 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift @@ -421,7 +421,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe if manifest.type == "Movie" { titleLabel.text = manifest.name ?? "" } else { - titleLabel.text = "\(R.string.localizable.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" + titleLabel.text = "\(L10n.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" setupNextUpView() upNextViewModel.delegate = self @@ -816,7 +816,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe shouldShowLoadingScreen = true videoControlsView.isHidden = true - titleLabel.text = "\(R.string.localizable.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" + titleLabel.text = "\(L10n.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" setupMediaPlayer() getNextEpisode() diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift index 7cb7f13b..2f018c88 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift @@ -56,7 +56,7 @@ struct VideoPlayerCastDeviceSelector: View { delegate?.castPopoverDismissed() } label: { HStack { - R.string.localizable.connect.text + L10n.connect.text .font(.caption) .fontWeight(.medium) Image(systemName: "bonjour") @@ -66,14 +66,14 @@ struct VideoPlayerCastDeviceSelector: View { } } } else { - R.string.localizable.noCastdevicesfound.text + L10n.noCastdevicesfound.text .foregroundColor(.secondary) .font(.subheadline) .fontWeight(.medium) } } .navigationBarTitleDisplayMode(.inline) - .navigationTitle(R.string.localizable.selectCastDestination()) + .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") - R.string.localizable.back.text.font(.callout) + L10n.back.text.font(.callout) } } } diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift index dad7ccb7..fb73836a 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift @@ -40,7 +40,7 @@ struct VideoPlayerSettings: View { var body: some View { Form { - Picker(R.string.localizable.closedCaptions(), 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(R.string.localizable.audioTrack(), 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(R.string.localizable.playbackSpeed(), 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(R.string.localizable.audioAndCaptions()) + .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") - R.string.localizable.back.text.font(.callout) + L10n.back.text.font(.callout) } } } diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift b/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift index 8a8e2722..0f886d2c 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift @@ -32,7 +32,7 @@ struct VideoUpNextView: View { } label: { HStack { VStack { - R.string.localizable.playNext.text + L10n.playNext.text .foregroundColor(.white) .font(.subheadline) .fontWeight(.semibold) diff --git a/Podfile b/Podfile index d685a2f6..42260767 100644 --- a/Podfile +++ b/Podfile @@ -1,7 +1,7 @@ use_frameworks! inhibit_all_warnings! def shared_pods - pod 'R.swift' + pod 'SwiftGen' end target 'JellyfinPlayer iOS' do @@ -14,4 +14,7 @@ target 'JellyfinPlayer tvOS' do platform :tvos, '14.0' shared_pods pod 'TVVLCKit' +end +target 'WidgetExtension' do + shared_pods end \ No newline at end of file diff --git a/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift b/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift index 81bd6fea..1c16c485 100644 --- a/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift +++ b/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift @@ -26,7 +26,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeHomeTab(isActive: Bool) -> some View { Image(systemName: "house") - R.string.localizable.home.text + L10n.home.text } func makeAllMedia() -> NavigationViewCoordinator { @@ -35,7 +35,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeAllMediaTab(isActive: Bool) -> some View { Image(systemName: "folder") - R.string.localizable.allMedia.text + L10n.allMedia.text } @ViewBuilder func customize(_ view: AnyView) -> some View { diff --git a/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift b/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift index 906b1a18..3f3877d0 100644 --- a/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift +++ b/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift @@ -33,7 +33,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeHomeTab(isActive: Bool) -> some View { HStack { Image(systemName: "house") - R.string.localizable.home.text + L10n.home.text } } diff --git a/Shared/Errors/NetworkError.swift b/Shared/Errors/NetworkError.swift index 69447991..0905158c 100644 --- a/Shared/Errors/NetworkError.swift +++ b/Shared/Errors/NetworkError.swift @@ -85,13 +85,13 @@ enum NetworkError: Error { logMessage = "Cannot connect to host." logConstructor.message = logMessage errorMessage = ErrorMessage(code: err._code, - title: R.string.localizable.error(), + title: L10n.error, displayMessage: displayMessage, logConstructor: logConstructor) default: logConstructor.message = logMessage errorMessage = ErrorMessage(code: err._code, - title: R.string.localizable.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: R.string.localizable.error(), + title: L10n.error, displayMessage: displayMessage, logConstructor: logConstructor) } @@ -140,7 +140,7 @@ enum NetworkError: Error { default: logConstructor.message = logMessage errorMessage = ErrorMessage(code: code, - title: R.string.localizable.error(), + title: L10n.error, displayMessage: displayMessage, logConstructor: logConstructor) } diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift index 525abc09..da06e0ba 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift @@ -80,7 +80,7 @@ public extension BaseItemDto { func getEpisodeLocator() -> String? { if let seasonNo = parentIndexNumber, let episodeNo = indexNumber { - return R.string.localizable.seasonAndEpisode(String(seasonNo), String(episodeNo)) + return L10n.seasonAndEpisode(String(seasonNo), String(episodeNo)) } return nil } diff --git a/Shared/Extensions/R.swift+SwiftUI.swift b/Shared/Extensions/R.swift+SwiftUI.swift deleted file mode 100644 index cbf453da..00000000 --- a/Shared/Extensions/R.swift+SwiftUI.swift +++ /dev/null @@ -1,39 +0,0 @@ -// - /* - * SwiftFin is subject to the terms of the Mozilla Public - * License, v2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * Copyright 2021 Aiden Vigue & Jellyfin Contributors - */ - -import Rswift -import SwiftUI - -extension FontResource { - func font(size: CGFloat) -> Font { - Font.custom(fontName, size: size) - } -} - -extension ColorResource { - var color: Color { - Color(name) - } -} - -extension StringResource { - var localizedStringKey: LocalizedStringKey { - LocalizedStringKey(key) - } - - var text: Text { - Text(localizedStringKey) - } -} - -extension ImageResource { - var image: Image { - Image(name) - } -} diff --git a/Shared/Extensions/StringExtensions.swift b/Shared/Extensions/StringExtensions.swift index 7261ddb9..b442b0b5 100644 --- a/Shared/Extensions/StringExtensions.swift +++ b/Shared/Extensions/StringExtensions.swift @@ -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) + } } diff --git a/Shared/Generated/Strings.swift b/Shared/Generated/Strings.swift new file mode 100644 index 00000000..792f5bd1 --- /dev/null +++ b/Shared/Generated/Strings.swift @@ -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 diff --git a/Shared/Objects/AppAppearance.swift b/Shared/Objects/AppAppearance.swift index d71e6e10..fdf6ba8d 100644 --- a/Shared/Objects/AppAppearance.swift +++ b/Shared/Objects/AppAppearance.swift @@ -18,11 +18,11 @@ enum AppAppearance: String, CaseIterable, Defaults.Serializable { var localizedName: String { switch self { case .system: - return R.string.localizable.system() + return L10n.system case .dark: - return R.string.localizable.dark() + return L10n.dark case .light: - return R.string.localizable.light() + return L10n.light } } diff --git a/Shared/Objects/Typings.swift b/Shared/Objects/Typings.swift index 9860cb1d..60d7a646 100644 --- a/Shared/Objects/Typings.swift +++ b/Shared/Objects/Typings.swift @@ -77,7 +77,7 @@ enum ItemType: String { var localized: String { switch self { case .episode: - return R.string.localizable.episodes() + return L10n.episodes case .movie: return "Movies" case .series: diff --git a/Shared/ViewModels/ItemViewModel.swift b/Shared/ViewModels/ItemViewModel.swift index 334d130a..473f8436 100644 --- a/Shared/ViewModels/ItemViewModel.swift +++ b/Shared/ViewModels/ItemViewModel.swift @@ -35,7 +35,7 @@ class ItemViewModel: ViewModel { } func playButtonText() -> String { - return item.getItemProgressString() == "" ? R.string.localizable.play() : item.getItemProgressString() + return item.getItemProgressString() == "" ? L10n.play : item.getItemProgressString() } func getItemDisplayName() -> String { diff --git a/Shared/ViewModels/SeasonItemViewModel.swift b/Shared/ViewModels/SeasonItemViewModel.swift index 0dd9c237..b33992aa 100644 --- a/Shared/ViewModels/SeasonItemViewModel.swift +++ b/Shared/ViewModels/SeasonItemViewModel.swift @@ -24,8 +24,8 @@ final class SeasonItemViewModel: ItemViewModel { } override func playButtonText() -> String { - guard let playButtonItem = playButtonItem else { return R.string.localizable.play() } - guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return R.string.localizable.play() } + guard let playButtonItem = playButtonItem else { return L10n.play } + guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return L10n.play } return episodeLocator } diff --git a/Shared/ViewModels/SeriesItemViewModel.swift b/Shared/ViewModels/SeriesItemViewModel.swift index 80a3ccaf..5ee679b3 100644 --- a/Shared/ViewModels/SeriesItemViewModel.swift +++ b/Shared/ViewModels/SeriesItemViewModel.swift @@ -23,8 +23,8 @@ final class SeriesItemViewModel: ItemViewModel { } override func playButtonText() -> String { - guard let playButtonItem = playButtonItem else { return R.string.localizable.play() } - guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return R.string.localizable.play() } + guard let playButtonItem = playButtonItem else { return L10n.play } + guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return L10n.play } return episodeLocator } diff --git a/Shared/Views/SearchBarView.swift b/Shared/Views/SearchBarView.swift index de0c098d..ce6db7f9 100644 --- a/Shared/Views/SearchBarView.swift +++ b/Shared/Views/SearchBarView.swift @@ -16,7 +16,7 @@ struct SearchBar: View { var body: some View { HStack(spacing: 8) { - TextField(R.string.localizable.search(), text: $text) + TextField(L10n.search, text: $text) .padding(8) .padding(.horizontal, 16) #if os(iOS) diff --git a/WidgetExtension/NextUpWidget.swift b/WidgetExtension/NextUpWidget.swift index a72f06dd..aa6db419 100644 --- a/WidgetExtension/NextUpWidget.swift +++ b/WidgetExtension/NextUpWidget.swift @@ -135,7 +135,7 @@ struct NextUpEntryView: View { } .background(Color.blue) } else if entry.items.isEmpty { - R.string.localizable.emptyNextUp() + L10n.emptyNextUp.text .font(.body) .bold() .foregroundColor(.primary) @@ -214,7 +214,7 @@ extension NextUpEntryView { .fontWeight(.semibold) .foregroundColor(.primary) .lineLimit(1) - Text("\(item.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(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) @@ -242,7 +242,7 @@ extension NextUpEntryView { .foregroundColor(.primary) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - Text("\(item.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(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) @@ -304,7 +304,7 @@ extension NextUpEntryView { .fontWeight(.semibold) .foregroundColor(.white) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - Text("\(firstItem.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(firstItem.0.parentIndexNumber ?? 0), String(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) @@ -346,7 +346,7 @@ struct NextUpWidget: Widget { provider: NextUpWidgetProvider()) { entry in NextUpEntryView(entry: entry) } - .configurationDisplayName(R.string.localizable.nextUp()) + .configurationDisplayName(L10n.nextUp) .description("Keep watching where you left off or see what's up next.") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) } diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Info.plist b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Info.plist new file mode 100644 index 00000000..01d943f2 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Info.plist @@ -0,0 +1,42 @@ + + + + + BuildMachineOSBuild + + CFBundleDevelopmentRegion + en + CFBundleExecutable + SwiftGen_SwiftGenCLI + CFBundleIdentifier + SwiftGen.SwiftGenCLI.resources + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + SwiftGen_SwiftGenCLI + CFBundlePackageType + BNDL + CFBundleSupportedPlatforms + + MacOSX + + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 13A233 + DTPlatformName + macosx + DTPlatformVersion + 11.3 + DTSDKBuild + 20E214 + DTSDKName + macosx11.3 + DTXcode + 1300 + DTXcodeBuild + 13A233 + LSMinimumSystemVersion + 10.11 + + diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift4.stencil new file mode 100644 index 00000000..af604776 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift4.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift5.stencil new file mode 100644 index 00000000..af604776 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/literals-swift5.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift4.stencil new file mode 100644 index 00000000..57c2d797 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift4.stencil @@ -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 %} + /// + /// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}}
(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift5.stencil new file mode 100644 index 00000000..57c2d797 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/colors/swift5.stencil @@ -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 %} + /// + /// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}}
(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift4.stencil new file mode 100644 index 00000000..9832876e --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift4.stencil @@ -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 diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift5.stencil new file mode 100644 index 00000000..9832876e --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/coredata/swift5.stencil @@ -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 diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift4.stencil new file mode 100644 index 00000000..09df24de --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift4.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift5.stencil new file mode 100644 index 00000000..09df24de --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/flat-swift5.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift4.stencil new file mode 100644 index 00000000..6d6db960 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift4.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift5.stencil new file mode 100644 index 00000000..6d6db960 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/files/structured-swift5.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift4.stencil new file mode 100644 index 00000000..744d6a4c --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift4.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift5.stencil new file mode 100644 index 00000000..5a268b58 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/fonts/swift5.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift4.stencil new file mode 100644 index 00000000..9ad52fff --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift4.stencil @@ -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 { + {{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 { + {{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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift5.stencil new file mode 100644 index 00000000..5f29f8ba --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/scenes-swift5.stencil @@ -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 { + {{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 { + {{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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift4.stencil new file mode 100644 index 00000000..476d5464 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift4.stencil @@ -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(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift5.stencil new file mode 100644 index 00000000..476d5464 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/ib/segues-swift5.stencil @@ -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(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift4.stencil new file mode 100644 index 00000000..62ca48db --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift4.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift5.stencil new file mode 100644 index 00000000..62ca48db --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/inline-swift5.stencil @@ -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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift4.stencil new file mode 100644 index 00000000..c2466c7e --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift4.stencil @@ -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(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(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift5.stencil new file mode 100644 index 00000000..c2466c7e --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/json/runtime-swift5.stencil @@ -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(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(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift4.stencil new file mode 100644 index 00000000..c8e88310 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift4.stencil @@ -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: - Plist 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] + {% 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 == "Date" %} + Date(timeIntervalSinceReferenceDate: {{ value.timeIntervalSinceReferenceDate }}) + {% 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:"PlistFiles"}} { + {% 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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift5.stencil new file mode 100644 index 00000000..c8e88310 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/inline-swift5.stencil @@ -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: - Plist 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] + {% 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 == "Date" %} + Date(timeIntervalSinceReferenceDate: {{ value.timeIntervalSinceReferenceDate }}) + {% 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:"PlistFiles"}} { + {% 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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift4.stencil new file mode 100644 index 00000000..a498a8f2 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift4.stencil @@ -0,0 +1,117 @@ +// 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: - Plist 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}} = arrayFromPlist(at: "{% call transformPath file.path %}") + {% elif document.metadata.type == "Dictionary" %} + private static let _document = PlistDocument(path: "{% call transformPath file.path %}") + + {% for key,value in document.metadata.properties %} + {{accessModifier}} {% call propertyBlock key value %} + {% endfor %} + {% else %} + // Unsupported root type `{{rootType}}` + {% endif %} +{% endmacro %} +{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %} + {% if metadata.type == "Array" %} + [{% call typeBlock metadata.element %}] + {% elif metadata.type == "Dictionary" %} + [String: 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:"PlistFiles"}} { + {% 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 arrayFromPlist(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 data = NSArray(contentsOf: url) as? [T] else { + fatalError("Unable to load PLIST at path: \(path)") + } + return data +} + +private struct PlistDocument { + let data: [String: Any] + + init(path: String) { + {% 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 data = NSDictionary(contentsOf: url) as? [String: Any] else { + fatalError("Unable to load PLIST at path: \(path)") + } + self.data = data + } + + subscript(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift5.stencil new file mode 100644 index 00000000..a498a8f2 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/plist/runtime-swift5.stencil @@ -0,0 +1,117 @@ +// 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: - Plist 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}} = arrayFromPlist(at: "{% call transformPath file.path %}") + {% elif document.metadata.type == "Dictionary" %} + private static let _document = PlistDocument(path: "{% call transformPath file.path %}") + + {% for key,value in document.metadata.properties %} + {{accessModifier}} {% call propertyBlock key value %} + {% endfor %} + {% else %} + // Unsupported root type `{{rootType}}` + {% endif %} +{% endmacro %} +{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %} + {% if metadata.type == "Array" %} + [{% call typeBlock metadata.element %}] + {% elif metadata.type == "Dictionary" %} + [String: 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:"PlistFiles"}} { + {% 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 arrayFromPlist(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 data = NSArray(contentsOf: url) as? [T] else { + fatalError("Unable to load PLIST at path: \(path)") + } + return data +} + +private struct PlistDocument { + let data: [String: Any] + + init(path: String) { + {% 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 data = NSDictionary(contentsOf: url) as? [String: Any] else { + fatalError("Unable to load PLIST at path: \(path)") + } + self.data = data + } + + subscript(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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift4.stencil new file mode 100644 index 00000000..5bb4a128 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift4.stencil @@ -0,0 +1,99 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Strings + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + _ p{{forloop.counter}}: Any + {% else %} + _ p{{forloop.counter}}: {{type}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + String(describing: p{{forloop.counter}}) + {% elif type == "UnsafeRawPointer" %} + Int(bitPattern: p{{forloop.counter}}) + {% else %} + p{{forloop.counter}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro recursiveBlock table item %} + {% for string in item.strings %} + {% if not param.noComments %} + {% for line in string.translation|split:"\n" %} + /// {{line}} + {% endfor %} + {% endif %} + {% if string.types %} + {{accessModifier}} static func {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { + return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %}) + } + {% elif param.lookupFunction %} + {# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #} + {{accessModifier}} static var {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") } + {% else %} + {{accessModifier}} static let {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}") + {% endif %} + {% endfor %} + {% for child in item.children %} + {% call recursiveBlock table child %} + {% endfor %} +{% endmacro %} +// swiftlint:disable function_parameter_count identifier_name line_length type_body_length +{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %} +{{accessModifier}} enum {{enumName}} { + {% if tables.count > 1 or param.forceFileNameEnum %} + {% for table in tables %} + {{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call recursiveBlock tables.first.name tables.first.levels %} + {% endif %} +} +// swiftlint:enable function_parameter_count identifier_name line_length type_body_length + +// MARK: - Implementation Details + +extension {{enumName}} { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + {% if param.lookupFunction %} + let format = {{ param.lookupFunction }}(key, table) + {% else %} + let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table) + {% endif %} + return String(format: format, locale: Locale.current, arguments: args) + } +} +{% 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 string found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift5.stencil new file mode 100644 index 00000000..5bb4a128 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/flat-swift5.stencil @@ -0,0 +1,99 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Strings + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + _ p{{forloop.counter}}: Any + {% else %} + _ p{{forloop.counter}}: {{type}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + String(describing: p{{forloop.counter}}) + {% elif type == "UnsafeRawPointer" %} + Int(bitPattern: p{{forloop.counter}}) + {% else %} + p{{forloop.counter}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro recursiveBlock table item %} + {% for string in item.strings %} + {% if not param.noComments %} + {% for line in string.translation|split:"\n" %} + /// {{line}} + {% endfor %} + {% endif %} + {% if string.types %} + {{accessModifier}} static func {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { + return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %}) + } + {% elif param.lookupFunction %} + {# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #} + {{accessModifier}} static var {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") } + {% else %} + {{accessModifier}} static let {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}") + {% endif %} + {% endfor %} + {% for child in item.children %} + {% call recursiveBlock table child %} + {% endfor %} +{% endmacro %} +// swiftlint:disable function_parameter_count identifier_name line_length type_body_length +{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %} +{{accessModifier}} enum {{enumName}} { + {% if tables.count > 1 or param.forceFileNameEnum %} + {% for table in tables %} + {{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call recursiveBlock tables.first.name tables.first.levels %} + {% endif %} +} +// swiftlint:enable function_parameter_count identifier_name line_length type_body_length + +// MARK: - Implementation Details + +extension {{enumName}} { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + {% if param.lookupFunction %} + let format = {{ param.lookupFunction }}(key, table) + {% else %} + let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table) + {% endif %} + return String(format: format, locale: Locale.current, arguments: args) + } +} +{% 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 string found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-h.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-h.stencil new file mode 100644 index 00000000..7c502917 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-h.stencil @@ -0,0 +1,68 @@ +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +#import + +NS_ASSUME_NONNULL_BEGIN + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + ({% call paramTranslate type %})p{{ forloop.counter }}{{ " :" if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + p{{forloop.counter}}{{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro paramTranslate swiftType %} + {% if swiftType == "Any" %} + id + {% elif swiftType == "CChar" %} + char + {% elif swiftType == "Float" %} + float + {% elif swiftType == "Int" %} + NSInteger + {% elif swiftType == "String" %} + id + {% elif swiftType == "UnsafePointer" %} + char* + {% elif swiftType == "UnsafeRawPointer" %} + void* + {% else %} + objc-h.stencil is missing '{{swiftType}}' + {% endif %} +{% endmacro %} +{% macro emitOneMethod table item %} +{% for string in item.strings %} +{% if not param.noComments %} +{% for line in string.translation|split:"\n" %} +/// {{line}} +{% endfor %} +{% endif %} +{% if string.types %} + {% if string.types.count == 1 %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValue:{% call parametersBlock string.types %}; + {% else %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValues:{% call parametersBlock string.types %}; + {% endif %} +{% else %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}; +{% endif %} +{% endfor %} +{% for child in item.children %} +{% call emitOneMethod table child %} +{% endfor %} +{% endmacro %} +{% for table in tables %} +@interface {{ table.name }} : NSObject + {% call emitOneMethod table.name table.levels %} +@end + +{% endfor %} + +NS_ASSUME_NONNULL_END +{% else %} +// No strings found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-m.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-m.stencil new file mode 100644 index 00000000..1f154b59 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/objc-m.stencil @@ -0,0 +1,90 @@ +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +#import "{{ param.headerName|default:"Localizable.h" }}" +{% if not param.bundle %} + +@interface BundleToken : NSObject +@end + +@implementation BundleToken +@end +{% endif %} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-security" + +static NSString* tr(NSString *tableName, NSString *key, ...) { + NSBundle *bundle = {{param.bundle|default:"[NSBundle bundleForClass:BundleToken.class]"}}; + NSString *format = [bundle localizedStringForKey:key value:nil table:tableName]; + NSLocale *locale = [NSLocale currentLocale]; + + va_list args; + va_start(args, key); + NSString *result = [[NSString alloc] initWithFormat:format locale:locale arguments:args]; + va_end(args); + + return result; +}; +#pragma clang diagnostic pop + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + ({% call paramTranslate type %})p{{ forloop.counter }}{{ " :" if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + p{{forloop.counter}}{{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro paramTranslate swiftType %} + {% if swiftType == "Any" %} + id + {% elif swiftType == "CChar" %} + char + {% elif swiftType == "Float" %} + float + {% elif swiftType == "Int" %} + NSInteger + {% elif swiftType == "String" %} + id + {% elif swiftType == "UnsafePointer" %} + char* + {% elif swiftType == "UnsafeRawPointer" %} + void* + {% else %} + objc-m.stencil is missing '{{swiftType}}' + {% endif %} +{% endmacro %} +{% macro tableContents table item %} + {% for string in item.strings %} + {% if string.types %} + {% if string.types.count == 1 %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValue:{% call parametersBlock string.types %} + {% else %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValues:{% call parametersBlock string.types %} + {% endif %} +{ + return tr(@"{{table}}", @"{{string.key}}", {% call argumentsBlock string.types %}); +} +{% else %} ++ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}} { + return tr(@"{{table}}", @"{{string.key}}"); +} + {% endif %} + {% endfor %} + {% for child in item.children %} + {% call tableContents table child %} + {% endfor %} +{% endmacro %} +{% for table in tables %} + {% set tableName %}{{table.name|default:"Localized"}}{% endset %} +@implementation {{ tableName }} : NSObject + {% call tableContents table.name table.levels %} +@end + +{% endfor %} +{% else %} +// No strings found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift4.stencil new file mode 100644 index 00000000..f809bc24 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift4.stencil @@ -0,0 +1,104 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Strings + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + _ p{{forloop.counter}}: Any + {% else %} + _ p{{forloop.counter}}: {{type}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + String(describing: p{{forloop.counter}}) + {% elif type == "UnsafeRawPointer" %} + Int(bitPattern: p{{forloop.counter}}) + {% else %} + p{{forloop.counter}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro recursiveBlock table item %} + {% for string in item.strings %} + {% if not param.noComments %} + {% for line in string.translation|split:"\n" %} + /// {{line}} + {% endfor %} + {% endif %} + {% if string.types %} + {{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { + return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %}) + } + {% elif param.lookupFunction %} + {# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #} + {{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") } + {% else %} + {{accessModifier}} static let {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}") + {% endif %} + {% endfor %} + {% for child in item.children %} + + {{accessModifier}} enum {{child.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %} + } + {% endfor %} +{% endmacro %} +// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces +{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %} +{{accessModifier}} enum {{enumName}} { + {% if tables.count > 1 or param.forceFileNameEnum %} + {% for table in tables %} + {{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call recursiveBlock tables.first.name tables.first.levels %} + {% endif %} +} +// 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 {{enumName}} { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + {% if param.lookupFunction %} + let format = {{ param.lookupFunction }}(key, table) + {% else %} + let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table) + {% endif %} + return String(format: format, locale: Locale.current, arguments: args) + } +} +{% 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 string found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift5.stencil new file mode 100644 index 00000000..f809bc24 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/strings/structured-swift5.stencil @@ -0,0 +1,104 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if tables.count > 0 %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Strings + +{% macro parametersBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + _ p{{forloop.counter}}: Any + {% else %} + _ p{{forloop.counter}}: {{type}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %} + {% for type in types %} + {% if type == "String" %} + String(describing: p{{forloop.counter}}) + {% elif type == "UnsafeRawPointer" %} + Int(bitPattern: p{{forloop.counter}}) + {% else %} + p{{forloop.counter}} + {% endif %} + {{ ", " if not forloop.last }} + {% endfor %} +{% endfilter %}{% endmacro %} +{% macro recursiveBlock table item %} + {% for string in item.strings %} + {% if not param.noComments %} + {% for line in string.translation|split:"\n" %} + /// {{line}} + {% endfor %} + {% endif %} + {% if string.types %} + {{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String { + return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %}) + } + {% elif param.lookupFunction %} + {# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #} + {{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") } + {% else %} + {{accessModifier}} static let {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}") + {% endif %} + {% endfor %} + {% for child in item.children %} + + {{accessModifier}} enum {{child.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %} + } + {% endfor %} +{% endmacro %} +// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces +{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %} +{{accessModifier}} enum {{enumName}} { + {% if tables.count > 1 or param.forceFileNameEnum %} + {% for table in tables %} + {{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call recursiveBlock tables.first.name tables.first.levels %} + {% endif %} +} +// 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 {{enumName}} { + private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String { + {% if param.lookupFunction %} + let format = {{ param.lookupFunction }}(key, table) + {% else %} + let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table) + {% endif %} + return String(format: format, locale: Locale.current, arguments: args) + } +} +{% 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 string found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift4.stencil new file mode 100644 index 00000000..c8565931 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift4.stencil @@ -0,0 +1,329 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if catalogs %} +{% set enumName %}{{param.enumName|default:"Asset"}}{% endset %} +{% set arResourceGroupType %}{{param.arResourceGroupTypeName|default:"ARResourceGroupAsset"}}{% endset %} +{% set colorType %}{{param.colorTypeName|default:"ColorAsset"}}{% endset %} +{% set dataType %}{{param.dataTypeName|default:"DataAsset"}}{% endset %} +{% set imageType %}{{param.imageTypeName|default:"ImageAsset"}}{% endset %} +{% set symbolType %}{{param.symbolTypeName|default:"SymbolAsset"}}{% endset %} +{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +#if os(macOS) + import AppKit +#elseif os(iOS) +{% if resourceCount.arresourcegroup > 0 %} + import ARKit +{% endif %} + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif + +// Deprecated typealiases +{% if resourceCount.color > 0 %} +@available(*, deprecated, renamed: "{{colorType}}.Color", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.colorAliasName|default:"AssetColorTypeAlias"}} = {{colorType}}.Color +{% endif %} +{% if resourceCount.image > 0 %} +@available(*, deprecated, renamed: "{{imageType}}.Image", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.imageAliasName|default:"AssetImageTypeAlias"}} = {{imageType}}.Image +{% endif %} + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +{% macro enumBlock assets %} + {% call casesBlock assets %} + {% if param.allValues %} + + // swiftlint:disable trailing_comma + {% if resourceCount.arresourcegroup > 0 %} + {{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.color > 0 %} + {{accessModifier}} static let allColors: [{{colorType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "color" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.data > 0 %} + {{accessModifier}} static let allDataAssets: [{{dataType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "data" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.image > 0 %} + {{accessModifier}} static let allImages: [{{imageType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "image" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.symbol > 0 %} + {{accessModifier}} static let allSymbols: [{{symbolType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "symbol" "" %}{% endfilter %} + ] + {% endif %} + // swiftlint:enable trailing_comma + {% endif %} +{% endmacro %} +{% macro casesBlock assets %} + {% for asset in assets %} + {% if asset.type == "arresourcegroup" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{arResourceGroupType}}(name: "{{asset.value}}") + {% elif asset.type == "color" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{colorType}}(name: "{{asset.value}}") + {% elif asset.type == "data" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{dataType}}(name: "{{asset.value}}") + {% elif asset.type == "image" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{imageType}}(name: "{{asset.value}}") + {% elif asset.type == "symbol" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{symbolType}}(name: "{{asset.value}}") + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {{accessModifier}} enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %} + } + {% elif asset.items %} + {% call casesBlock asset.items %} + {% endif %} + {% endfor %} +{% endmacro %} +{% macro allValuesBlock assets filter prefix %} + {% for asset in assets %} + {% if asset.type == filter %} + {{prefix}}{{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}, + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {% set prefix2 %}{{prefix}}{{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.{% endset %} + {% call allValuesBlock asset.items filter prefix2 %} + {% elif asset.items %} + {% call allValuesBlock asset.items filter prefix %} + {% endif %} + {% endfor %} +{% endmacro %} +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +{{accessModifier}} enum {{enumName}} { + {% if catalogs.count > 1 or param.forceFileNameEnum %} + {% for catalog in catalogs %} + {{accessModifier}} enum {{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call enumBlock catalog.assets %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call enumBlock catalogs.first.assets %} + {% endif %} +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details +{% if resourceCount.arresourcegroup > 0 %} + +{{accessModifier}} struct {{arResourceGroupType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) + @available(iOS 11.3, *) + {{accessModifier}} var referenceImages: Set { + return ARReferenceImage.referenceImages(in: self) + } + + @available(iOS 12.0, *) + {{accessModifier}} var referenceObjects: Set { + return ARReferenceObject.referenceObjects(in: self) + } + #endif +} + +#if os(iOS) +@available(iOS 11.3, *) +{{accessModifier}} extension ARReferenceImage { + static func referenceImages(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceImages(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} + +@available(iOS 12.0, *) +{{accessModifier}} extension ARReferenceObject { + static func referenceObjects(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceObjects(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} +#endif +{% endif %} +{% if resourceCount.color > 0 %} + +{{accessModifier}} final class {{colorType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + {{accessModifier}} private(set) lazy var color: Color = Color(asset: self) + + #if os(iOS) || os(tvOS) + @available(iOS 11.0, tvOS 11.0, *) + {{accessModifier}} func color(compatibleWith traitCollection: UITraitCollection) -> Color { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load color asset named \(name).") + } + return color + } + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +{{accessModifier}} extension {{colorType}}.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + convenience init!(asset: {{colorType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.data > 0 %} + +{{accessModifier}} struct {{dataType}} { + {{accessModifier}} fileprivate(set) var name: String + + @available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) + {{accessModifier}} var data: NSDataAsset { + return NSDataAsset(asset: self) + } +} + +@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) +{{accessModifier}} extension NSDataAsset { + convenience init!(asset: {{dataType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) || os(watchOS) + self.init(name: asset.name, bundle: bundle) + #elseif os(macOS) + self.init(name: NSDataAsset.Name(asset.name), bundle: bundle) + #endif + } +} +{% endif %} +{% if resourceCount.image > 0 %} + +{{accessModifier}} struct {{imageType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Image = UIImage + #endif + + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let name = NSImage.Name(self.name) + let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if os(iOS) || os(tvOS) + @available(iOS 8.0, tvOS 9.0, *) + {{accessModifier}} func image(compatibleWith traitCollection: UITraitCollection) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif +} + +{{accessModifier}} extension {{imageType}}.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) + @available(macOS, deprecated, + message: "This initializer is unsafe on macOS, please use the {{imageType}}.image property") + convenience init!(asset: {{imageType}}) { + #if os(iOS) || os(tvOS) + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.symbol > 0 %} + +{{accessModifier}} struct {{symbolType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) || os(tvOS) || os(watchOS) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} typealias Configuration = UIImage.SymbolConfiguration + {{accessModifier}} typealias Image = UIImage + + @available(iOS 12.0, tvOS 12.0, watchOS 5.0, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} func image(with configuration: Configuration) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, with: configuration) else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + #endif +} +{% endif %} +{% if not param.bundle %} + +// 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 assets found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift5.stencil new file mode 100644 index 00000000..42df7be8 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/xcassets/swift5.stencil @@ -0,0 +1,337 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if catalogs %} +{% set enumName %}{{param.enumName|default:"Asset"}}{% endset %} +{% set arResourceGroupType %}{{param.arResourceGroupTypeName|default:"ARResourceGroupAsset"}}{% endset %} +{% set colorType %}{{param.colorTypeName|default:"ColorAsset"}}{% endset %} +{% set dataType %}{{param.dataTypeName|default:"DataAsset"}}{% endset %} +{% set imageType %}{{param.imageTypeName|default:"ImageAsset"}}{% endset %} +{% set symbolType %}{{param.symbolTypeName|default:"SymbolAsset"}}{% endset %} +{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +#if os(macOS) + import AppKit +#elseif os(iOS) +{% if resourceCount.arresourcegroup > 0 %} + import ARKit +{% endif %} + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif + +// Deprecated typealiases +{% if resourceCount.color > 0 %} +@available(*, deprecated, renamed: "{{colorType}}.Color", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.colorAliasName|default:"AssetColorTypeAlias"}} = {{colorType}}.Color +{% endif %} +{% if resourceCount.image > 0 %} +@available(*, deprecated, renamed: "{{imageType}}.Image", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.imageAliasName|default:"AssetImageTypeAlias"}} = {{imageType}}.Image +{% endif %} + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +{% macro enumBlock assets %} + {% call casesBlock assets %} + {% if param.allValues %} + + // swiftlint:disable trailing_comma + {% if resourceCount.arresourcegroup > 0 %} + {{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.color > 0 %} + {{accessModifier}} static let allColors: [{{colorType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "color" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.data > 0 %} + {{accessModifier}} static let allDataAssets: [{{dataType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "data" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.image > 0 %} + {{accessModifier}} static let allImages: [{{imageType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "image" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.symbol > 0 %} + {{accessModifier}} static let allSymbols: [{{symbolType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "symbol" "" %}{% endfilter %} + ] + {% endif %} + // swiftlint:enable trailing_comma + {% endif %} +{% endmacro %} +{% macro casesBlock assets %} + {% for asset in assets %} + {% if asset.type == "arresourcegroup" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{arResourceGroupType}}(name: "{{asset.value}}") + {% elif asset.type == "color" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{colorType}}(name: "{{asset.value}}") + {% elif asset.type == "data" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{dataType}}(name: "{{asset.value}}") + {% elif asset.type == "image" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{imageType}}(name: "{{asset.value}}") + {% elif asset.type == "symbol" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{symbolType}}(name: "{{asset.value}}") + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {{accessModifier}} enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %} + } + {% elif asset.items %} + {% call casesBlock asset.items %} + {% endif %} + {% endfor %} +{% endmacro %} +{% macro allValuesBlock assets filter prefix %} + {% for asset in assets %} + {% if asset.type == filter %} + {{prefix}}{{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}, + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {% set prefix2 %}{{prefix}}{{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.{% endset %} + {% call allValuesBlock asset.items filter prefix2 %} + {% elif asset.items %} + {% call allValuesBlock asset.items filter prefix %} + {% endif %} + {% endfor %} +{% endmacro %} +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +{{accessModifier}} enum {{enumName}} { + {% if catalogs.count > 1 or param.forceFileNameEnum %} + {% for catalog in catalogs %} + {{accessModifier}} enum {{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call enumBlock catalog.assets %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call enumBlock catalogs.first.assets %} + {% endif %} +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details +{% if resourceCount.arresourcegroup > 0 %} + +{{accessModifier}} struct {{arResourceGroupType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) + @available(iOS 11.3, *) + {{accessModifier}} var referenceImages: Set { + return ARReferenceImage.referenceImages(in: self) + } + + @available(iOS 12.0, *) + {{accessModifier}} var referenceObjects: Set { + return ARReferenceObject.referenceObjects(in: self) + } + #endif +} + +#if os(iOS) +@available(iOS 11.3, *) +{{accessModifier}} extension ARReferenceImage { + static func referenceImages(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceImages(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} + +@available(iOS 12.0, *) +{{accessModifier}} extension ARReferenceObject { + static func referenceObjects(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceObjects(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} +#endif +{% endif %} +{% if resourceCount.color > 0 %} + +{{accessModifier}} final class {{colorType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + {{accessModifier}} private(set) lazy var color: Color = { + guard let color = Color(asset: self) else { + fatalError("Unable to load color asset named \(name).") + } + return color + }() + + #if os(iOS) || os(tvOS) + @available(iOS 11.0, tvOS 11.0, *) + {{accessModifier}} func color(compatibleWith traitCollection: UITraitCollection) -> Color { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load color asset named \(name).") + } + return color + } + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +{{accessModifier}} extension {{colorType}}.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + convenience init?(asset: {{colorType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.data > 0 %} + +{{accessModifier}} struct {{dataType}} { + {{accessModifier}} fileprivate(set) var name: String + + @available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) + {{accessModifier}} var data: NSDataAsset { + guard let data = NSDataAsset(asset: self) else { + fatalError("Unable to load data asset named \(name).") + } + return data + } +} + +@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) +{{accessModifier}} extension NSDataAsset { + convenience init?(asset: {{dataType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) || os(watchOS) + self.init(name: asset.name, bundle: bundle) + #elseif os(macOS) + self.init(name: NSDataAsset.Name(asset.name), bundle: bundle) + #endif + } +} +{% endif %} +{% if resourceCount.image > 0 %} + +{{accessModifier}} struct {{imageType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Image = UIImage + #endif + + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let name = NSImage.Name(self.name) + let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if os(iOS) || os(tvOS) + @available(iOS 8.0, tvOS 9.0, *) + {{accessModifier}} func image(compatibleWith traitCollection: UITraitCollection) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif +} + +{{accessModifier}} extension {{imageType}}.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) + @available(macOS, deprecated, + message: "This initializer is unsafe on macOS, please use the {{imageType}}.image property") + convenience init?(asset: {{imageType}}) { + #if os(iOS) || os(tvOS) + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.symbol > 0 %} + +{{accessModifier}} struct {{symbolType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) || os(tvOS) || os(watchOS) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} typealias Configuration = UIImage.SymbolConfiguration + {{accessModifier}} typealias Image = UIImage + + @available(iOS 12.0, tvOS 12.0, watchOS 5.0, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} func image(with configuration: Configuration) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, with: configuration) else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + #endif +} +{% endif %} +{% if not param.bundle %} + +// 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 assets found +{% endif %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift4.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift4.stencil new file mode 100644 index 00000000..9cc2aa36 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift4.stencil @@ -0,0 +1,92 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if files %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +{% set documentPrefix %}{{param.documentName|default:"Document"}}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - YAML Files +{% macro fileBlock file %} + {% if file.documents.count > 1 %} + {% for document in file.documents %} + {% set documentName %}{{documentPrefix}}{{forloop.counter}}{% endset %} + {{accessModifier}} enum {{documentName|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call documentBlock file document %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call documentBlock file file.documents.first %} + {% endif %} +{% 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:"YAMLFiles"}} { + {% 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 %} diff --git a/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift5.stencil b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift5.stencil new file mode 100644 index 00000000..9cc2aa36 --- /dev/null +++ b/bin/SwiftGen_SwiftGenCLI.bundle/Contents/Resources/templates/yaml/inline-swift5.stencil @@ -0,0 +1,92 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if files %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +{% set documentPrefix %}{{param.documentName|default:"Document"}}{% endset %} +import Foundation + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable file_length + +// MARK: - YAML Files +{% macro fileBlock file %} + {% if file.documents.count > 1 %} + {% for document in file.documents %} + {% set documentName %}{{documentPrefix}}{{forloop.counter}}{% endset %} + {{accessModifier}} enum {{documentName|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call documentBlock file document %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call documentBlock file file.documents.first %} + {% endif %} +{% 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:"YAMLFiles"}} { + {% 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 %} diff --git a/bin/swiftgen b/bin/swiftgen new file mode 100755 index 00000000..1bc2b874 Binary files /dev/null and b/bin/swiftgen differ diff --git a/swiftgen.yml b/swiftgen.yml new file mode 100644 index 00000000..f6e76e1e --- /dev/null +++ b/swiftgen.yml @@ -0,0 +1,5 @@ +strings: + inputs: Translations/en.lproj + outputs: + - templateName: structured-swift5 + output: Shared/Generated/Strings.swift