diff --git a/ios/nskeyedarchiver/archiver_test.go b/ios/nskeyedarchiver/archiver_test.go index effbc8ac..1dd33c64 100644 --- a/ios/nskeyedarchiver/archiver_test.go +++ b/ios/nskeyedarchiver/archiver_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "reflect" "testing" @@ -16,6 +17,18 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSavedState(t *testing.T) { + bytes, _ := os.ReadFile("fixtures/savedstate.plist") + res, err := nskeyedarchiver.Unarchive(bytes) + if assert.NoError(t, err) { + nskm := res[0].(map[string]interface{}) + assert.Equal(t, uint64(25919), nskm["CoreGraphics Window ID"]) + assert.Equal(t, true, nskm["MenuBar Main"]) + assert.Equal(t, []interface{}{"MenuBar Main", "MenuBar AvailableSpace", "CoreGraphics Window ID"}, nskm["_NSDictionaryKeys"]) + assert.Equal(t, float64(1053), nskm["MenuBar AvailableSpace"]) + } +} + func TestArchiveSlice(t *testing.T) { var option = make(map[string]interface{}) option["name"] = "james" diff --git a/ios/nskeyedarchiver/archiverutils.go b/ios/nskeyedarchiver/archiverutils.go index c53c661b..16f4a69a 100644 --- a/ios/nskeyedarchiver/archiverutils.go +++ b/ios/nskeyedarchiver/archiverutils.go @@ -4,10 +4,30 @@ import ( "bytes" "encoding/json" "fmt" - plist "howett.net/plist" + "regexp" ) +func areNumeric(keys []string) bool { + for _, key := range keys { + match, _ := regexp.MatchString("\\$[0-9]+", key) + if !match { + return false + } + } + return true +} + +func getKeysOfMap(themap map[string]interface{}) []string { + result := make([]string, len(themap)) + i := 0 + for key, _ := range themap { + result[i] = key + i++ + } + return result +} + func toInterfaceSlice(stringSlice []string) []interface{} { result := make([]interface{}, len(stringSlice)) for i, e := range stringSlice { diff --git a/ios/nskeyedarchiver/fixtures/savedstate.plist b/ios/nskeyedarchiver/fixtures/savedstate.plist new file mode 100755 index 00000000..69f7ad46 Binary files /dev/null and b/ios/nskeyedarchiver/fixtures/savedstate.plist differ diff --git a/ios/nskeyedarchiver/unarchiver.go b/ios/nskeyedarchiver/unarchiver.go index 0a0f5aa7..ed120812 100644 --- a/ios/nskeyedarchiver/unarchiver.go +++ b/ios/nskeyedarchiver/unarchiver.go @@ -32,13 +32,32 @@ func extractObjectsFromTop(top map[string]interface{}, objects []interface{}) ([ if root, ok := top["root"]; ok { return extractObjects([]plist.UID{root.(plist.UID)}, objects) } + objectRefs := make([]plist.UID, objectCount) - //convert the Dictionary with the objectReferences into a flat list of UIDs, so we can reuse the extractObjects function later - for i := 0; i < objectCount; i++ { - objectIndex := top[fmt.Sprintf("$%d", i)].(plist.UID) + keys := getKeysOfMap(top) + //if all keys are like $0, $1 etc. the code returns an array + if areNumeric(keys) { + for i := 0; i < objectCount; i++ { + objectIndex := top[fmt.Sprintf("$%d", i)].(plist.UID) + objectRefs[i] = objectIndex + } + return extractObjects(objectRefs, objects) + } + // otherwise return a map as the only object in []interface{} + for i, key := range keys { + objectIndex := top[key].(plist.UID) objectRefs[i] = objectIndex } - return extractObjects(objectRefs, objects) + extractedObjects, err := extractObjects(objectRefs, objects) + if err != nil { + return extractedObjects, err + } + + result := map[string]interface{}{} + for i, key := range keys { + result[key] = extractedObjects[i] + } + return []interface{}{result}, nil } func extractObjects(objectRefs []plist.UID, objects []interface{}) ([]interface{}, error) { @@ -52,8 +71,8 @@ func extractObjects(objectRefs []plist.UID, objects []interface{}) ([]interface{ continue } //if this crashes, I forgot a primitive type - nonPrimitiveObjectRef,ok := objectRef.(map[string]interface{}) - if !ok{ + nonPrimitiveObjectRef, ok := objectRef.(map[string]interface{}) + if !ok { return []interface{}{}, fmt.Errorf("object not a dictionary: %+v", objectRef) } if object, ok := isArrayObject(nonPrimitiveObjectRef, objects); ok {