Skip to content

Commit

Permalink
Merge pull request #919 from jhildensperger/plist-bugs
Browse files Browse the repository at this point in the history
Fix support for plist items that are in arrays
  • Loading branch information
tomlokhorst authored Nov 9, 2024
2 parents 1d223e9 + 8ca8980 commit d3d19c8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 14 deletions.
30 changes: 30 additions & 0 deletions Examples/ResourceApp/ResourceAppTests/InfoPlistTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,34 @@ class InfoPlistTests: XCTestCase {
func testVariable() {
XCTAssertEqual(R.info.nsExtension.nsExtensionPrincipalClass, "ResourceApp.IntentHandler")
}

func testUIApplicationShortcutItems() {
XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsQrScanning.uiApplicationShortcutItemIconFile, "ShortcutQrScanning")
XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsQrScanning.uiApplicationShortcutItemTitle, "Scan QR-code")
XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsQrScanning.uiApplicationShortcutItemType, "nl.mathijskadijk.shortcuts.qr-scanning")

XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsSendParcel.uiApplicationShortcutItemIconFile, "ShortcutSendParcel")
XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsSendParcel.uiApplicationShortcutItemTitle, "Send a Parcel")
XCTAssertEqual(R.info.uiApplicationShortcutItems.nlMathijskadijkShortcutsSendParcel.uiApplicationShortcutItemType, "nl.mathijskadijk.shortcuts.send-parcel")
}

func testUIApplicationSceneManifest() {
XCTAssertFalse(R.info.uiApplicationSceneManifest.uiApplicationSupportsMultipleScenes)

XCTAssertEqual(R.info.uiApplicationSceneManifest.uiSceneConfigurations.uiWindowSceneSessionRoleApplication.defaultConfiguration.uiSceneConfigurationName, "Default Configuration")
XCTAssertEqual(R.info.uiApplicationSceneManifest.uiSceneConfigurations.uiWindowSceneSessionRoleApplication.defaultConfiguration.uiSceneDelegateClassName, "ResourceApp.SceneDelegate")
}

func testNSUserActivityTypes() {
XCTAssertEqual(R.info.nsUserActivityTypes.planTripIntent, "PlanTripIntent")
XCTAssertEqual(R.info.nsUserActivityTypes.showDeparturesIntent, "ShowDeparturesIntent")
}

func testNSExtension() {
XCTAssertEqual(R.info.nsExtension.nsExtensionAttributes.intentsSupported.planTripIntent, "PlanTripIntent")
XCTAssertEqual(R.info.nsExtension.nsExtensionAttributes.intentsSupported.showDeparturesIntent, "ShowDeparturesIntent")

XCTAssertEqual(R.info.nsExtension.nsExtensionPrincipalClass, "ResourceApp.IntentHandler")
XCTAssertEqual(R.info.nsExtension.nsExtensionPointIdentifier, "com.apple.intents-service")
}
}
34 changes: 26 additions & 8 deletions Sources/RswiftGenerators/PropertyListResource+Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,16 @@ extension PropertyListResource {
}
}

protocol PlistPathComponent {
typealias Key = String
typealias Index = Int
}

extension PlistPathComponent.Key: PlistPathComponent {}
extension PlistPathComponent.Index: PlistPathComponent {}

extension PropertyListResource.Contents {
@StructMembersBuilder func generateMembers(resourceName: String, path: [String], isInfoPlist: Bool, warning: (String) -> Void) -> StructMembers {
@StructMembersBuilder func generateMembers(resourceName: String, path: [PlistPathComponent], includeKey: Bool = true, isInfoPlist: Bool, warning: (String) -> Void) -> StructMembers {
let groupedContents = self.grouped(bySwiftIdentifier: { $0.key })
groupedContents.reportWarningsForDuplicatesAndEmpties(source: resourceName, result: resourceName, warning: warning)

Expand All @@ -73,7 +81,7 @@ extension PropertyListResource.Contents {
VarGetter(
name: SwiftIdentifier(name: key),
typeReference: .string,
valueCodeString: "bundle.infoDictionaryString(path: \(path), key: \"\(key.escapedStringLiteral)\") ?? \"\(value.escapedStringLiteral)\""
valueCodeString: valueCodedString(path: path, includeKey: includeKey, key: key, value: value)
)
} else {
LetBinding(
Expand All @@ -86,12 +94,13 @@ extension PropertyListResource.Contents {
case let duplicateArray as [String]:
let groupedArray = duplicateArray.grouped(bySwiftIdentifier: { $0 })
groupedArray.reportWarningsForDuplicatesAndEmpties(source: resourceName, result: resourceName, warning: warning)
let dicts = Dictionary(groupedArray.uniques.map { ($0, $0) }, uniquingKeysWith: { l, r in l })

bundleStruct(name: key, usesBundle: isInfoPlist) {
dicts
.generateMembers(resourceName: resourceName, path: newPath, isInfoPlist: isInfoPlist, warning: warning)
.sorted()
for (index, value) in groupedArray.uniques.enumerated() {
[value: value]
.generateMembers(resourceName: resourceName, path: newPath + [index], includeKey: false, isInfoPlist: isInfoPlist, warning: warning)
.sorted()
}
}


Expand All @@ -105,11 +114,11 @@ extension PropertyListResource.Contents {

case let dicts as [[String: Any]] where arrayOfDictionariesPrimaryKeys.keys.contains(key):
bundleStruct(name: key, usesBundle: isInfoPlist) {
for dict in dicts {
for (index, dict) in dicts.enumerated() {
if let primaryKey = arrayOfDictionariesPrimaryKeys[key],
let primary = dict[primaryKey] as? String {
bundleStruct(name: primary, usesBundle: isInfoPlist) {
dict.generateMembers(resourceName: resourceName, path: newPath, isInfoPlist: isInfoPlist, warning: warning)
dict.generateMembers(resourceName: resourceName, path: newPath + [index], isInfoPlist: isInfoPlist, warning: warning)
.sorted()
}
}
Expand All @@ -121,7 +130,16 @@ extension PropertyListResource.Contents {
}
}
}

private func valueCodedString(path: [PlistPathComponent], includeKey: Bool, key: String, value: String) -> String {
var string = "bundle.infoDictionaryString(path: \(path)"

if includeKey {
string += ", key: \"\(key.escapedStringLiteral)\""
}

return string + ") ?? \"\(value.escapedStringLiteral)\""
}
}

@StructMembersBuilder func bundleStruct(name: String, usesBundle: Bool, @StructMembersBuilder builder: () -> StructMembers) -> StructMembers {
Expand Down
37 changes: 31 additions & 6 deletions Sources/RswiftResources/Integrations/Bundle+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,41 @@

import Foundation

extension Bundle {
public protocol PlistPathComponent {
typealias Key = String
typealias Index = Int
}

extension PlistPathComponent.Key: PlistPathComponent {}
extension PlistPathComponent.Index: PlistPathComponent {}

extension Bundle {

/// Returns the string associated with the specified path + key in the receiver's information property list.
public func infoDictionaryString(path: [String], key: String) -> String? {
var dict = infoDictionary
public func infoDictionaryString(path: [PlistPathComponent], key: PlistPathComponent.Key? = nil) -> String? {
var currentObject: Any? = infoDictionary

for step in path {
guard let obj = dict?[step] as? [String: Any] else { return nil }
dict = obj
if let currentDict = currentObject as? [String: Any], let key = step as? String {
// If the current object is a dictionary, move to the next step using the dictionary key
currentObject = currentDict[key]
} else if let currentArray = currentObject as? [Any], let index = step as? Int, currentArray.indices.contains(index) {
// If the current object is an array, and the step is a valid index, move to the array element
currentObject = currentArray[index]
} else {
// If the path leads to an invalid object type or out of bounds index, return nil
return nil
}
}

// Attempt to extract a string from the final object using the provided key, else if key == nil, assume
// we have arrived at a string value.
if let dict = currentObject as? [String: Any], let key = key {
return dict[key] as? String
} else if key == nil, let value = currentObject as? String {
return value
}
return dict?[key] as? String
return nil
}

/// Find first bundle and locale for which the table exists
Expand Down

0 comments on commit d3d19c8

Please sign in to comment.