diff --git a/.codecov.yml b/.codecov.yml index e7d5ee618..50064ab5b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,6 +6,6 @@ coverage: changes: false project: default: - target: 45 + target: 52 comment: require_changes: true diff --git a/ParseSwift.playground/Pages/1- Your first Object.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/1- Your first Object.xcplaygroundpage/Contents.swift index 11f608a5f..e3a405538 100644 --- a/ParseSwift.playground/Pages/1- Your first Object.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/1- Your first Object.xcplaygroundpage/Contents.swift @@ -10,7 +10,7 @@ PlaygroundPage.current.needsIndefiniteExecution = true initializeParse() -struct GameScore: ParseSwift.ObjectType { +struct GameScore: ParseObject { //: Those are required for Object var objectId: String? var createdAt: Date? diff --git a/ParseSwift.playground/Pages/2- Finding Objects.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/2- Finding Objects.xcplaygroundpage/Contents.swift index 6f609fddd..2e6ee4aac 100644 --- a/ParseSwift.playground/Pages/2- Finding Objects.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/2- Finding Objects.xcplaygroundpage/Contents.swift @@ -10,7 +10,7 @@ PlaygroundPage.current.needsIndefiniteExecution = true initializeParse() -struct GameScore: ParseSwift.ObjectType { +struct GameScore: ParseObject { var objectId: String? var createdAt: Date? var updatedAt: Date? diff --git a/ParseSwift.playground/Pages/3- Users.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/3- Users.xcplaygroundpage/Contents.swift index 3ba81a5fd..566811e7d 100644 --- a/ParseSwift.playground/Pages/3- Users.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/3- Users.xcplaygroundpage/Contents.swift @@ -7,7 +7,7 @@ PlaygroundPage.current.needsIndefiniteExecution = true import ParseSwift initializeParse() -struct User: ParseSwift.UserType { +struct User: ParseUser { //: Those are required for Object var objectId: String? var createdAt: Date? diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index d9002c3a9..baba90c63 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -7,78 +7,19 @@ objects = { /* Begin PBXBuildFile section */ - 4A2D8C811F48B3A900EE1FCC /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */; }; - 4A2F14941F4A5E1E00A7A7EF /* ObjectType+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */; }; - 4A2F14961F4A5F2900A7A7EF /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14951F4A5F2900A7A7EF /* Responses.swift */; }; - 4A2F14981F4A5F6900A7A7EF /* ObjectType+Batch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */; }; - 4A2F14991F4A5FB500A7A7EF /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14951F4A5F2900A7A7EF /* Responses.swift */; }; - 4A2F149A1F4A5FBA00A7A7EF /* ObjectType+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */; }; - 4A2F149B1F4A5FBA00A7A7EF /* ObjectType+Batch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */; }; - 4A65114F1F48E3F3005237DF /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */; }; - 4A6511501F48E400005237DF /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */; }; - 4A6511511F48E406005237DF /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC3976F1F48778900DEA9D3 /* ACL.swift */; }; - 4A6511521F48E406005237DF /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7ED1F254B820063D731 /* GeoPoint.swift */; }; - 4A6511531F48E410005237DF /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397731F488FF900DEA9D3 /* API.swift */; }; - 4A82B7F41F254CCE0063D731 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F01F254B820063D731 /* File.swift */; }; - 4A82B7F51F254CCE0063D731 /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7ED1F254B820063D731 /* GeoPoint.swift */; }; 4A82B7F61F254CCE0063D731 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; - 4A82B7F71F254CCE0063D731 /* ObjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EC1F254B820063D731 /* ObjectType.swift */; }; - 4A82B7F81F254CCE0063D731 /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F11F254B820063D731 /* Pointer.swift */; }; - 4A82B7FF1F256A8F0063D731 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7FE1F256A8F0063D731 /* Query.swift */; }; - 4A82B8011F256B330063D731 /* ObjectType+Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B8001F256B330063D731 /* ObjectType+Query.swift */; }; - 4A99A4691F2650CA00D72A59 /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */; }; - 4A99A46C1F2650FF00D72A59 /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */; }; - 4A99A46E1F26512100D72A59 /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */; }; - 4AA8074B1F7930E9008CD551 /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */; }; - 4AA8074C1F7930E9008CD551 /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */; }; - 4AA8074E1F7931B7008CD551 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */; }; - 4AA8074F1F7931B7008CD551 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */; }; 4AA8075B1F794242008CD551 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8075A1F794242008CD551 /* AppDelegate.swift */; }; 4AA807601F794242008CD551 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA8075E1F794242008CD551 /* Main.storyboard */; }; 4AA807621F794242008CD551 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AA807611F794242008CD551 /* Assets.xcassets */; }; 4AA807651F794242008CD551 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4AA807631F794242008CD551 /* LaunchScreen.storyboard */; }; 4AA807701F794C31008CD551 /* KeychainStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8076E1F794C1C008CD551 /* KeychainStoreTests.swift */; }; - 4AA807711F794C33008CD551 /* ParseSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8076F1F794C1C008CD551 /* ParseSwiftTests.swift */; }; 4AB8B4FE1F254AE10070F682 /* ParseSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4AB8B4F41F254AE10070F682 /* ParseSwift.framework */; }; 4AB8B5051F254AE10070F682 /* Parse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AB8B4F71F254AE10070F682 /* Parse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4AC397701F48778900DEA9D3 /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC3976F1F48778900DEA9D3 /* ACL.swift */; }; - 4AC397721F488F9E00DEA9D3 /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */; }; - 4AC397741F488FF900DEA9D3 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397731F488FF900DEA9D3 /* API.swift */; }; - 4AEBA5491F26519B00628B17 /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA5481F26519B00628B17 /* AddOperation.swift */; }; - 4AEBA54B1F2651D900628B17 /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */; }; - 4AEBA54D1F26523800628B17 /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */; }; - 4AEBA54F1F265A0D00628B17 /* UserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54E1F265A0D00628B17 /* UserType.swift */; }; - 4AF85BD31F78011100665264 /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BD21F78011100665264 /* ParseError.swift */; }; - 4AF85BD61F7803C100665264 /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BD21F78011100665264 /* ParseError.swift */; }; - 4AF85BDD1F783B9800665264 /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BDC1F783B9800665264 /* API+Commands.swift */; }; - 4AF85BDE1F783BB400665264 /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BDC1F783B9800665264 /* API+Commands.swift */; }; 4AFDA72A1F26DAE1002AE4FC /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; - 4AFDA72B1F26DAE1002AE4FC /* ObjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EC1F254B820063D731 /* ObjectType.swift */; }; - 4AFDA72C1F26DAE1002AE4FC /* UserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54E1F265A0D00628B17 /* UserType.swift */; }; - 4AFDA72D1F26DAE1002AE4FC /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */; }; - 4AFDA72E1F26DAE1002AE4FC /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA5481F26519B00628B17 /* AddOperation.swift */; }; - 4AFDA72F1F26DAE1002AE4FC /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */; }; - 4AFDA7301F26DAE1002AE4FC /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */; }; - 4AFDA7311F26DAE1002AE4FC /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */; }; - 4AFDA7321F26DAE1002AE4FC /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */; }; - 4AFDA7331F26DAE1002AE4FC /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F01F254B820063D731 /* File.swift */; }; - 4AFDA7341F26DAE1002AE4FC /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F11F254B820063D731 /* Pointer.swift */; }; - 4AFDA7351F26DAE1002AE4FC /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7FE1F256A8F0063D731 /* Query.swift */; }; - 4AFDA7361F26DAE1002AE4FC /* ObjectType+Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B8001F256B330063D731 /* ObjectType+Query.swift */; }; 4AFDA7391F26DAF8002AE4FC /* Parse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AB8B4F71F254AE10070F682 /* Parse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 709075C724B9117500B95310 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C424B9117400B95310 /* AnyCodable.swift */; }; - 709075C824B9117500B95310 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C424B9117400B95310 /* AnyCodable.swift */; }; - 709075C924B9117500B95310 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C524B9117500B95310 /* AnyDecodable.swift */; }; - 709075CA24B9117500B95310 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C524B9117500B95310 /* AnyDecodable.swift */; }; - 709075CB24B9117500B95310 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C624B9117500B95310 /* AnyEncodable.swift */; }; - 709075CC24B9117500B95310 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C624B9117500B95310 /* AnyEncodable.swift */; }; 70C7DC1E24D20E530050419B /* ParseUserCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70C7DC1D24D20E530050419B /* ParseUserCommandTests.swift */; }; 70C7DC2124D20F190050419B /* ParseQueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70C7DC1F24D20F180050419B /* ParseQueryTests.swift */; }; 70C7DC2224D20F190050419B /* ParseObjectBatchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70C7DC2024D20F190050419B /* ParseObjectBatchTests.swift */; }; - 70C7DC2324D620300050419B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */; }; - 70C7DC2424D620330050419B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */; }; - 70E5504324CA4E6000F6D8D2 /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */; }; - 70E5504424CA4E6000F6D8D2 /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */; }; 7FFF552E2217E72A007C3B4E /* AnyEncodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFF552B2217E729007C3B4E /* AnyEncodableTests.swift */; }; 7FFF552F2217E72A007C3B4E /* AnyCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFF552C2217E729007C3B4E /* AnyCodableTests.swift */; }; 7FFF55302217E72A007C3B4E /* AnyDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFF552D2217E729007C3B4E /* AnyDecodableTests.swift */; }; @@ -89,63 +30,154 @@ 912C9BCF24D3005D009947C3 /* ParseSwift_watchOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 912C9BCD24D3005D009947C3 /* ParseSwift_watchOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 912C9BDC24D3011F009947C3 /* ParseSwift_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 912C9BDA24D3011F009947C3 /* ParseSwift_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 912C9BE024D302B0009947C3 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; - 912C9BE124D302B0009947C3 /* ObjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EC1F254B820063D731 /* ObjectType.swift */; }; - 912C9BE224D302B0009947C3 /* ObjectType+Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B8001F256B330063D731 /* ObjectType+Query.swift */; }; - 912C9BE324D302B0009947C3 /* ObjectType+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */; }; - 912C9BE424D302B0009947C3 /* ObjectType+Batch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */; }; - 912C9BE524D302B0009947C3 /* UserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54E1F265A0D00628B17 /* UserType.swift */; }; - 912C9BE624D302B0009947C3 /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC3976F1F48778900DEA9D3 /* ACL.swift */; }; - 912C9BE724D302B0009947C3 /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BD21F78011100665264 /* ParseError.swift */; }; - 912C9BE824D302B0009947C3 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F01F254B820063D731 /* File.swift */; }; - 912C9BE924D302B0009947C3 /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7ED1F254B820063D731 /* GeoPoint.swift */; }; - 912C9BEA24D302B0009947C3 /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F11F254B820063D731 /* Pointer.swift */; }; - 912C9BEB24D302B0009947C3 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7FE1F256A8F0063D731 /* Query.swift */; }; - 912C9BEC24D302B0009947C3 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C424B9117400B95310 /* AnyCodable.swift */; }; - 912C9BED24D302B0009947C3 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C524B9117500B95310 /* AnyDecodable.swift */; }; - 912C9BEE24D302B0009947C3 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C624B9117500B95310 /* AnyEncodable.swift */; }; - 912C9BEF24D302B0009947C3 /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */; }; - 912C9BF024D302B0009947C3 /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */; }; - 912C9BF124D302B0009947C3 /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA5481F26519B00628B17 /* AddOperation.swift */; }; - 912C9BF224D302B0009947C3 /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */; }; - 912C9BF324D302B0009947C3 /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */; }; - 912C9BF424D302B0009947C3 /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */; }; - 912C9BF524D302B0009947C3 /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */; }; - 912C9BF624D302B0009947C3 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397731F488FF900DEA9D3 /* API.swift */; }; - 912C9BF724D302B0009947C3 /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BDC1F783B9800665264 /* API+Commands.swift */; }; - 912C9BF824D302B0009947C3 /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */; }; 912C9BF924D302B0009947C3 /* (null) in Sources */ = {isa = PBXBuildFile; }; - 912C9BFA24D302B0009947C3 /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14951F4A5F2900A7A7EF /* Responses.swift */; }; - 912C9BFB24D302B0009947C3 /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */; }; - 912C9BFC24D302B0009947C3 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */; }; 912C9BFD24D302B2009947C3 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; - 912C9BFE24D302B2009947C3 /* ObjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EC1F254B820063D731 /* ObjectType.swift */; }; - 912C9BFF24D302B2009947C3 /* ObjectType+Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B8001F256B330063D731 /* ObjectType+Query.swift */; }; - 912C9C0024D302B2009947C3 /* ObjectType+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */; }; - 912C9C0124D302B2009947C3 /* ObjectType+Batch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */; }; - 912C9C0224D302B2009947C3 /* UserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54E1F265A0D00628B17 /* UserType.swift */; }; - 912C9C0324D302B2009947C3 /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC3976F1F48778900DEA9D3 /* ACL.swift */; }; - 912C9C0424D302B2009947C3 /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BD21F78011100665264 /* ParseError.swift */; }; - 912C9C0524D302B2009947C3 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F01F254B820063D731 /* File.swift */; }; - 912C9C0624D302B2009947C3 /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7ED1F254B820063D731 /* GeoPoint.swift */; }; - 912C9C0724D302B2009947C3 /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7F11F254B820063D731 /* Pointer.swift */; }; - 912C9C0824D302B2009947C3 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7FE1F256A8F0063D731 /* Query.swift */; }; - 912C9C0924D302B2009947C3 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C424B9117400B95310 /* AnyCodable.swift */; }; - 912C9C0A24D302B2009947C3 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C524B9117500B95310 /* AnyDecodable.swift */; }; - 912C9C0B24D302B2009947C3 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 709075C624B9117500B95310 /* AnyEncodable.swift */; }; - 912C9C0C24D302B2009947C3 /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */; }; - 912C9C0D24D302B2009947C3 /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */; }; - 912C9C0E24D302B2009947C3 /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA5481F26519B00628B17 /* AddOperation.swift */; }; - 912C9C0F24D302B2009947C3 /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */; }; - 912C9C1024D302B2009947C3 /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */; }; - 912C9C1124D302B2009947C3 /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */; }; - 912C9C1224D302B2009947C3 /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */; }; - 912C9C1324D302B2009947C3 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397731F488FF900DEA9D3 /* API.swift */; }; - 912C9C1424D302B2009947C3 /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF85BDC1F783B9800665264 /* API+Commands.swift */; }; - 912C9C1524D302B2009947C3 /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */; }; 912C9C1624D302B2009947C3 /* (null) in Sources */ = {isa = PBXBuildFile; }; - 912C9C1724D302B2009947C3 /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2F14951F4A5F2900A7A7EF /* Responses.swift */; }; - 912C9C1824D302B2009947C3 /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */; }; - 912C9C1924D302B2009947C3 /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */; }; + F971F4F624DE381A006CB79B /* ParseEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F971F4F524DE381A006CB79B /* ParseEncoderTests.swift */; }; + F97B45CE24D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; }; + F97B45CF24D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; }; + F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; }; + F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; }; + F97B45D224D9C6F200F4A88B /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */; }; + F97B45D324D9C6F200F4A88B /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */; }; + F97B45D424D9C6F200F4A88B /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */; }; + F97B45D524D9C6F200F4A88B /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */; }; + F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */; }; + F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */; }; + F97B45D824D9C6F200F4A88B /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */; }; + F97B45D924D9C6F200F4A88B /* ParseEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */; }; + F97B45DA24D9C6F200F4A88B /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B724D9C6F200F4A88B /* Extensions.swift */; }; + F97B45DB24D9C6F200F4A88B /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B724D9C6F200F4A88B /* Extensions.swift */; }; + F97B45DC24D9C6F200F4A88B /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B724D9C6F200F4A88B /* Extensions.swift */; }; + F97B45DD24D9C6F200F4A88B /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B724D9C6F200F4A88B /* Extensions.swift */; }; + F97B45DE24D9C6F200F4A88B /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B824D9C6F200F4A88B /* AnyCodable.swift */; }; + F97B45DF24D9C6F200F4A88B /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B824D9C6F200F4A88B /* AnyCodable.swift */; }; + F97B45E024D9C6F200F4A88B /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B824D9C6F200F4A88B /* AnyCodable.swift */; }; + F97B45E124D9C6F200F4A88B /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B824D9C6F200F4A88B /* AnyCodable.swift */; }; + F97B45E224D9C6F200F4A88B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */; }; + F97B45E324D9C6F200F4A88B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */; }; + F97B45E424D9C6F200F4A88B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */; }; + F97B45E524D9C6F200F4A88B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */; }; + F97B45E624D9C6F200F4A88B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BB24D9C6F200F4A88B /* Query.swift */; }; + F97B45E724D9C6F200F4A88B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BB24D9C6F200F4A88B /* Query.swift */; }; + F97B45E824D9C6F200F4A88B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BB24D9C6F200F4A88B /* Query.swift */; }; + F97B45E924D9C6F200F4A88B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BB24D9C6F200F4A88B /* Query.swift */; }; + F97B45EA24D9C6F200F4A88B /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */; }; + F97B45EB24D9C6F200F4A88B /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */; }; + F97B45EC24D9C6F200F4A88B /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */; }; + F97B45ED24D9C6F200F4A88B /* GeoPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */; }; + F97B45EE24D9C6F200F4A88B /* BaseParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */; }; + F97B45EF24D9C6F200F4A88B /* BaseParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */; }; + F97B45F024D9C6F200F4A88B /* BaseParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */; }; + F97B45F124D9C6F200F4A88B /* BaseParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */; }; + F97B45F224D9C6F200F4A88B /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BE24D9C6F200F4A88B /* Pointer.swift */; }; + F97B45F324D9C6F200F4A88B /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BE24D9C6F200F4A88B /* Pointer.swift */; }; + F97B45F424D9C6F200F4A88B /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BE24D9C6F200F4A88B /* Pointer.swift */; }; + F97B45F524D9C6F200F4A88B /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BE24D9C6F200F4A88B /* Pointer.swift */; }; + F97B45F624D9C6F200F4A88B /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BF24D9C6F200F4A88B /* ParseError.swift */; }; + F97B45F724D9C6F200F4A88B /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BF24D9C6F200F4A88B /* ParseError.swift */; }; + F97B45F824D9C6F200F4A88B /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BF24D9C6F200F4A88B /* ParseError.swift */; }; + F97B45F924D9C6F200F4A88B /* ParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45BF24D9C6F200F4A88B /* ParseError.swift */; }; + F97B45FA24D9C6F200F4A88B /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C024D9C6F200F4A88B /* ACL.swift */; }; + F97B45FB24D9C6F200F4A88B /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C024D9C6F200F4A88B /* ACL.swift */; }; + F97B45FC24D9C6F200F4A88B /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C024D9C6F200F4A88B /* ACL.swift */; }; + F97B45FD24D9C6F200F4A88B /* ACL.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C024D9C6F200F4A88B /* ACL.swift */; }; + F97B45FE24D9C6F200F4A88B /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C124D9C6F200F4A88B /* File.swift */; }; + F97B45FF24D9C6F200F4A88B /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C124D9C6F200F4A88B /* File.swift */; }; + F97B460024D9C6F200F4A88B /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C124D9C6F200F4A88B /* File.swift */; }; + F97B460124D9C6F200F4A88B /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C124D9C6F200F4A88B /* File.swift */; }; + F97B460224D9C6F200F4A88B /* NoBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C224D9C6F200F4A88B /* NoBody.swift */; }; + F97B460324D9C6F200F4A88B /* NoBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C224D9C6F200F4A88B /* NoBody.swift */; }; + F97B460424D9C6F200F4A88B /* NoBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C224D9C6F200F4A88B /* NoBody.swift */; }; + F97B460524D9C6F200F4A88B /* NoBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C224D9C6F200F4A88B /* NoBody.swift */; }; + F97B460624D9C6F200F4A88B /* ParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C424D9C6F200F4A88B /* ParseUser.swift */; }; + F97B460724D9C6F200F4A88B /* ParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C424D9C6F200F4A88B /* ParseUser.swift */; }; + F97B460824D9C6F200F4A88B /* ParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C424D9C6F200F4A88B /* ParseUser.swift */; }; + F97B460924D9C6F200F4A88B /* ParseUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C424D9C6F200F4A88B /* ParseUser.swift */; }; + F97B460A24D9C6F200F4A88B /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C524D9C6F200F4A88B /* Fetchable.swift */; }; + F97B460B24D9C6F200F4A88B /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C524D9C6F200F4A88B /* Fetchable.swift */; }; + F97B460C24D9C6F200F4A88B /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C524D9C6F200F4A88B /* Fetchable.swift */; }; + F97B460D24D9C6F200F4A88B /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C524D9C6F200F4A88B /* Fetchable.swift */; }; + F97B460E24D9C6F200F4A88B /* ParseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C624D9C6F200F4A88B /* ParseObject.swift */; }; + F97B460F24D9C6F200F4A88B /* ParseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C624D9C6F200F4A88B /* ParseObject.swift */; }; + F97B461024D9C6F200F4A88B /* ParseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C624D9C6F200F4A88B /* ParseObject.swift */; }; + F97B461124D9C6F200F4A88B /* ParseObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C624D9C6F200F4A88B /* ParseObject.swift */; }; + F97B461224D9C6F200F4A88B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C724D9C6F200F4A88B /* Saveable.swift */; }; + F97B461324D9C6F200F4A88B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C724D9C6F200F4A88B /* Saveable.swift */; }; + F97B461424D9C6F200F4A88B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C724D9C6F200F4A88B /* Saveable.swift */; }; + F97B461524D9C6F200F4A88B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C724D9C6F200F4A88B /* Saveable.swift */; }; + F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C824D9C6F200F4A88B /* Queryable.swift */; }; + F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C824D9C6F200F4A88B /* Queryable.swift */; }; + F97B461824D9C6F200F4A88B /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C824D9C6F200F4A88B /* Queryable.swift */; }; + F97B461924D9C6F200F4A88B /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45C824D9C6F200F4A88B /* Queryable.swift */; }; + F97B461A24D9C6F200F4A88B /* URLSession+sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */; }; + F97B461B24D9C6F200F4A88B /* URLSession+sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */; }; + F97B461C24D9C6F200F4A88B /* URLSession+sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */; }; + F97B461D24D9C6F200F4A88B /* URLSession+sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */; }; + F97B461E24D9C6F200F4A88B /* ParseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */; }; + F97B461F24D9C6F200F4A88B /* ParseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */; }; + F97B462024D9C6F200F4A88B /* ParseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */; }; + F97B462124D9C6F200F4A88B /* ParseStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */; }; + F97B462224D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */; }; + F97B462324D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */; }; + F97B462424D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */; }; + F97B462524D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */; }; + F97B462724D9C72700F4A88B /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462624D9C72700F4A88B /* API.swift */; }; + F97B462824D9C72700F4A88B /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462624D9C72700F4A88B /* API.swift */; }; + F97B462924D9C72700F4A88B /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462624D9C72700F4A88B /* API.swift */; }; + F97B462A24D9C72700F4A88B /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462624D9C72700F4A88B /* API.swift */; }; + F97B462F24D9C74400F4A88B /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462B24D9C74400F4A88B /* BatchUtils.swift */; }; + F97B463024D9C74400F4A88B /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462B24D9C74400F4A88B /* BatchUtils.swift */; }; + F97B463124D9C74400F4A88B /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462B24D9C74400F4A88B /* BatchUtils.swift */; }; + F97B463224D9C74400F4A88B /* BatchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462B24D9C74400F4A88B /* BatchUtils.swift */; }; + F97B463324D9C74400F4A88B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */; }; + F97B463424D9C74400F4A88B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */; }; + F97B463524D9C74400F4A88B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */; }; + F97B463624D9C74400F4A88B /* URLSession+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */; }; + F97B463724D9C74400F4A88B /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462D24D9C74400F4A88B /* Responses.swift */; }; + F97B463824D9C74400F4A88B /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462D24D9C74400F4A88B /* Responses.swift */; }; + F97B463924D9C74400F4A88B /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462D24D9C74400F4A88B /* Responses.swift */; }; + F97B463A24D9C74400F4A88B /* Responses.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462D24D9C74400F4A88B /* Responses.swift */; }; + F97B463B24D9C74400F4A88B /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462E24D9C74400F4A88B /* API+Commands.swift */; }; + F97B463C24D9C74400F4A88B /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462E24D9C74400F4A88B /* API+Commands.swift */; }; + F97B463D24D9C74400F4A88B /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462E24D9C74400F4A88B /* API+Commands.swift */; }; + F97B463E24D9C74400F4A88B /* API+Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B462E24D9C74400F4A88B /* API+Commands.swift */; }; + F97B464624D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */; }; + F97B464724D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */; }; + F97B464824D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */; }; + F97B464924D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */; }; + F97B464A24D9C78B00F4A88B /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464124D9C78B00F4A88B /* DeleteOperation.swift */; }; + F97B464B24D9C78B00F4A88B /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464124D9C78B00F4A88B /* DeleteOperation.swift */; }; + F97B464C24D9C78B00F4A88B /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464124D9C78B00F4A88B /* DeleteOperation.swift */; }; + F97B464D24D9C78B00F4A88B /* DeleteOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464124D9C78B00F4A88B /* DeleteOperation.swift */; }; + F97B464E24D9C78B00F4A88B /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464224D9C78B00F4A88B /* AddOperation.swift */; }; + F97B464F24D9C78B00F4A88B /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464224D9C78B00F4A88B /* AddOperation.swift */; }; + F97B465024D9C78B00F4A88B /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464224D9C78B00F4A88B /* AddOperation.swift */; }; + F97B465124D9C78C00F4A88B /* AddOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464224D9C78B00F4A88B /* AddOperation.swift */; }; + F97B465224D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */; }; + F97B465324D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */; }; + F97B465424D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */; }; + F97B465524D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */; }; + F97B465624D9C78C00F4A88B /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464424D9C78B00F4A88B /* RemoveOperation.swift */; }; + F97B465724D9C78C00F4A88B /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464424D9C78B00F4A88B /* RemoveOperation.swift */; }; + F97B465824D9C78C00F4A88B /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464424D9C78B00F4A88B /* RemoveOperation.swift */; }; + F97B465924D9C78C00F4A88B /* RemoveOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464424D9C78B00F4A88B /* RemoveOperation.swift */; }; + F97B465A24D9C78C00F4A88B /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464524D9C78B00F4A88B /* IncrementOperation.swift */; }; + F97B465B24D9C78C00F4A88B /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464524D9C78B00F4A88B /* IncrementOperation.swift */; }; + F97B465C24D9C78C00F4A88B /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464524D9C78B00F4A88B /* IncrementOperation.swift */; }; + F97B465D24D9C78C00F4A88B /* IncrementOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B464524D9C78B00F4A88B /* IncrementOperation.swift */; }; + F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B465E24D9C7B500F4A88B /* KeychainStore.swift */; }; + F97B466024D9C7B500F4A88B /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B465E24D9C7B500F4A88B /* KeychainStore.swift */; }; + F97B466124D9C7B500F4A88B /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B465E24D9C7B500F4A88B /* KeychainStore.swift */; }; + F97B466224D9C7B500F4A88B /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B465E24D9C7B500F4A88B /* KeychainStore.swift */; }; + F97B466424D9C88600F4A88B /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466324D9C88600F4A88B /* SecureStorage.swift */; }; + F97B466524D9C88600F4A88B /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466324D9C88600F4A88B /* SecureStorage.swift */; }; + F97B466624D9C88600F4A88B /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466324D9C88600F4A88B /* SecureStorage.swift */; }; + F97B466724D9C88600F4A88B /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466324D9C88600F4A88B /* SecureStorage.swift */; }; + F97B466924D9C8C600F4A88B /* FindResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466824D9C8C600F4A88B /* FindResult.swift */; }; + F97B466A24D9C8C600F4A88B /* FindResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466824D9C8C600F4A88B /* FindResult.swift */; }; + F97B466B24D9C8C600F4A88B /* FindResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466824D9C8C600F4A88B /* FindResult.swift */; }; + F97B466C24D9C8C600F4A88B /* FindResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B466824D9C8C600F4A88B /* FindResult.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -167,22 +199,7 @@ /* Begin PBXFileReference section */ 4A1120BF1F49FC3300E32D94 /* LinuxMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxMain.swift; sourceTree = ""; }; - 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseEncoder.swift; sourceTree = ""; }; - 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObjectType+Equatable.swift"; sourceTree = ""; }; - 4A2F14951F4A5F2900A7A7EF /* Responses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Responses.swift; sourceTree = ""; }; - 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObjectType+Batch.swift"; sourceTree = ""; }; - 4A82B7EC1F254B820063D731 /* ObjectType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectType.swift; sourceTree = ""; }; - 4A82B7ED1F254B820063D731 /* GeoPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoPoint.swift; sourceTree = ""; }; 4A82B7EE1F254B820063D731 /* Parse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parse.swift; sourceTree = ""; }; - 4A82B7F01F254B820063D731 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; - 4A82B7F11F254B820063D731 /* Pointer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pointer.swift; sourceTree = ""; }; - 4A82B7FE1F256A8F0063D731 /* Query.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Query.swift; sourceTree = ""; }; - 4A82B8001F256B330063D731 /* ObjectType+Query.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObjectType+Query.swift"; sourceTree = ""; }; - 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseMutationContainer.swift; sourceTree = ""; }; - 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddUniqueOperation.swift; sourceTree = ""; }; - 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncrementOperation.swift; sourceTree = ""; }; - 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureStorage.swift; sourceTree = ""; }; - 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = ""; }; 4AA807581F794242008CD551 /* TestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4AA8075A1F794242008CD551 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4AA8075F1F794242008CD551 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -191,30 +208,16 @@ 4AA807661F794242008CD551 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4AA8076D1F794C1C008CD551 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4AA8076E1F794C1C008CD551 /* KeychainStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStoreTests.swift; sourceTree = ""; }; - 4AA8076F1F794C1C008CD551 /* ParseSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSwiftTests.swift; sourceTree = ""; }; 4AB8B4F41F254AE10070F682 /* ParseSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ParseSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4AB8B4F71F254AE10070F682 /* Parse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Parse.h; sourceTree = ""; }; 4AB8B4F81F254AE10070F682 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4AB8B4FD1F254AE10070F682 /* ParseSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ParseSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 4AC3976F1F48778900DEA9D3 /* ACL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACL.swift; sourceTree = ""; }; - 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchUtils.swift; sourceTree = ""; }; - 4AC397731F488FF900DEA9D3 /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; 4ACFC2E21F3CA21F0046F3A3 /* ParseSwift.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = ParseSwift.playground; sourceTree = ""; }; - 4AEBA5481F26519B00628B17 /* AddOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOperation.swift; sourceTree = ""; }; - 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveOperation.swift; sourceTree = ""; }; - 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteOperation.swift; sourceTree = ""; }; - 4AEBA54E1F265A0D00628B17 /* UserType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserType.swift; sourceTree = ""; }; - 4AF85BD21F78011100665264 /* ParseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseError.swift; sourceTree = ""; }; - 4AF85BDC1F783B9800665264 /* API+Commands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+Commands.swift"; sourceTree = ""; }; 4AFDA7121F26D9A5002AE4FC /* ParseSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ParseSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4AFDA7151F26D9A5002AE4FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 709075C424B9117400B95310 /* AnyCodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = ""; }; - 709075C524B9117500B95310 /* AnyDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = ""; }; - 709075C624B9117500B95310 /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = ""; }; 70C7DC1D24D20E530050419B /* ParseUserCommandTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseUserCommandTests.swift; sourceTree = ""; }; 70C7DC1F24D20F180050419B /* ParseQueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseQueryTests.swift; sourceTree = ""; }; 70C7DC2024D20F190050419B /* ParseObjectBatchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseObjectBatchTests.swift; sourceTree = ""; }; - 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLSession+extensions.swift"; sourceTree = ""; }; 7FFF552B2217E729007C3B4E /* AnyEncodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodableTests.swift; sourceTree = ""; }; 7FFF552C2217E729007C3B4E /* AnyCodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyCodableTests.swift; sourceTree = ""; }; 7FFF552D2217E729007C3B4E /* AnyDecodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyDecodableTests.swift; sourceTree = ""; }; @@ -228,6 +231,43 @@ 912C9BD824D3011F009947C3 /* ParseSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ParseSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 912C9BDA24D3011F009947C3 /* ParseSwift_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParseSwift_tvOS.h; sourceTree = ""; }; 912C9BDB24D3011F009947C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F971F4F524DE381A006CB79B /* ParseEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseEncoderTests.swift; sourceTree = ""; }; + F97B45B424D9C6F200F4A88B /* ParseCoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseCoding.swift; sourceTree = ""; }; + F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = ""; }; + F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseEncoder.swift; sourceTree = ""; }; + F97B45B724D9C6F200F4A88B /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + F97B45B824D9C6F200F4A88B /* AnyCodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = ""; }; + F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = ""; }; + F97B45BB24D9C6F200F4A88B /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Query.swift; sourceTree = ""; }; + F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeoPoint.swift; sourceTree = ""; }; + F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseParseUser.swift; sourceTree = ""; }; + F97B45BE24D9C6F200F4A88B /* Pointer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pointer.swift; sourceTree = ""; }; + F97B45BF24D9C6F200F4A88B /* ParseError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseError.swift; sourceTree = ""; }; + F97B45C024D9C6F200F4A88B /* ACL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACL.swift; sourceTree = ""; }; + F97B45C124D9C6F200F4A88B /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; + F97B45C224D9C6F200F4A88B /* NoBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoBody.swift; sourceTree = ""; }; + F97B45C424D9C6F200F4A88B /* ParseUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseUser.swift; sourceTree = ""; }; + F97B45C524D9C6F200F4A88B /* Fetchable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = ""; }; + F97B45C624D9C6F200F4A88B /* ParseObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseObject.swift; sourceTree = ""; }; + F97B45C724D9C6F200F4A88B /* Saveable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Saveable.swift; sourceTree = ""; }; + F97B45C824D9C6F200F4A88B /* Queryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queryable.swift; sourceTree = ""; }; + F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLSession+sync.swift"; sourceTree = ""; }; + F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseStorage.swift; sourceTree = ""; }; + F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveObjectStore.swift; sourceTree = ""; }; + F97B462624D9C72700F4A88B /* API.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; + F97B462B24D9C74400F4A88B /* BatchUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchUtils.swift; sourceTree = ""; }; + F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLSession+extensions.swift"; sourceTree = ""; }; + F97B462D24D9C74400F4A88B /* Responses.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Responses.swift; sourceTree = ""; }; + F97B462E24D9C74400F4A88B /* API+Commands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "API+Commands.swift"; sourceTree = ""; }; + F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseMutationContainer.swift; sourceTree = ""; }; + F97B464124D9C78B00F4A88B /* DeleteOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeleteOperation.swift; sourceTree = ""; }; + F97B464224D9C78B00F4A88B /* AddOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddOperation.swift; sourceTree = ""; }; + F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddUniqueOperation.swift; sourceTree = ""; }; + F97B464424D9C78B00F4A88B /* RemoveOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoveOperation.swift; sourceTree = ""; }; + F97B464524D9C78B00F4A88B /* IncrementOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementOperation.swift; sourceTree = ""; }; + F97B465E24D9C7B500F4A88B /* KeychainStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = ""; }; + F97B466324D9C88600F4A88B /* SecureStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureStorage.swift; sourceTree = ""; }; + F97B466824D9C8C600F4A88B /* FindResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindResult.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -277,14 +317,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 4A2F149C1F4A604900A7A7EF /* Encoder */ = { - isa = PBXGroup; - children = ( - 4A2D8C801F48B3A900EE1FCC /* ParseEncoder.swift */, - ); - path = Encoder; - sourceTree = ""; - }; 4A5EE45F1F49E9E000D3CAE3 /* Sources */ = { isa = PBXGroup; children = ( @@ -310,42 +342,6 @@ path = "ParseSwift-iOS"; sourceTree = ""; }; - 4A82B7FD1F25691B0063D731 /* Types */ = { - isa = PBXGroup; - children = ( - 4AC3976F1F48778900DEA9D3 /* ACL.swift */, - 4AF85BD21F78011100665264 /* ParseError.swift */, - 4A82B7F01F254B820063D731 /* File.swift */, - 4A82B7ED1F254B820063D731 /* GeoPoint.swift */, - 4A82B7F11F254B820063D731 /* Pointer.swift */, - 4A82B7FE1F256A8F0063D731 /* Query.swift */, - ); - path = Types; - sourceTree = ""; - }; - 4A99A46A1F2650E200D72A59 /* Mutations */ = { - isa = PBXGroup; - children = ( - 4A99A4681F2650CA00D72A59 /* ParseMutationContainer.swift */, - 4AEBA5481F26519B00628B17 /* AddOperation.swift */, - 4AEBA54A1F2651D900628B17 /* RemoveOperation.swift */, - 4AEBA54C1F26523800628B17 /* DeleteOperation.swift */, - 4A99A46B1F2650FF00D72A59 /* AddUniqueOperation.swift */, - 4A99A46D1F26512100D72A59 /* IncrementOperation.swift */, - ); - name = Mutations; - path = MutationOperations; - sourceTree = ""; - }; - 4AA807491F7930D0008CD551 /* Storage */ = { - isa = PBXGroup; - children = ( - 4AA8074A1F7930E9008CD551 /* SecureStorage.swift */, - 4AA8074D1F7931B7008CD551 /* KeychainStore.swift */, - ); - path = Storage; - sourceTree = ""; - }; 4AA807591F794242008CD551 /* TestHost */ = { isa = PBXGroup; children = ( @@ -364,10 +360,10 @@ 4AA8076D1F794C1C008CD551 /* Info.plist */, 911DB12D24C4837E0027F3C7 /* APICommandTests.swift */, 4AA8076E1F794C1C008CD551 /* KeychainStoreTests.swift */, + F971F4F524DE381A006CB79B /* ParseEncoderTests.swift */, 70C7DC2024D20F190050419B /* ParseObjectBatchTests.swift */, 911DB13524C4FC100027F3C7 /* ParseObjectCommandTests.swift */, 70C7DC1F24D20F180050419B /* ParseQueryTests.swift */, - 4AA8076F1F794C1C008CD551 /* ParseSwiftTests.swift */, 70C7DC1D24D20E530050419B /* ParseUserCommandTests.swift */, 7FFF552A2217E729007C3B4E /* AnyCodableTests */, 911DB12A24C3F7260027F3C7 /* NetworkMocking */, @@ -406,43 +402,18 @@ 4AB8B4F61F254AE10070F682 /* ParseSwift */ = { isa = PBXGroup; children = ( + F97B45C924D9C6F200F4A88B /* API */, + F97B45B324D9C6F200F4A88B /* Coding */, + F97B463F24D9C78B00F4A88B /* Mutation Operations */, + F97B45C324D9C6F200F4A88B /* Objects */, + F97B45BA24D9C6F200F4A88B /* Parse Types */, + F97B45CB24D9C6F200F4A88B /* Storage */, 4A82B7EE1F254B820063D731 /* Parse.swift */, - 4AC397761F4895A200DEA9D3 /* Objects Protocols */, - 4A82B7FD1F25691B0063D731 /* Types */, - 709075CD24B9829600B95310 /* AnyCodable */, - 4A2F149C1F4A604900A7A7EF /* Encoder */, - 4A99A46A1F2650E200D72A59 /* Mutations */, - 4AC397771F4895C000DEA9D3 /* API */, - 4AA807491F7930D0008CD551 /* Storage */, 4AB8B4F71F254AE10070F682 /* Parse.h */, ); path = ParseSwift; sourceTree = ""; }; - 4AC397761F4895A200DEA9D3 /* Objects Protocols */ = { - isa = PBXGroup; - children = ( - 4A82B7EC1F254B820063D731 /* ObjectType.swift */, - 4A82B8001F256B330063D731 /* ObjectType+Query.swift */, - 4A2F14931F4A5E1E00A7A7EF /* ObjectType+Equatable.swift */, - 4A2F14971F4A5F6900A7A7EF /* ObjectType+Batch.swift */, - 4AEBA54E1F265A0D00628B17 /* UserType.swift */, - ); - path = "Objects Protocols"; - sourceTree = ""; - }; - 4AC397771F4895C000DEA9D3 /* API */ = { - isa = PBXGroup; - children = ( - 4AC397731F488FF900DEA9D3 /* API.swift */, - 4AF85BDC1F783B9800665264 /* API+Commands.swift */, - 4AC397711F488F9E00DEA9D3 /* BatchUtils.swift */, - 70E5504224CA4E5F00F6D8D2 /* URLSession+extensions.swift */, - 4A2F14951F4A5F2900A7A7EF /* Responses.swift */, - ); - path = API; - sourceTree = ""; - }; 4AFDA7131F26D9A5002AE4FC /* ParseSwift-macOS */ = { isa = PBXGroup; children = ( @@ -451,16 +422,6 @@ path = "ParseSwift-macOS"; sourceTree = ""; }; - 709075CD24B9829600B95310 /* AnyCodable */ = { - isa = PBXGroup; - children = ( - 709075C424B9117400B95310 /* AnyCodable.swift */, - 709075C524B9117500B95310 /* AnyDecodable.swift */, - 709075C624B9117500B95310 /* AnyEncodable.swift */, - ); - path = AnyCodable; - sourceTree = ""; - }; 7FFF552A2217E729007C3B4E /* AnyCodableTests */ = { isa = PBXGroup; children = ( @@ -498,6 +459,84 @@ path = "ParseSwift-tvOS"; sourceTree = ""; }; + F97B45B324D9C6F200F4A88B /* Coding */ = { + isa = PBXGroup; + children = ( + F97B45B424D9C6F200F4A88B /* ParseCoding.swift */, + F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */, + F97B45B624D9C6F200F4A88B /* ParseEncoder.swift */, + F97B45B724D9C6F200F4A88B /* Extensions.swift */, + F97B45B824D9C6F200F4A88B /* AnyCodable.swift */, + F97B45B924D9C6F200F4A88B /* AnyEncodable.swift */, + ); + path = Coding; + sourceTree = ""; + }; + F97B45BA24D9C6F200F4A88B /* Parse Types */ = { + isa = PBXGroup; + children = ( + F97B45BB24D9C6F200F4A88B /* Query.swift */, + F97B45BC24D9C6F200F4A88B /* GeoPoint.swift */, + F97B45BD24D9C6F200F4A88B /* BaseParseUser.swift */, + F97B466824D9C8C600F4A88B /* FindResult.swift */, + F97B45BE24D9C6F200F4A88B /* Pointer.swift */, + F97B45BF24D9C6F200F4A88B /* ParseError.swift */, + F97B45C024D9C6F200F4A88B /* ACL.swift */, + F97B45C124D9C6F200F4A88B /* File.swift */, + F97B45C224D9C6F200F4A88B /* NoBody.swift */, + ); + path = "Parse Types"; + sourceTree = ""; + }; + F97B45C324D9C6F200F4A88B /* Objects */ = { + isa = PBXGroup; + children = ( + F97B45C424D9C6F200F4A88B /* ParseUser.swift */, + F97B45C524D9C6F200F4A88B /* Fetchable.swift */, + F97B45C624D9C6F200F4A88B /* ParseObject.swift */, + F97B45C724D9C6F200F4A88B /* Saveable.swift */, + F97B45C824D9C6F200F4A88B /* Queryable.swift */, + ); + path = Objects; + sourceTree = ""; + }; + F97B45C924D9C6F200F4A88B /* API */ = { + isa = PBXGroup; + children = ( + F97B462624D9C72700F4A88B /* API.swift */, + F97B462E24D9C74400F4A88B /* API+Commands.swift */, + F97B462B24D9C74400F4A88B /* BatchUtils.swift */, + F97B462D24D9C74400F4A88B /* Responses.swift */, + F97B462C24D9C74400F4A88B /* URLSession+extensions.swift */, + F97B45CA24D9C6F200F4A88B /* URLSession+sync.swift */, + ); + path = API; + sourceTree = ""; + }; + F97B45CB24D9C6F200F4A88B /* Storage */ = { + isa = PBXGroup; + children = ( + F97B465E24D9C7B500F4A88B /* KeychainStore.swift */, + F97B45CC24D9C6F200F4A88B /* ParseStorage.swift */, + F97B45CD24D9C6F200F4A88B /* PrimitiveObjectStore.swift */, + F97B466324D9C88600F4A88B /* SecureStorage.swift */, + ); + path = Storage; + sourceTree = ""; + }; + F97B463F24D9C78B00F4A88B /* Mutation Operations */ = { + isa = PBXGroup; + children = ( + F97B464024D9C78B00F4A88B /* ParseMutationContainer.swift */, + F97B464124D9C78B00F4A88B /* DeleteOperation.swift */, + F97B464224D9C78B00F4A88B /* AddOperation.swift */, + F97B464324D9C78B00F4A88B /* AddUniqueOperation.swift */, + F97B464424D9C78B00F4A88B /* RemoveOperation.swift */, + F97B464524D9C78B00F4A88B /* IncrementOperation.swift */, + ); + path = "Mutation Operations"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -799,35 +838,43 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4A2F14961F4A5F2900A7A7EF /* Responses.swift in Sources */, - 709075C724B9117500B95310 /* AnyCodable.swift in Sources */, - 4AC397741F488FF900DEA9D3 /* API.swift in Sources */, - 4A82B7F71F254CCE0063D731 /* ObjectType.swift in Sources */, - 70E5504324CA4E6000F6D8D2 /* URLSession+extensions.swift in Sources */, - 4A82B7F51F254CCE0063D731 /* GeoPoint.swift in Sources */, - 4AA8074B1F7930E9008CD551 /* SecureStorage.swift in Sources */, + F97B463724D9C74400F4A88B /* Responses.swift in Sources */, + F97B461624D9C6F200F4A88B /* Queryable.swift in Sources */, + F97B461A24D9C6F200F4A88B /* URLSession+sync.swift in Sources */, + F97B45DA24D9C6F200F4A88B /* Extensions.swift in Sources */, + F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */, + F97B465224D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */, + F97B45D624D9C6F200F4A88B /* ParseEncoder.swift in Sources */, + F97B45F224D9C6F200F4A88B /* Pointer.swift in Sources */, + F97B461E24D9C6F200F4A88B /* ParseStorage.swift in Sources */, + F97B45D224D9C6F200F4A88B /* AnyDecodable.swift in Sources */, + F97B463B24D9C74400F4A88B /* API+Commands.swift in Sources */, + F97B464624D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */, + F97B464A24D9C78B00F4A88B /* DeleteOperation.swift in Sources */, + F97B460624D9C6F200F4A88B /* ParseUser.swift in Sources */, + F97B465A24D9C78C00F4A88B /* IncrementOperation.swift in Sources */, + F97B45E224D9C6F200F4A88B /* AnyEncodable.swift in Sources */, + F97B466924D9C8C600F4A88B /* FindResult.swift in Sources */, + F97B462224D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */, + F97B45E624D9C6F200F4A88B /* Query.swift in Sources */, + F97B466424D9C88600F4A88B /* SecureStorage.swift in Sources */, + F97B462F24D9C74400F4A88B /* BatchUtils.swift in Sources */, 4A82B7F61F254CCE0063D731 /* Parse.swift in Sources */, - 4AEBA54F1F265A0D00628B17 /* UserType.swift in Sources */, - 4A99A4691F2650CA00D72A59 /* ParseMutationContainer.swift in Sources */, - 4AC397701F48778900DEA9D3 /* ACL.swift in Sources */, - 4A2D8C811F48B3A900EE1FCC /* ParseEncoder.swift in Sources */, - 4A99A46E1F26512100D72A59 /* IncrementOperation.swift in Sources */, - 4A82B8011F256B330063D731 /* ObjectType+Query.swift in Sources */, - 4A82B7FF1F256A8F0063D731 /* Query.swift in Sources */, - 4A2F14981F4A5F6900A7A7EF /* ObjectType+Batch.swift in Sources */, - 709075CB24B9117500B95310 /* AnyEncodable.swift in Sources */, - 4A82B7F41F254CCE0063D731 /* File.swift in Sources */, - 4AF85BD31F78011100665264 /* ParseError.swift in Sources */, - 4AA8074E1F7931B7008CD551 /* KeychainStore.swift in Sources */, - 4A2F14941F4A5E1E00A7A7EF /* ObjectType+Equatable.swift in Sources */, - 4AC397721F488F9E00DEA9D3 /* BatchUtils.swift in Sources */, - 4AEBA54D1F26523800628B17 /* DeleteOperation.swift in Sources */, - 4AF85BDD1F783B9800665264 /* API+Commands.swift in Sources */, - 4A82B7F81F254CCE0063D731 /* Pointer.swift in Sources */, - 709075C924B9117500B95310 /* AnyDecodable.swift in Sources */, - 4AEBA5491F26519B00628B17 /* AddOperation.swift in Sources */, - 4A99A46C1F2650FF00D72A59 /* AddUniqueOperation.swift in Sources */, - 4AEBA54B1F2651D900628B17 /* RemoveOperation.swift in Sources */, + F97B45EA24D9C6F200F4A88B /* GeoPoint.swift in Sources */, + F97B460224D9C6F200F4A88B /* NoBody.swift in Sources */, + F97B45F624D9C6F200F4A88B /* ParseError.swift in Sources */, + F97B463324D9C74400F4A88B /* URLSession+extensions.swift in Sources */, + F97B464E24D9C78B00F4A88B /* AddOperation.swift in Sources */, + F97B45FE24D9C6F200F4A88B /* File.swift in Sources */, + F97B45EE24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, + F97B460A24D9C6F200F4A88B /* Fetchable.swift in Sources */, + F97B460E24D9C6F200F4A88B /* ParseObject.swift in Sources */, + F97B461224D9C6F200F4A88B /* Saveable.swift in Sources */, + F97B45CE24D9C6F200F4A88B /* ParseCoding.swift in Sources */, + F97B465624D9C78C00F4A88B /* RemoveOperation.swift in Sources */, + F97B45FA24D9C6F200F4A88B /* ACL.swift in Sources */, + F97B462724D9C72700F4A88B /* API.swift in Sources */, + F97B45DE24D9C6F200F4A88B /* AnyCodable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -843,7 +890,7 @@ 70C7DC2224D20F190050419B /* ParseObjectBatchTests.swift in Sources */, 7FFF552F2217E72A007C3B4E /* AnyCodableTests.swift in Sources */, 4AA807701F794C31008CD551 /* KeychainStoreTests.swift in Sources */, - 4AA807711F794C33008CD551 /* ParseSwiftTests.swift in Sources */, + F971F4F624DE381A006CB79B /* ParseEncoderTests.swift in Sources */, 70C7DC2124D20F190050419B /* ParseQueryTests.swift in Sources */, 70C7DC1E24D20E530050419B /* ParseUserCommandTests.swift in Sources */, 911DB13324C494390027F3C7 /* MockURLProtocol.swift in Sources */, @@ -854,35 +901,43 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4AFDA72B1F26DAE1002AE4FC /* ObjectType.swift in Sources */, - 709075C824B9117500B95310 /* AnyCodable.swift in Sources */, - 4AFDA7301F26DAE1002AE4FC /* DeleteOperation.swift in Sources */, - 4AFDA7361F26DAE1002AE4FC /* ObjectType+Query.swift in Sources */, - 70E5504424CA4E6000F6D8D2 /* URLSession+extensions.swift in Sources */, - 4AFDA7341F26DAE1002AE4FC /* Pointer.swift in Sources */, - 4AA8074C1F7930E9008CD551 /* SecureStorage.swift in Sources */, - 4A6511501F48E400005237DF /* BatchUtils.swift in Sources */, + F97B463824D9C74400F4A88B /* Responses.swift in Sources */, + F97B461724D9C6F200F4A88B /* Queryable.swift in Sources */, + F97B461B24D9C6F200F4A88B /* URLSession+sync.swift in Sources */, + F97B45DB24D9C6F200F4A88B /* Extensions.swift in Sources */, + F97B466024D9C7B500F4A88B /* KeychainStore.swift in Sources */, + F97B465324D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */, + F97B45D724D9C6F200F4A88B /* ParseEncoder.swift in Sources */, + F97B45F324D9C6F200F4A88B /* Pointer.swift in Sources */, + F97B461F24D9C6F200F4A88B /* ParseStorage.swift in Sources */, + F97B45D324D9C6F200F4A88B /* AnyDecodable.swift in Sources */, + F97B463C24D9C74400F4A88B /* API+Commands.swift in Sources */, + F97B464724D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */, + F97B464B24D9C78B00F4A88B /* DeleteOperation.swift in Sources */, + F97B460724D9C6F200F4A88B /* ParseUser.swift in Sources */, + F97B465B24D9C78C00F4A88B /* IncrementOperation.swift in Sources */, + F97B45E324D9C6F200F4A88B /* AnyEncodable.swift in Sources */, + F97B466A24D9C8C600F4A88B /* FindResult.swift in Sources */, + F97B462324D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */, + F97B45E724D9C6F200F4A88B /* Query.swift in Sources */, + F97B466524D9C88600F4A88B /* SecureStorage.swift in Sources */, + F97B463024D9C74400F4A88B /* BatchUtils.swift in Sources */, 4AFDA72A1F26DAE1002AE4FC /* Parse.swift in Sources */, - 4AFDA7351F26DAE1002AE4FC /* Query.swift in Sources */, - 4A2F14991F4A5FB500A7A7EF /* Responses.swift in Sources */, - 4A2F149A1F4A5FBA00A7A7EF /* ObjectType+Equatable.swift in Sources */, - 4A65114F1F48E3F3005237DF /* ParseEncoder.swift in Sources */, - 4AFDA72E1F26DAE1002AE4FC /* AddOperation.swift in Sources */, - 4AFDA7311F26DAE1002AE4FC /* AddUniqueOperation.swift in Sources */, - 4A2F149B1F4A5FBA00A7A7EF /* ObjectType+Batch.swift in Sources */, - 709075CC24B9117500B95310 /* AnyEncodable.swift in Sources */, - 4A6511521F48E406005237DF /* GeoPoint.swift in Sources */, - 4AF85BD61F7803C100665264 /* ParseError.swift in Sources */, - 4AA8074F1F7931B7008CD551 /* KeychainStore.swift in Sources */, - 4AFDA7331F26DAE1002AE4FC /* File.swift in Sources */, - 4A6511531F48E410005237DF /* API.swift in Sources */, - 4AFDA7321F26DAE1002AE4FC /* IncrementOperation.swift in Sources */, - 4AF85BDE1F783BB400665264 /* API+Commands.swift in Sources */, - 4AFDA72D1F26DAE1002AE4FC /* ParseMutationContainer.swift in Sources */, - 709075CA24B9117500B95310 /* AnyDecodable.swift in Sources */, - 4AFDA72C1F26DAE1002AE4FC /* UserType.swift in Sources */, - 4A6511511F48E406005237DF /* ACL.swift in Sources */, - 4AFDA72F1F26DAE1002AE4FC /* RemoveOperation.swift in Sources */, + F97B45EB24D9C6F200F4A88B /* GeoPoint.swift in Sources */, + F97B460324D9C6F200F4A88B /* NoBody.swift in Sources */, + F97B45F724D9C6F200F4A88B /* ParseError.swift in Sources */, + F97B463424D9C74400F4A88B /* URLSession+extensions.swift in Sources */, + F97B464F24D9C78B00F4A88B /* AddOperation.swift in Sources */, + F97B45FF24D9C6F200F4A88B /* File.swift in Sources */, + F97B45EF24D9C6F200F4A88B /* BaseParseUser.swift in Sources */, + F97B460B24D9C6F200F4A88B /* Fetchable.swift in Sources */, + F97B460F24D9C6F200F4A88B /* ParseObject.swift in Sources */, + F97B461324D9C6F200F4A88B /* Saveable.swift in Sources */, + F97B45CF24D9C6F200F4A88B /* ParseCoding.swift in Sources */, + F97B465724D9C78C00F4A88B /* RemoveOperation.swift in Sources */, + F97B45FB24D9C6F200F4A88B /* ACL.swift in Sources */, + F97B462824D9C72700F4A88B /* API.swift in Sources */, + F97B45DF24D9C6F200F4A88B /* AnyCodable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -890,36 +945,44 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 912C9C1724D302B2009947C3 /* Responses.swift in Sources */, - 912C9C1024D302B2009947C3 /* DeleteOperation.swift in Sources */, - 912C9C1824D302B2009947C3 /* SecureStorage.swift in Sources */, - 912C9C0924D302B2009947C3 /* AnyCodable.swift in Sources */, - 912C9C0B24D302B2009947C3 /* AnyEncodable.swift in Sources */, - 912C9C0424D302B2009947C3 /* ParseError.swift in Sources */, - 912C9BFE24D302B2009947C3 /* ObjectType.swift in Sources */, + F97B45D524D9C6F200F4A88B /* AnyDecodable.swift in Sources */, + F97B45E924D9C6F200F4A88B /* Query.swift in Sources */, + F97B463624D9C74400F4A88B /* URLSession+extensions.swift in Sources */, + F97B460524D9C6F200F4A88B /* NoBody.swift in Sources */, + F97B45E124D9C6F200F4A88B /* AnyCodable.swift in Sources */, + F97B45E524D9C6F200F4A88B /* AnyEncodable.swift in Sources */, + F97B465D24D9C78C00F4A88B /* IncrementOperation.swift in Sources */, + F97B45FD24D9C6F200F4A88B /* ACL.swift in Sources */, + F97B465124D9C78C00F4A88B /* AddOperation.swift in Sources */, + F97B461124D9C6F200F4A88B /* ParseObject.swift in Sources */, + F97B460D24D9C6F200F4A88B /* Fetchable.swift in Sources */, + F97B45ED24D9C6F200F4A88B /* GeoPoint.swift in Sources */, + F97B45F524D9C6F200F4A88B /* Pointer.swift in Sources */, + F97B460924D9C6F200F4A88B /* ParseUser.swift in Sources */, + F97B463A24D9C74400F4A88B /* Responses.swift in Sources */, + F97B466C24D9C8C600F4A88B /* FindResult.swift in Sources */, + F97B45DD24D9C6F200F4A88B /* Extensions.swift in Sources */, + F97B462124D9C6F200F4A88B /* ParseStorage.swift in Sources */, + F97B466724D9C88600F4A88B /* SecureStorage.swift in Sources */, 912C9C1624D302B2009947C3 /* (null) in Sources */, - 912C9C0724D302B2009947C3 /* Pointer.swift in Sources */, - 912C9C1124D302B2009947C3 /* AddUniqueOperation.swift in Sources */, - 912C9C0124D302B2009947C3 /* ObjectType+Batch.swift in Sources */, - 912C9C0824D302B2009947C3 /* Query.swift in Sources */, - 912C9C0E24D302B2009947C3 /* AddOperation.swift in Sources */, - 912C9C0224D302B2009947C3 /* UserType.swift in Sources */, - 912C9C1524D302B2009947C3 /* BatchUtils.swift in Sources */, - 912C9C0F24D302B2009947C3 /* RemoveOperation.swift in Sources */, - 912C9C1424D302B2009947C3 /* API+Commands.swift in Sources */, - 912C9C0C24D302B2009947C3 /* ParseEncoder.swift in Sources */, - 912C9C0624D302B2009947C3 /* GeoPoint.swift in Sources */, - 912C9C0024D302B2009947C3 /* ObjectType+Equatable.swift in Sources */, - 912C9C1324D302B2009947C3 /* API.swift in Sources */, - 912C9C0324D302B2009947C3 /* ACL.swift in Sources */, - 912C9BFF24D302B2009947C3 /* ObjectType+Query.swift in Sources */, - 912C9C0524D302B2009947C3 /* File.swift in Sources */, - 912C9C0A24D302B2009947C3 /* AnyDecodable.swift in Sources */, - 912C9C1224D302B2009947C3 /* IncrementOperation.swift in Sources */, - 70C7DC2424D620330050419B /* URLSession+extensions.swift in Sources */, + F97B465924D9C78C00F4A88B /* RemoveOperation.swift in Sources */, + F97B45F924D9C6F200F4A88B /* ParseError.swift in Sources */, + F97B460124D9C6F200F4A88B /* File.swift in Sources */, + F97B464924D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */, + F97B45D124D9C6F200F4A88B /* ParseCoding.swift in Sources */, + F97B465524D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */, + F97B464D24D9C78B00F4A88B /* DeleteOperation.swift in Sources */, + F97B461524D9C6F200F4A88B /* Saveable.swift in Sources */, + F97B462524D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */, + F97B466224D9C7B500F4A88B /* KeychainStore.swift in Sources */, + F97B463E24D9C74400F4A88B /* API+Commands.swift in Sources */, + F97B462A24D9C72700F4A88B /* API.swift in Sources */, + F97B463224D9C74400F4A88B /* BatchUtils.swift in Sources */, + F97B461D24D9C6F200F4A88B /* URLSession+sync.swift in Sources */, + F97B45F124D9C6F200F4A88B /* BaseParseUser.swift in Sources */, + F97B45D924D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 912C9BFD24D302B2009947C3 /* Parse.swift in Sources */, - 912C9C0D24D302B2009947C3 /* ParseMutationContainer.swift in Sources */, - 912C9C1924D302B2009947C3 /* KeychainStore.swift in Sources */, + F97B461924D9C6F200F4A88B /* Queryable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -927,36 +990,44 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 912C9BFA24D302B0009947C3 /* Responses.swift in Sources */, - 912C9BF324D302B0009947C3 /* DeleteOperation.swift in Sources */, - 912C9BFB24D302B0009947C3 /* SecureStorage.swift in Sources */, - 912C9BEC24D302B0009947C3 /* AnyCodable.swift in Sources */, - 912C9BEE24D302B0009947C3 /* AnyEncodable.swift in Sources */, - 912C9BE724D302B0009947C3 /* ParseError.swift in Sources */, - 912C9BE124D302B0009947C3 /* ObjectType.swift in Sources */, + F97B45D424D9C6F200F4A88B /* AnyDecodable.swift in Sources */, + F97B45E824D9C6F200F4A88B /* Query.swift in Sources */, + F97B463524D9C74400F4A88B /* URLSession+extensions.swift in Sources */, + F97B460424D9C6F200F4A88B /* NoBody.swift in Sources */, + F97B45E024D9C6F200F4A88B /* AnyCodable.swift in Sources */, + F97B45E424D9C6F200F4A88B /* AnyEncodable.swift in Sources */, + F97B465C24D9C78C00F4A88B /* IncrementOperation.swift in Sources */, + F97B45FC24D9C6F200F4A88B /* ACL.swift in Sources */, + F97B465024D9C78B00F4A88B /* AddOperation.swift in Sources */, + F97B461024D9C6F200F4A88B /* ParseObject.swift in Sources */, + F97B460C24D9C6F200F4A88B /* Fetchable.swift in Sources */, + F97B45EC24D9C6F200F4A88B /* GeoPoint.swift in Sources */, + F97B45F424D9C6F200F4A88B /* Pointer.swift in Sources */, + F97B460824D9C6F200F4A88B /* ParseUser.swift in Sources */, + F97B463924D9C74400F4A88B /* Responses.swift in Sources */, + F97B466B24D9C8C600F4A88B /* FindResult.swift in Sources */, + F97B45DC24D9C6F200F4A88B /* Extensions.swift in Sources */, + F97B462024D9C6F200F4A88B /* ParseStorage.swift in Sources */, + F97B466624D9C88600F4A88B /* SecureStorage.swift in Sources */, 912C9BF924D302B0009947C3 /* (null) in Sources */, - 912C9BEA24D302B0009947C3 /* Pointer.swift in Sources */, - 912C9BF424D302B0009947C3 /* AddUniqueOperation.swift in Sources */, - 912C9BE424D302B0009947C3 /* ObjectType+Batch.swift in Sources */, - 912C9BEB24D302B0009947C3 /* Query.swift in Sources */, - 912C9BF124D302B0009947C3 /* AddOperation.swift in Sources */, - 912C9BE524D302B0009947C3 /* UserType.swift in Sources */, - 912C9BF824D302B0009947C3 /* BatchUtils.swift in Sources */, - 912C9BF224D302B0009947C3 /* RemoveOperation.swift in Sources */, - 912C9BF724D302B0009947C3 /* API+Commands.swift in Sources */, - 912C9BEF24D302B0009947C3 /* ParseEncoder.swift in Sources */, - 912C9BE924D302B0009947C3 /* GeoPoint.swift in Sources */, - 912C9BE324D302B0009947C3 /* ObjectType+Equatable.swift in Sources */, - 912C9BF624D302B0009947C3 /* API.swift in Sources */, - 912C9BE624D302B0009947C3 /* ACL.swift in Sources */, - 912C9BE224D302B0009947C3 /* ObjectType+Query.swift in Sources */, - 912C9BE824D302B0009947C3 /* File.swift in Sources */, - 912C9BED24D302B0009947C3 /* AnyDecodable.swift in Sources */, - 912C9BF524D302B0009947C3 /* IncrementOperation.swift in Sources */, - 70C7DC2324D620300050419B /* URLSession+extensions.swift in Sources */, + F97B465824D9C78C00F4A88B /* RemoveOperation.swift in Sources */, + F97B45F824D9C6F200F4A88B /* ParseError.swift in Sources */, + F97B460024D9C6F200F4A88B /* File.swift in Sources */, + F97B464824D9C78B00F4A88B /* ParseMutationContainer.swift in Sources */, + F97B45D024D9C6F200F4A88B /* ParseCoding.swift in Sources */, + F97B465424D9C78C00F4A88B /* AddUniqueOperation.swift in Sources */, + F97B464C24D9C78B00F4A88B /* DeleteOperation.swift in Sources */, + F97B461424D9C6F200F4A88B /* Saveable.swift in Sources */, + F97B462424D9C6F200F4A88B /* PrimitiveObjectStore.swift in Sources */, + F97B466124D9C7B500F4A88B /* KeychainStore.swift in Sources */, + F97B463D24D9C74400F4A88B /* API+Commands.swift in Sources */, + F97B462924D9C72700F4A88B /* API.swift in Sources */, + F97B463124D9C74400F4A88B /* BatchUtils.swift in Sources */, + F97B461C24D9C6F200F4A88B /* URLSession+sync.swift in Sources */, + F97B45F024D9C6F200F4A88B /* BaseParseUser.swift in Sources */, + F97B45D824D9C6F200F4A88B /* ParseEncoder.swift in Sources */, 912C9BE024D302B0009947C3 /* Parse.swift in Sources */, - 912C9BF024D302B0009947C3 /* ParseMutationContainer.swift in Sources */, - 912C9BFC24D302B0009947C3 /* KeychainStore.swift in Sources */, + F97B461824D9C6F200F4A88B /* Queryable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1001,7 +1072,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = TestHost/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.parse.TestHost; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1016,7 +1087,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = TestHost/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.parse.TestHost; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Sources/ParseSwift/API/API+Commands.swift b/Sources/ParseSwift/API/API+Commands.swift index a052e82ea..7823619f3 100644 --- a/Sources/ParseSwift/API/API+Commands.swift +++ b/Sources/ParseSwift/API/API+Commands.swift @@ -19,7 +19,7 @@ internal extension API { let params: [String: String?]? internal var data: Data? { - return try? getJSONEncoder().encode(body) + return try? ParseCoding.jsonEncoder().encode(body) } init(method: API.Method, @@ -94,7 +94,7 @@ internal extension API { internal extension API.Command { // MARK: Saving - static func saveCommand(_ object: T) -> API.Command where T: ObjectType { + static func saveCommand(_ object: T) -> API.Command where T: ParseObject { if object.isSaved { return updateCommand(object) } @@ -102,9 +102,9 @@ internal extension API.Command { } // MARK: Saving - private - private static func createCommand(_ object: T) -> API.Command where T: ObjectType { + private static func createCommand(_ object: T) -> API.Command where T: ParseObject { let mapper = { (data) -> T in - try getDecoder().decode(SaveResponse.self, from: data).apply(to: object) + try ParseCoding.jsonDecoder().decode(SaveResponse.self, from: data).apply(to: object) } return API.Command(method: .POST, path: object.endpoint, @@ -112,9 +112,9 @@ internal extension API.Command { mapper: mapper) } - private static func updateCommand(_ object: T) -> API.Command where T: ObjectType { + private static func updateCommand(_ object: T) -> API.Command where T: ParseObject { let mapper = { (data: Data) -> T in - try getDecoder().decode(UpdateResponse.self, from: data).apply(to: object) + try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: data).apply(to: object) } return API.Command(method: .PUT, path: object.endpoint, @@ -123,18 +123,21 @@ internal extension API.Command { } // MARK: Fetching - static func fetchCommand(_ object: T) throws -> API.Command where T: ObjectType { + static func fetchCommand(_ object: T) throws -> API.Command where T: ParseObject { guard object.isSaved else { throw ParseError(code: .unknownError, message: "Cannot Fetch an object without id") } - return API.Command(method: .GET, - path: object.endpoint) { (data) -> T in - try getDecoder().decode(FetchResponse.self, from: data).apply(to: object) + + return API.Command( + method: .GET, + path: object.endpoint + ) { (data) -> T in + try ParseCoding.jsonDecoder().decode(FetchResponse.self, from: data).apply(to: object) } } } -extension API.Command where T: ObjectType { +extension API.Command where T: ParseObject { internal var data: Data? { guard let body = body else { return nil } @@ -159,7 +162,7 @@ extension API.Command where T: ObjectType { let mapper = { (data: Data) -> [Result] in let decodingType = [BatchResponseItem].self do { - let responses = try getDecoder().decode(decodingType, from: data) + let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data) return bodies.enumerated().map({ (object) -> (Result) in let response = responses[object.offset] if let success = response.success { diff --git a/Sources/ParseSwift/API/API.swift b/Sources/ParseSwift/API/API.swift index b559d5901..39c3b696f 100644 --- a/Sources/ParseSwift/API/API.swift +++ b/Sources/ParseSwift/API/API.swift @@ -75,7 +75,7 @@ public struct API { headers["X-Parse-Client-Key"] = clientKey } - if let token = CurrentUserInfo.currentSessionToken { + if let token = BaseParseUser.currentUserContainer?.sessionToken { headers["X-Parse-Session-Token"] = token } diff --git a/Sources/ParseSwift/API/BatchUtils.swift b/Sources/ParseSwift/API/BatchUtils.swift index 4b94a41c4..b6e12d196 100644 --- a/Sources/ParseSwift/API/BatchUtils.swift +++ b/Sources/ParseSwift/API/BatchUtils.swift @@ -8,10 +8,10 @@ import Foundation -typealias ParseObjectBatchCommand = BatchCommand where T: ObjectType +typealias ParseObjectBatchCommand = BatchCommand where T: ParseObject typealias ParseObjectBatchResponse = [(Result)] // swiftlint:disable line_length -typealias RESTBatchCommandType = API.Command, ParseObjectBatchResponse> where T: ObjectType +typealias RESTBatchCommandType = API.Command, ParseObjectBatchResponse> where T: ParseObject // swiftlint:enable line_length internal struct BatchCommand: Encodable where T: Encodable { @@ -49,7 +49,7 @@ internal struct WriteResponse: Codable { return FetchResponse(createdAt: createdAt, updatedAt: updatedAt) } - func apply(to object: T, method: API.Method) -> T where T: ObjectType { + func apply(to object: T, method: API.Method) -> T where T: ParseObject { switch method { case .POST: return asSaveResponse().apply(to: object) diff --git a/Sources/ParseSwift/API/Responses.swift b/Sources/ParseSwift/API/Responses.swift index e0e979b3d..a88a0ffa4 100644 --- a/Sources/ParseSwift/API/Responses.swift +++ b/Sources/ParseSwift/API/Responses.swift @@ -15,7 +15,7 @@ internal struct SaveResponse: Decodable { return createdAt } - func apply(to object: T) -> T where T: ObjectType { + func apply(to object: T) -> T where T: ParseObject { var object = object object.objectId = objectId object.createdAt = createdAt @@ -27,7 +27,7 @@ internal struct SaveResponse: Decodable { internal struct UpdateResponse: Decodable { var updatedAt: Date - func apply(to object: T) -> T where T: ObjectType { + func apply(to object: T) -> T where T: ParseObject { var object = object object.updatedAt = updatedAt return object @@ -38,7 +38,7 @@ internal struct FetchResponse: Decodable { var createdAt: Date var updatedAt: Date - func apply(to object: T) -> T where T: ObjectType { + func apply(to object: T) -> T where T: ParseObject { var object = object object.createdAt = createdAt object.updatedAt = updatedAt diff --git a/Sources/ParseSwift/API/URLSession+extensions.swift b/Sources/ParseSwift/API/URLSession+extensions.swift index 06b5ee8ee..a46434cf2 100755 --- a/Sources/ParseSwift/API/URLSession+extensions.swift +++ b/Sources/ParseSwift/API/URLSession+extensions.swift @@ -23,7 +23,7 @@ extension URLSession { do { return try .success(mapper(responseData)) } catch { - let parseError = try? getDecoder().decode(ParseError.self, from: responseData) + let parseError = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: responseData) return .failure(parseError ?? ParseError(code: .unknownError, message: "cannot decode error")) } } else if let responseError = responseError { diff --git a/Sources/ParseSwift/AnyCodable/AnyCodable.swift b/Sources/ParseSwift/Coding/AnyCodable.swift similarity index 90% rename from Sources/ParseSwift/AnyCodable/AnyCodable.swift rename to Sources/ParseSwift/Coding/AnyCodable.swift index e73a93c14..d631cf700 100755 --- a/Sources/ParseSwift/AnyCodable/AnyCodable.swift +++ b/Sources/ParseSwift/Coding/AnyCodable.swift @@ -16,8 +16,18 @@ import Foundation Source: https://github.com/Flight-School/AnyCodable */ public struct AnyCodable: Codable { + public typealias DateEncodingStrategy = (Date, Encoder) throws -> Void + + public let dateEncodingStrategy: DateEncodingStrategy? public let value: Any + public init(_ value: T?) { + self.dateEncodingStrategy = nil + self.value = value ?? () + } + + public init(_ value: T?, dateEncodingStrategy: DateEncodingStrategy?) { + self.dateEncodingStrategy = dateEncodingStrategy self.value = value ?? () } } diff --git a/Sources/ParseSwift/AnyCodable/AnyDecodable.swift b/Sources/ParseSwift/Coding/AnyDecodable.swift similarity index 99% rename from Sources/ParseSwift/AnyCodable/AnyDecodable.swift rename to Sources/ParseSwift/Coding/AnyDecodable.swift index 50a4df79d..8c3990c83 100755 --- a/Sources/ParseSwift/AnyCodable/AnyDecodable.swift +++ b/Sources/ParseSwift/Coding/AnyDecodable.swift @@ -45,22 +45,23 @@ extension AnyDecodable: _AnyDecodable {} extension _AnyDecodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() + if container.decodeNil() { self.init(()) - } else if let bool = try? container.decode(Bool.self) { - self.init(bool) + } else if let dictionary = try? container.decode([String: AnyCodable].self) { + self.init(dictionary.mapValues { $0.value }) + } else if let array = try? container.decode([AnyCodable].self) { + self.init(array.map { $0.value }) + } else if let string = try? container.decode(String.self) { + self.init(string) } else if let int = try? container.decode(Int.self) { self.init(int) } else if let uint = try? container.decode(UInt.self) { self.init(uint) } else if let double = try? container.decode(Double.self) { self.init(double) - } else if let string = try? container.decode(String.self) { - self.init(string) - } else if let array = try? container.decode([AnyCodable].self) { - self.init(array.map { $0.value }) - } else if let dictionary = try? container.decode([String: AnyCodable].self) { - self.init(dictionary.mapValues { $0.value }) + } else if let bool = try? container.decode(Bool.self) { + self.init(bool) } else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyCodable value cannot be decoded") diff --git a/Sources/ParseSwift/AnyCodable/AnyEncodable.swift b/Sources/ParseSwift/Coding/AnyEncodable.swift similarity index 90% rename from Sources/ParseSwift/AnyCodable/AnyEncodable.swift rename to Sources/ParseSwift/Coding/AnyEncodable.swift index 6c373bab5..e9489be44 100755 --- a/Sources/ParseSwift/AnyCodable/AnyEncodable.swift +++ b/Sources/ParseSwift/Coding/AnyEncodable.swift @@ -29,14 +29,24 @@ import Foundation Source: https://github.com/Flight-School/AnyCodable */ public struct AnyEncodable: Encodable { + public let dateEncodingStrategy: AnyCodable.DateEncodingStrategy? public let value: Any + + public init(_ value: T?, dateEncodingStrategy: AnyCodable.DateEncodingStrategy?) { + self.dateEncodingStrategy = dateEncodingStrategy + self.value = value ?? () + } + public init(_ value: T?) { + self.dateEncodingStrategy = nil self.value = value ?? () } } @usableFromInline protocol _AnyEncodable { + var dateEncodingStrategy: AnyCodable.DateEncodingStrategy? { get } + var value: Any { get } init(_ value: T?) } @@ -46,13 +56,25 @@ extension AnyEncodable: _AnyEncodable {} // MARK: - Encodable extension _AnyEncodable { - public func encode(to encoder: Encoder) throws { // swiftlint:disable:this cyclomatic_complexity function_body_length line_length + // swiftlint:disable:next cyclomatic_complexity function_body_length + public func encode(to encoder: Encoder) throws { + if let date = self.value as? Date, let strategy = dateEncodingStrategy { + try strategy(date, encoder) + return + } + var container = encoder.singleValueContainer() switch self.value { - case is Void: - try container.encodeNil() - case let bool as Bool: - try container.encode(bool) + case let dictionary as [String: Any?]: + try container.encode(dictionary.mapValues { AnyCodable($0, dateEncodingStrategy: dateEncodingStrategy) }) + case let array as [Any?]: + try container.encode(array.map { AnyCodable($0, dateEncodingStrategy: dateEncodingStrategy) }) + case let url as URL: + try container.encode(url) + case let string as String: + try container.encode(string) + case let date as Date: + try container.encode(date) case let int as Int: try container.encode(int) case let int8 as Int8: @@ -77,16 +99,10 @@ extension _AnyEncodable { try container.encode(float) case let double as Double: try container.encode(double) - case let string as String: - try container.encode(string) - case let date as Date: - try container.encode(date) - case let url as URL: - try container.encode(url) - case let array as [Any?]: - try container.encode(array.map { AnyCodable($0) }) - case let dictionary as [String: Any?]: - try container.encode(dictionary.mapValues { AnyCodable($0) }) + case let bool as Bool: + try container.encode(bool) + case is Void: + try container.encodeNil() default: let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyCodable value cannot be encoded") diff --git a/Sources/ParseSwift/Coding/Extensions.swift b/Sources/ParseSwift/Coding/Extensions.swift new file mode 100644 index 000000000..3df6ead68 --- /dev/null +++ b/Sources/ParseSwift/Coding/Extensions.swift @@ -0,0 +1,41 @@ +// +// Extensions.swift +// +// +// Created by Pranjal Satija on 7/19/20. +// + +import Foundation + +// MARK: Date +internal extension Date { + func parseFormatted() -> String { + return ParseCoding.dateFormatter.string(from: self) + } + + var parseRepresentation: [String: String] { + return ["__type": "Date", "iso": parseFormatted()] + } +} + +// MARK: JSONEncoder +extension JSONEncoder { + func encodeAsString(_ value: T) throws -> String where T: Encodable { + guard let string = String(data: try encode(value), encoding: .utf8) else { + throw ParseError(code: .unknownError, message: "Unable to encode object...") + } + + return string + } +} + +// MARK: ParseObject +internal extension ParseObject { + func getEncoder(skipKeys: Bool = true) -> ParseEncoder { + return ParseCoding.parseEncoder(skipKeys: skipKeys) + } + + func getTestDecoder() -> JSONDecoder { + ParseCoding.jsonDecoder() + } +} diff --git a/Sources/ParseSwift/Coding/ParseCoding.swift b/Sources/ParseSwift/Coding/ParseCoding.swift new file mode 100644 index 000000000..773f856cd --- /dev/null +++ b/Sources/ParseSwift/Coding/ParseCoding.swift @@ -0,0 +1,90 @@ +// +// ParseCoding.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2017 Parse. All rights reserved. +// + +import Foundation + +// MARK: ParseCoding +internal enum ParseCoding {} + +// MARK: Coders +extension ParseCoding { + private static let forbiddenKeys = Set(["createdAt", "updatedAt", "objectId", "className"]) + + static func jsonEncoder() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = jsonDateEncodingStrategy + return encoder + } + + static func jsonDecoder() -> JSONDecoder { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = dateDecodingStrategy + return decoder + } + + static func parseEncoder(skipKeys: Bool = true) -> ParseEncoder { + ParseEncoder( + dateEncodingStrategy: parseDateEncodingStrategy, + skippingKeys: skipKeys ? forbiddenKeys : [] + ) + } +} + +// MARK: Dates +extension ParseCoding { + enum DateEncodingKeys: String, CodingKey { + case iso + case type = "__type" + } + + static let dateFormatter: DateFormatter = { + var dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "") + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + return dateFormatter + }() + + static let jsonDateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .custom(parseDateEncodingStrategy) + + static let parseDateEncodingStrategy: AnyCodable.DateEncodingStrategy = { (date, encoder) in + var container = encoder.container(keyedBy: DateEncodingKeys.self) + try container.encode("Date", forKey: .type) + let dateString = dateFormatter.string(from: date) + try container.encode(dateString, forKey: .iso) + } + + static let dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .custom({ (decoder) -> Date in + do { + let container = try decoder.singleValueContainer() + let decodedString = try container.decode(String.self) + + if let date = dateFormatter.date(from: decodedString) { + return date + } else { + throw ParseError( + code: .unknownError, + message: "An invalid date string was provided when decoding dates." + ) + } + } catch let error { + let container = try decoder.container(keyedBy: DateEncodingKeys.self) + + if + let decoded = try container.decodeIfPresent(String.self, forKey: .iso), + let date = dateFormatter.date(from: decoded) + { + return date + } else { + throw ParseError( + code: .unknownError, + message: "An invalid date string was provided when decoding dates." + ) + } + } + }) +} diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift new file mode 100644 index 000000000..8338128f4 --- /dev/null +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -0,0 +1,263 @@ +// +// ParseEncoder.swift +// ParseSwift +// +// Created by Pranjal Satija on 7/20/20. +// Copyright © 2020 Parse. All rights reserved. +// + +// This rule doesn't allow types with underscores in their names. +// swiftlint:disable type_name + +import Foundation + +// MARK: ParseEncoder +internal struct ParseEncoder { + let dateEncodingStrategy: AnyCodable.DateEncodingStrategy? + let jsonEncoder: JSONEncoder + let skippedKeys: Set + + init( + dateEncodingStrategy: AnyCodable.DateEncodingStrategy? = nil, + jsonEncoder: JSONEncoder = JSONEncoder(), + skippingKeys: Set = [] + ) { + self.dateEncodingStrategy = dateEncodingStrategy + self.jsonEncoder = jsonEncoder + self.skippedKeys = skippingKeys + } + + func encodeToDictionary(_ value: T) throws -> [AnyHashable: Any] { + let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: skippedKeys) + try value.encode(to: encoder) + + // The encoder always uses an `NSDictionary` with string keys. + // swiftlint:disable:next force_cast + return encoder.dictionary as! [AnyHashable: Any] + } + + func encode(_ value: T) throws -> Data { + let dictionary = try encodeToDictionary(value) + return try jsonEncoder.encode(AnyCodable(dictionary, dateEncodingStrategy: dateEncodingStrategy)) + } + + func encode(_ array: [T]) throws -> Data { + let dictionaries = try array.map { try encodeToDictionary($0) } + return try jsonEncoder.encode(AnyCodable(dictionaries, dateEncodingStrategy: dateEncodingStrategy)) + } +} + +// MARK: _ParseEncoder +internal struct _ParseEncoder: Encoder { + let codingPath: [CodingKey] + let dictionary: NSMutableDictionary + let skippedKeys: Set + let userInfo: [CodingUserInfoKey: Any] = [:] + + init(codingPath: [CodingKey], dictionary: NSMutableDictionary, skippingKeys: Set) { + self.codingPath = codingPath + self.dictionary = dictionary + self.skippedKeys = skippingKeys + } + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key: CodingKey { + let container = _ParseEncoderKeyedEncodingContainer( + codingPath: codingPath, + dictionary: dictionary, + skippingKeys: skippedKeys + ) + + return KeyedEncodingContainer(container) + } + + func singleValueContainer() -> SingleValueEncodingContainer { + _ParseEncoderSingleValueEncodingContainer( + codingPath: codingPath, + dictionary: dictionary, + skippingKeys: skippedKeys + ) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + _ParseEncoderUnkeyedEncodingContainer( + codingPath: codingPath, + dictionary: dictionary, + skippingKeys: skippedKeys + ) + } + + static func encode( + _ value: T, + with codingPath: [CodingKey], + skippingKeys skippedKeys: Set + ) throws -> Any { + switch value { + case is Bool, is Int, is Int8, is Int16, is Int32, is Int64, is UInt, is UInt8, is UInt16, is UInt32, is UInt64, + is Float, is Double, is String, is Date: + return value + default: + let dictionary = NSMutableDictionary() + let encoder = _ParseEncoder(codingPath: codingPath, dictionary: dictionary, skippingKeys: skippedKeys) + try value.encode(to: encoder) + + return codingPath.last.map { dictionary[$0.stringValue] ?? dictionary } ?? dictionary + } + } +} + +// MARK: _ParseEncoderKeyedEncodingContainer +internal struct _ParseEncoderKeyedEncodingContainer: KeyedEncodingContainerProtocol { + let codingPath: [CodingKey] + let dictionary: NSMutableDictionary + let skippedKeys: Set + + init(codingPath: [CodingKey], dictionary: NSMutableDictionary, skippingKeys: Set) { + self.codingPath = codingPath + self.dictionary = dictionary + self.skippedKeys = skippingKeys + } + + mutating func encodeNil(forKey key: Key) throws { + if skippedKeys.contains(key.stringValue) { return } + + dictionary[key.stringValue] = nil + } + + mutating func encode(_ value: T, forKey key: Key) throws where T: Encodable { + if skippedKeys.contains(key.stringValue) { return } + + dictionary[key.stringValue] = try _ParseEncoder.encode( + value, + with: codingPath + [key], + skippingKeys: skippedKeys + ) + } + + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type, + forKey key: Key + ) -> KeyedEncodingContainer where NestedKey: CodingKey { + let container = _ParseEncoderKeyedEncodingContainer( + codingPath: codingPath + [key], + dictionary: dictionary, + skippingKeys: skippedKeys + ) + + return KeyedEncodingContainer(container) + } + + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + _ParseEncoderUnkeyedEncodingContainer( + codingPath: codingPath + [key], + dictionary: dictionary, + skippingKeys: skippedKeys + ) + } + + mutating func superEncoder() -> Encoder { + fatalError("You can't encode supertypes yet.") + } + + mutating func superEncoder(forKey key: Key) -> Encoder { + fatalError("You can't encode supertypes yet.") + } +} + +// MARK: _ParseEncoderSingleValueEncodingContainer +internal struct _ParseEncoderSingleValueEncodingContainer: SingleValueEncodingContainer { + let codingPath: [CodingKey] + let dictionary: NSMutableDictionary + let skippedKeys: Set + + init(codingPath: [CodingKey], dictionary: NSMutableDictionary, skippingKeys: Set) { + self.codingPath = codingPath + self.dictionary = dictionary + self.skippedKeys = skippingKeys + } + + var key: String { + codingPath.last?.stringValue ?? "" + } + + mutating func encodeNil() throws { + dictionary[key] = nil + } + + mutating func encode(_ value: T) throws where T: Encodable { + dictionary[key] = try _ParseEncoder.encode(value, with: codingPath, skippingKeys: skippedKeys) + } +} + +// MARK: _ParseEncoderUnkeyedEncodingContainer +internal struct _ParseEncoderUnkeyedEncodingContainer: UnkeyedEncodingContainer { + let codingPath: [CodingKey] + let dictionary: NSMutableDictionary + let skippedKeys: Set + + var array: NSMutableArray { + get { + guard let array = dictionary[key] as? NSMutableArray else { + fatalError("There's no array available for unkeyed encoding.") + } + + return array + } + + set { dictionary[key] = newValue } + } + + var count: Int { + array.count + } + + var key: String { + codingPath.last?.stringValue ?? "" + } + + init(codingPath: [CodingKey], dictionary: NSMutableDictionary, skippingKeys: Set) { + self.codingPath = codingPath + self.dictionary = dictionary + self.skippedKeys = skippingKeys + + self.array = NSMutableArray() + } + + mutating func encodeNil() throws { + array.add(NSNull()) + } + + mutating func encode(_ value: T) throws where T: Encodable { + let encoded = try _ParseEncoder.encode(value, with: codingPath, skippingKeys: skippedKeys) + array.add(encoded) + } + + mutating func nestedContainer( + keyedBy keyType: NestedKey.Type + ) -> KeyedEncodingContainer where NestedKey: CodingKey { + let dictionary = NSMutableDictionary() + array.add(dictionary) + + let container = _ParseEncoderKeyedEncodingContainer( + codingPath: codingPath, + dictionary: dictionary, + skippingKeys: skippedKeys + ) + + return KeyedEncodingContainer(container) + } + + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + let dictionary = NSMutableDictionary() + array.add(dictionary) + + return _ParseEncoderUnkeyedEncodingContainer( + codingPath: codingPath, + dictionary: dictionary, + skippingKeys: skippedKeys + ) + } + + mutating func superEncoder() -> Encoder { + fatalError("You can't encode supertypes yet.") + } +} diff --git a/Sources/ParseSwift/Encoder/ParseEncoder.swift b/Sources/ParseSwift/Encoder/ParseEncoder.swift deleted file mode 100644 index 9a17fe778..000000000 --- a/Sources/ParseSwift/Encoder/ParseEncoder.swift +++ /dev/null @@ -1,863 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// JSON Encoder -//===----------------------------------------------------------------------===// - -/// `JSONEncoder` facilitates the encoding of `Encodable` values into JSON. -/// `ParseEncoder` facilitates the encoding of `ObjectType` values into JSON. - -/// All Credit to Apple, this is a simple encoder with capability of skipping keys at runtime. -import Foundation - -// swiftlint:disable colon -// swiftlint:disable force_cast -// swiftlint:disable line_length -// swiftlint:disable return_arrow_whitespace -// swiftlint:disable trailing_semicolon -// swiftlint:disable file_length -// swiftlint:disable private_over_fileprivate -// swiftlint:disable type_name -// swiftlint:disable identifier_name - -open class ParseEncoder { - // MARK: Options - - /// The formatting of the output JSON data. - public struct OutputFormatting : OptionSet { - /// The format's default value. - public let rawValue: UInt - - /// Creates an OutputFormatting value with the given raw value. - public init(rawValue: UInt) { - self.rawValue = rawValue - } - - /// Produce human-readable JSON with indented output. - public static let prettyPrinted = OutputFormatting(rawValue: 1 << 0) - - /// Produce JSON with dictionary keys sorted in lexicographic order. - @available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) - public static let sortedKeys = OutputFormatting(rawValue: 1 << 1) - } - - /// The strategy to use for encoding `Date` values. - public enum DateEncodingStrategy { - /// Defer to `Date` for choosing an encoding. This is the default strategy. - case deferredToDate - - /// Encode the `Date` as a UNIX timestamp (as a JSON number). - case secondsSince1970 - - /// Encode the `Date` as UNIX millisecond timestamp (as a JSON number). - case millisecondsSince1970 - - /// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). - @available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - case iso8601 - - /// Encode the `Date` as a string formatted by the given formatter. - case formatted(DateFormatter) - - /// Encode the `Date` as a custom value encoded by the given closure. - /// - /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place. - case custom((Date, Encoder) throws -> Void) - } - - /// The strategy to use for encoding `Data` values. - public enum DataEncodingStrategy { - /// Defer to `Data` for choosing an encoding. - case deferredToData - - /// Encoded the `Data` as a Base64-encoded string. This is the default strategy. - case base64 - - /// Encode the `Data` as a custom value encoded by the given closure. - /// - /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place. - case custom((Data, Encoder) throws -> Void) - } - - /// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN). - public enum NonConformingFloatEncodingStrategy { - /// Throw upon encountering non-conforming values. This is the default strategy. - case `throw` - - /// Encode the values using the given representation strings. - case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String) - } - - /// The output format to produce. Defaults to `[]`. - open var outputFormatting: OutputFormatting = [] - - /// The strategy to use in encoding dates. Defaults to `.deferredToDate`. - open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate - - /// The strategy to use in encoding binary data. Defaults to `.base64`. - open var dataEncodingStrategy: DataEncodingStrategy = .base64 - - /// The strategy to use in encoding non-conforming numbers. Defaults to `.throw`. - open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy = .throw - - /// Contextual user-provided information for use during encoding. - open var userInfo: [CodingUserInfoKey : Any] = [:] - - open var shouldEncodeKey: ((String, [CodingKey?]) -> Bool) = { (_, _) in true } - /// Options set on the top-level encoder to pass down the encoding hierarchy. - internal struct _Options { - let dateEncodingStrategy: DateEncodingStrategy - let dataEncodingStrategy: DataEncodingStrategy - let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy - let userInfo: [CodingUserInfoKey : Any] - let shouldEncodeKey: ((String, [CodingKey?]) -> Bool) - } - - /// The options set on the top-level encoder. - fileprivate var options: _Options { - return _Options(dateEncodingStrategy: dateEncodingStrategy, - dataEncodingStrategy: dataEncodingStrategy, - nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy, - userInfo: userInfo, - shouldEncodeKey: shouldEncodeKey) - } - - // MARK: - Constructing a JSON Encoder - - /// Initializes `self` with default strategies. - public init() {} - - // MARK: - Encoding Values - - /// Encodes the given top-level value and returns its JSON representation. - /// - /// - parameter value: The value to encode. - /// - returns: A new `Data` value containing the encoded JSON data. - /// - throws: `EncodingError.invalidValue` if a non-comforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`. - /// - throws: An error if any value throws an error during encoding. - open func encode(_ value: T) throws -> Data { - let encoder = _JSONEncoder(options: self.options) - try value.encode(to: encoder) - - guard encoder.storage.count > 0 else { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) did not encode any values.")) - } - - let topLevel = encoder.storage.popContainer() - if topLevel is NSNull { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as null JSON fragment.")) - } else if topLevel is NSNumber { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as number JSON fragment.")) - } else if topLevel is NSString { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Top-level \(T.self) encoded as string JSON fragment.")) - } - - let writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue) - do { - return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions) - } catch { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: [], debugDescription: "Unable to encode the given top-level value to JSON.")) - } - } -} - -// MARK: - _JSONEncoder - -internal class _JSONEncoder : Encoder { - // MARK: Properties - - /// The encoder's storage. - fileprivate var storage: _JSONEncodingStorage - - /// Options set on the top-level encoder. - internal let options: ParseEncoder._Options - - /// The path to the current point in encoding. - public var codingPath: [CodingKey] - - /// Contextual user-provided information for use during encoding. - public var userInfo: [CodingUserInfoKey : Any] { - return self.options.userInfo - } - - // MARK: - Initialization - - /// Initializes `self` with the given top-level encoder options. - fileprivate init(options: ParseEncoder._Options, codingPath: [CodingKey] = []) { - self.options = options - self.storage = _JSONEncodingStorage() - self.codingPath = codingPath - } - - /// Returns whether a new element can be encoded at this coding path. - /// - /// `true` if an element has not yet been encoded at this coding path; `false` otherwise. - fileprivate var canEncodeNewValue: Bool { - // Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container). - // At the same time, every time a container is requested, a new value gets pushed onto the storage stack. - // If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition. - // - // This means that anytime something that can request a new container goes onto the stack, we MUST push a key onto the coding path. - // Things which will not request containers do not need to have the coding path extended for them (but it doesn't matter if it is, because they will not reach here). - return self.storage.count == self.codingPath.count - } - - // MARK: - Encoder Methods - public func container(keyedBy: Key.Type) -> KeyedEncodingContainer { - // If an existing keyed container was already requested, return that one. - let topContainer: NSMutableDictionary - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushKeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableDictionary else { - preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - let container = _JSONKeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - return KeyedEncodingContainer(container) - } - - public func unkeyedContainer() -> UnkeyedEncodingContainer { - // If an existing unkeyed container was already requested, return that one. - let topContainer: NSMutableArray - if self.canEncodeNewValue { - // We haven't yet pushed a container at this level; do so here. - topContainer = self.storage.pushUnkeyedContainer() - } else { - guard let container = self.storage.containers.last as? NSMutableArray else { - preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.") - } - - topContainer = container - } - - return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer) - } - - public func singleValueContainer() -> SingleValueEncodingContainer { - return self - } -} - -// MARK: - Encoding Storage and Containers - -fileprivate struct _JSONEncodingStorage { - // MARK: Properties - - /// The container stack. - /// Elements may be any one of the JSON types (NSNull, NSNumber, NSString, NSArray, NSDictionary). - private(set) fileprivate var containers: [NSObject] = [] - - // MARK: - Initialization - - /// Initializes `self` with no containers. - fileprivate init() {} - - // MARK: - Modifying the Stack - - fileprivate var count: Int { - return self.containers.count - } - - fileprivate mutating func pushKeyedContainer() -> NSMutableDictionary { - let dictionary = NSMutableDictionary() - self.containers.append(dictionary) - return dictionary - } - - fileprivate mutating func pushUnkeyedContainer() -> NSMutableArray { - let array = NSMutableArray() - self.containers.append(array) - return array - } - - fileprivate mutating func push(container: NSObject) { - self.containers.append(container) - } - - fileprivate mutating func popContainer() -> NSObject { - precondition(self.containers.count > 0, "Empty container stack.") - return self.containers.popLast()! - } -} - -// MARK: - Encoding Containers - -fileprivate struct _JSONKeyedEncodingContainer : KeyedEncodingContainerProtocol { - typealias Key = K - - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: _JSONEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableDictionary - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableDictionary) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - private func shouldEncode(key: Key) -> Bool { - return encoder.options.shouldEncodeKey(key.stringValue, self.codingPath) - } - - private func shouldSkip(_ key: Key) -> Bool { - return !shouldEncode(key: key) - } - - // MARK: - KeyedEncodingContainerProtocol Methods - - public mutating func encodeNil(forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = NSNull() } - public mutating func encode(_ value: Bool, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int8, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int16, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int32, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: Int64, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt8, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt16, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt32, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: UInt64, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - public mutating func encode(_ value: String, forKey key: Key) throws { if shouldSkip(key) { return }; self.container[key.stringValue] = self.encoder.box(value) } - - public mutating func encode(_ value: Float, forKey key: Key) throws { - if shouldSkip(key) { return }; - // Since the float may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[key.stringValue] = try self.encoder.box(value) - } - - public mutating func encode(_ value: Double, forKey key: Key) throws { - if shouldSkip(key) { return }; - // Since the double may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[key.stringValue] = try self.encoder.box(value) - } - - public mutating func encode(_ value: T, forKey key: Key) throws { - if shouldSkip(key) { return }; - self.encoder.codingPath.append(key) - defer { self.encoder.codingPath.removeLast() } - self.container[key.stringValue] = try self.encoder.box(value) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { - let dictionary = NSMutableDictionary() - self.container[key.stringValue] = dictionary - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - - let container = _JSONKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - let array = NSMutableArray() - self.container[key.stringValue] = array - - self.codingPath.append(key) - defer { self.codingPath.removeLast() } - return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return _JSONReferencingEncoder(referencing: self.encoder, at: _JSONKey.super, wrapping: self.container) - } - - public mutating func superEncoder(forKey key: Key) -> Encoder { - return _JSONReferencingEncoder(referencing: self.encoder, at: key, wrapping: self.container) - } -} - -fileprivate struct _JSONUnkeyedEncodingContainer : UnkeyedEncodingContainer { - // MARK: Properties - - /// A reference to the encoder we're writing to. - private let encoder: _JSONEncoder - - /// A reference to the container we're writing to. - private let container: NSMutableArray - - /// The path of coding keys taken to get to this point in encoding. - private(set) public var codingPath: [CodingKey] - - /// The number of elements encoded into the container. - public var count: Int { - return self.container.count - } - - // MARK: - Initialization - - /// Initializes `self` with the given references. - fileprivate init(referencing encoder: _JSONEncoder, codingPath: [CodingKey], wrapping container: NSMutableArray) { - self.encoder = encoder - self.codingPath = codingPath - self.container = container - } - - // MARK: - UnkeyedEncodingContainer Methods - - public mutating func encodeNil() throws { self.container.add(NSNull()) } - public mutating func encode(_ value: Bool) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: Int64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt8) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt16) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt32) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: UInt64) throws { self.container.add(self.encoder.box(value)) } - public mutating func encode(_ value: String) throws { self.container.add(self.encoder.box(value)) } - - public mutating func encode(_ value: Float) throws { - // Since the float may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func encode(_ value: Double) throws { - // Since the double may be invalid and throw, the coding path needs to contain this key. - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func encode(_ value: T) throws { - self.encoder.codingPath.append(_JSONKey(index: self.count)) - defer { self.encoder.codingPath.removeLast() } - self.container.add(try self.encoder.box(value)) - } - - public mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - self.codingPath.append(_JSONKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let dictionary = NSMutableDictionary() - self.container.add(dictionary) - - let container = _JSONKeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: dictionary) - return KeyedEncodingContainer(container) - } - - public mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - self.codingPath.append(_JSONKey(index: self.count)) - defer { self.codingPath.removeLast() } - - let array = NSMutableArray() - self.container.add(array) - return _JSONUnkeyedEncodingContainer(referencing: self.encoder, codingPath: self.codingPath, wrapping: array) - } - - public mutating func superEncoder() -> Encoder { - return _JSONReferencingEncoder(referencing: self.encoder, at: self.container.count, wrapping: self.container) - } -} - -extension _JSONEncoder : SingleValueEncodingContainer { - // MARK: - SingleValueEncodingContainer Methods - - fileprivate func assertCanEncodeNewValue() { - precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.") - } - - public func encodeNil() throws { - assertCanEncodeNewValue() - self.storage.push(container: NSNull()) - } - - public func encode(_ value: Bool) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Int) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Int8) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Int16) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Int32) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Int64) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: UInt) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: UInt8) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: UInt16) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: UInt32) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: UInt64) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: String) throws { - assertCanEncodeNewValue() - self.storage.push(container: box(value)) - } - - public func encode(_ value: Float) throws { - assertCanEncodeNewValue() - try self.storage.push(container: box(value)) - } - - public func encode(_ value: Double) throws { - assertCanEncodeNewValue() - try self.storage.push(container: box(value)) - } - - public func encode(_ value: T) throws { - assertCanEncodeNewValue() - try self.storage.push(container: box(value)) - } -} - -// MARK: - Concrete Value Representations - -extension _JSONEncoder { - /// Returns the given value boxed in a container appropriate for pushing onto the container stack. - fileprivate func box(_ value: Bool) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: Int64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt8) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt16) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt32) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: UInt64) -> NSObject { return NSNumber(value: value) } - fileprivate func box(_ value: String) -> NSObject { return NSString(string: value) } - - fileprivate func box(_ float: Float) throws -> NSObject { - guard !float.isInfinite && !float.isNaN else { - guard case let .convertToString(positiveInfinity: posInfString, - negativeInfinity: negInfString, - nan: nanString) = self.options.nonConformingFloatEncodingStrategy else { - throw EncodingError._invalidFloatingPointValue(float, at: codingPath) - } - - if float == Float.infinity { - return NSString(string: posInfString) - } else if float == -Float.infinity { - return NSString(string: negInfString) - } else { - return NSString(string: nanString) - } - } - - return NSNumber(value: float) - } - - fileprivate func box(_ double: Double) throws -> NSObject { - guard !double.isInfinite && !double.isNaN else { - guard case let .convertToString(positiveInfinity: posInfString, - negativeInfinity: negInfString, - nan: nanString) = self.options.nonConformingFloatEncodingStrategy else { - throw EncodingError._invalidFloatingPointValue(double, at: codingPath) - } - - if double == Double.infinity { - return NSString(string: posInfString) - } else if double == -Double.infinity { - return NSString(string: negInfString) - } else { - return NSString(string: nanString) - } - } - - return NSNumber(value: double) - } - - fileprivate func box(_ date: Date) throws -> NSObject { - switch self.options.dateEncodingStrategy { - case .deferredToDate: - // Must be called with a surrounding with(pushedKey:) call. - try date.encode(to: self) - return self.storage.popContainer() - - case .secondsSince1970: - return NSNumber(value: date.timeIntervalSince1970) - - case .millisecondsSince1970: - return NSNumber(value: 1000.0 * date.timeIntervalSince1970) - - case .iso8601: - if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - return NSString(string: _iso8601Formatter.string(from: date)) - } else { - fatalError("ISO8601DateFormatter is unavailable on this platform.") - } - - case .formatted(let formatter): - return NSString(string: formatter.string(from: date)) - - case .custom(let closure): - let depth = self.storage.count - try closure(date, self) - - guard self.storage.count > depth else { - // The closure didn't encode anything. Return the default keyed container. - return NSDictionary() - } - - // We can pop because the closure encoded something. - return self.storage.popContainer() - } - } - - fileprivate func box(_ data: Data) throws -> NSObject { - switch self.options.dataEncodingStrategy { - case .deferredToData: - // Must be called with a surrounding with(pushedKey:) call. - try data.encode(to: self) - return self.storage.popContainer() - - case .base64: - return NSString(string: data.base64EncodedString()) - - case .custom(let closure): - let depth = self.storage.count - try closure(data, self) - - guard self.storage.count > depth else { - // The closure didn't encode anything. Return the default keyed container. - return NSDictionary() - } - - // We can pop because the closure encoded something. - return self.storage.popContainer() - } - } - - fileprivate func box(_ value: T) throws -> NSObject { - if T.self == Date.self { - // Respect Date encoding strategy - return try self.box((value as! Date)) - } else if T.self == Data.self { - // Respect Data encoding strategy - return try self.box((value as! Data)) - } else if T.self == URL.self { - // Encode URLs as single strings. - return self.box((value as! URL).absoluteString) - } else if T.self == Decimal.self { - // JSONSerialization can natively handle NSDecimalNumber. - return (value as! Decimal) as NSDecimalNumber - } - - // The value should request a container from the _JSONEncoder. - let topContainer = self.storage.containers.last - try value.encode(to: self) - - // The top container should be a new container. - guard self.storage.containers.last! !== topContainer else { - // If the value didn't request a container at all, encode the default container instead. - return NSDictionary() - } - - return self.storage.popContainer() - } -} - -// MARK: - _JSONReferencingEncoder - -/// _JSONReferencingEncoder is a special subclass of _JSONEncoder which has its own storage, but references the contents of a different encoder. -/// It's used in superEncoder(), which returns a new encoder for encoding a superclass -- the lifetime of the encoder should not escape the scope it's created in, but it doesn't necessarily know when it's done being used (to write to the original container). -fileprivate class _JSONReferencingEncoder : _JSONEncoder { - // MARK: Reference types. - - /// The type of container we're referencing. - private enum Reference { - /// Referencing a specific index in an array container. - case array(NSMutableArray, Int) - - /// Referencing a specific key in a dictionary container. - case dictionary(NSMutableDictionary, String) - } - - // MARK: - Properties - - /// The encoder we're referencing. - fileprivate let encoder: _JSONEncoder - - /// The container reference itself. - private let reference: Reference - - // MARK: - Initialization - - /// Initializes `self` by referencing the given array container in the given encoder. - fileprivate init(referencing encoder: _JSONEncoder, at index: Int, wrapping array: NSMutableArray) { - self.encoder = encoder - self.reference = .array(array, index) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(_JSONKey(index: index)) - } - - /// Initializes `self` by referencing the given dictionary container in the given encoder. - fileprivate init(referencing encoder: _JSONEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) { - self.encoder = encoder - self.reference = .dictionary(dictionary, key.stringValue) - super.init(options: encoder.options, codingPath: encoder.codingPath) - - self.codingPath.append(key) - } - - // MARK: - Coding Path Operations - - fileprivate override var canEncodeNewValue: Bool { - // With a regular encoder, the storage and coding path grow together. - // A referencing encoder, however, inherits its parents coding path, as well as the key it was created for. - // We have to take this into account. - return self.storage.count == self.codingPath.count - self.encoder.codingPath.count - 1 - } - - // MARK: - Deinitialization - - // Finalizes `self` by writing the contents of our storage to the referenced encoder's storage. - deinit { - let value: Any - switch self.storage.count { - case 0: value = NSDictionary() - case 1: value = self.storage.popContainer() - default: fatalError("Referencing encoder deallocated with multiple containers on stack.") - } - - switch self.reference { - case .array(let array, let index): - array.insert(value, at: index) - - case .dictionary(let dictionary, let key): - dictionary[NSString(string: key)] = value - } - } -} - -//===----------------------------------------------------------------------===// -// Shared Key Types -//===----------------------------------------------------------------------===// - -fileprivate struct _JSONKey : CodingKey { - public var stringValue: String - public var intValue: Int? - - public init?(stringValue: String) { - self.stringValue = stringValue - self.intValue = nil - } - - public init?(intValue: Int) { - self.stringValue = "\(intValue)" - self.intValue = intValue - } - - fileprivate init(index: Int) { - self.stringValue = "Index \(index)" - self.intValue = index - } - - fileprivate static let `super` = _JSONKey(stringValue: "super")! -} - -//===----------------------------------------------------------------------===// -// Shared ISO8601 Date Formatter -//===----------------------------------------------------------------------===// - -// NOTE: This value is implicitly lazy and _must_ be lazy. We're compiled against the latest SDK (w/ ISO8601DateFormatter), but linked against whichever Foundation the user has. ISO8601DateFormatter might not exist, so we better not hit this code path on an older OS. -@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) -fileprivate var _iso8601Formatter: ISO8601DateFormatter = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = .withInternetDateTime - return formatter -}() - -//===----------------------------------------------------------------------===// -// Error Utilities -//===----------------------------------------------------------------------===// - -fileprivate extension EncodingError { - /// Returns a `.invalidValue` error describing the given invalid floating-point value. - /// - /// - /// - parameter value: The value that was invalid to encode. - /// - parameter path: The path of `CodingKey`s taken to encode this value. - /// - returns: An `EncodingError` with the appropriate path and debug description. - static func _invalidFloatingPointValue(_ value: T, at codingPath: [CodingKey]) -> EncodingError { - let valueDescription: String - if value == T.infinity { - valueDescription = "\(T.self).infinity" - } else if value == -T.infinity { - valueDescription = "-\(T.self).infinity" - } else { - valueDescription = "\(T.self).nan" - } - - let debugDescription = "Unable to encode \(valueDescription) directly in JSON. Use JSONEncoder.NonConformingFloatEncodingStrategy.convertToString to specify how the value should be encoded." - return .invalidValue(value, EncodingError.Context(codingPath: codingPath, debugDescription: debugDescription)) - } -} - -extension DecodingError { - static func _typeMismatch(at: [CodingKey], expectation: Any.Type, reality: Any) -> DecodingError { - return DecodingError.typeMismatch(expectation, Context(codingPath: at, debugDescription: "\(reality)")) - } -} diff --git a/Sources/ParseSwift/MutationOperations/AddOperation.swift b/Sources/ParseSwift/Mutation Operations/AddOperation.swift similarity index 100% rename from Sources/ParseSwift/MutationOperations/AddOperation.swift rename to Sources/ParseSwift/Mutation Operations/AddOperation.swift diff --git a/Sources/ParseSwift/MutationOperations/AddUniqueOperation.swift b/Sources/ParseSwift/Mutation Operations/AddUniqueOperation.swift similarity index 100% rename from Sources/ParseSwift/MutationOperations/AddUniqueOperation.swift rename to Sources/ParseSwift/Mutation Operations/AddUniqueOperation.swift diff --git a/Sources/ParseSwift/MutationOperations/DeleteOperation.swift b/Sources/ParseSwift/Mutation Operations/DeleteOperation.swift similarity index 100% rename from Sources/ParseSwift/MutationOperations/DeleteOperation.swift rename to Sources/ParseSwift/Mutation Operations/DeleteOperation.swift diff --git a/Sources/ParseSwift/MutationOperations/IncrementOperation.swift b/Sources/ParseSwift/Mutation Operations/IncrementOperation.swift similarity index 100% rename from Sources/ParseSwift/MutationOperations/IncrementOperation.swift rename to Sources/ParseSwift/Mutation Operations/IncrementOperation.swift diff --git a/Sources/ParseSwift/MutationOperations/ParseMutationContainer.swift b/Sources/ParseSwift/Mutation Operations/ParseMutationContainer.swift similarity index 98% rename from Sources/ParseSwift/MutationOperations/ParseMutationContainer.swift rename to Sources/ParseSwift/Mutation Operations/ParseMutationContainer.swift index be60e0eab..f02a58d35 100644 --- a/Sources/ParseSwift/MutationOperations/ParseMutationContainer.swift +++ b/Sources/ParseSwift/Mutation Operations/ParseMutationContainer.swift @@ -8,8 +8,9 @@ import Foundation -public struct ParseMutationContainer: Encodable where T: ObjectType { +public struct ParseMutationContainer: Encodable where T: ParseObject { typealias ObjectType = T + var target: T private var operations = [String: Encodable]() diff --git a/Sources/ParseSwift/MutationOperations/RemoveOperation.swift b/Sources/ParseSwift/Mutation Operations/RemoveOperation.swift similarity index 100% rename from Sources/ParseSwift/MutationOperations/RemoveOperation.swift rename to Sources/ParseSwift/Mutation Operations/RemoveOperation.swift diff --git a/Sources/ParseSwift/Objects Protocols/ObjectType+Batch.swift b/Sources/ParseSwift/Objects Protocols/ObjectType+Batch.swift deleted file mode 100644 index 2498bc080..000000000 --- a/Sources/ParseSwift/Objects Protocols/ObjectType+Batch.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// ObjectType+Batch.swift -// ParseSwift -// -// Created by Florent Vilmart on 17-08-20. -// Copyright © 2017 Parse. All rights reserved. -// - -import Foundation - -public extension ObjectType { - static func saveAll(_ objects: Self...) throws -> [(Result)] { - return try objects.saveAll() - } - - static func saveAll(options: API.Options = [], - _ objects: Self..., - completion: @escaping (Result<[(Result)], ParseError>) -> Void) { - objects.saveAll(options: options, completion: completion) - } -} - -extension Sequence where Element: ObjectType { - public func saveAll(options: API.Options = []) throws -> [(Result)] { - let commands = map { $0.saveCommand() } - return try API.Command - .batch(commands: commands) - .execute(options: options) - } - - public func saveAll(options: API.Options = [], callbackQueue: DispatchQueue = .main, - completion: @escaping (Result<[(Result)], ParseError>) -> Void) { - let commands = map { $0.saveCommand() } - API.Command - .batch(commands: commands) - .executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) - } -} diff --git a/Sources/ParseSwift/Objects Protocols/ObjectType+Equatable.swift b/Sources/ParseSwift/Objects Protocols/ObjectType+Equatable.swift deleted file mode 100644 index 776096a42..000000000 --- a/Sources/ParseSwift/Objects Protocols/ObjectType+Equatable.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// ObjectType+Equatable.swift -// ParseSwift -// -// Created by Florent Vilmart on 17-08-20. -// Copyright © 2017 Parse. All rights reserved. -// - -import Foundation - -public func == (lhs: T?, rhs: T?) -> Bool where T: ObjectType { - guard let lhs = lhs, let rhs = rhs else { return false } - return lhs == rhs -} - -public func == (lhs: T, rhs: T) -> Bool where T: ObjectType { - return lhs.className == rhs.className && rhs.objectId == lhs.objectId -} diff --git a/Sources/ParseSwift/Objects Protocols/ObjectType+Query.swift b/Sources/ParseSwift/Objects Protocols/ObjectType+Query.swift deleted file mode 100644 index b25b22270..000000000 --- a/Sources/ParseSwift/Objects Protocols/ObjectType+Query.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Object+Query.swift -// Parse -// -// Created by Florent Vilmart on 17-07-23. -// Copyright © 2020 Parse Community. All rights reserved. -// - -import Foundation - -public extension ObjectType { - static func find() throws -> [Self] { - return try query().find() - } - - static func query() -> Query { - return Query() - } - - static func query(_ constraints: QueryConstraint...) -> Query { - return Query(constraints) - } -} diff --git a/Sources/ParseSwift/Objects Protocols/ObjectType.swift b/Sources/ParseSwift/Objects Protocols/ObjectType.swift deleted file mode 100644 index 36cd365e1..000000000 --- a/Sources/ParseSwift/Objects Protocols/ObjectType.swift +++ /dev/null @@ -1,236 +0,0 @@ -// -// ParseObjectType.swift -// ParseSwift -// -// Created by Florent Vilmart on 17-07-24. -// Copyright © 2020 Parse Community. All rights reserved. -// - -import Foundation - -public struct NoBody: Codable {} - -public protocol Saving: Codable { - associatedtype SavingType - func save(options: API.Options) throws -> SavingType - func save() throws -> SavingType -} - -extension Saving { - public func save() throws -> SavingType { - return try save(options: []) - } -} - -public protocol Fetching: Codable { - associatedtype FetchingType - func fetch(options: API.Options) throws -> FetchingType - func fetch() throws -> FetchingType -} - -extension Fetching { - public func fetch() throws -> FetchingType { - return try fetch(options: []) - } -} - -public protocol ObjectType: Fetching, Saving, CustomDebugStringConvertible, Equatable { - static var className: String { get } - var objectId: String? { get set } - var createdAt: Date? { get set } - var updatedAt: Date? { get set } - var ACL: ACL? { get set } -} - -internal extension ObjectType { - func getEncoder() -> ParseEncoder { - return getParseEncoder() - } - - func getTestDecoder() -> JSONDecoder { - return getDecoder() - } - - func getEncoderWithoutSkippingKeys() -> ParseEncoder { - return getParseEncoderWithoutSkippingKeys() - } -} - -extension ObjectType { - // Parse ClassName inference - public static var className: String { - let classType = "\(type(of: self))" - return classType.components(separatedBy: ".").first! // strip .Type - } - public var className: String { - return Self.className - } -} - -extension ObjectType { - public var debugDescription: String { - guard let descriptionData = try? getJSONEncoder().encode(self), - let descriptionString = String(data: descriptionData, encoding: .utf8) else { - return "\(className) ()" - } - return "\(className) (\(descriptionString))" - } -} - -public extension ObjectType { - func toPointer() -> Pointer { - return Pointer(self) - } -} - -enum DateEncodingKeys: String, CodingKey { - case iso - case type = "__type" -} - -let dateFormatter: DateFormatter = { - var dateFormatter = DateFormatter() - dateFormatter.locale = Locale(identifier: "") - dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" - return dateFormatter -}() - -let parseDateEncodingStrategy: ParseEncoder.DateEncodingStrategy = .custom({ (date, enc) in - var container = enc.container(keyedBy: DateEncodingKeys.self) - try container.encode("Date", forKey: .type) - let dateString = dateFormatter.string(from: date) - try container.encode(dateString, forKey: .iso) -}) - -let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .custom({ (date, enc) in - var container = enc.container(keyedBy: DateEncodingKeys.self) - try container.encode("Date", forKey: .type) - let dateString = dateFormatter.string(from: date) - try container.encode(dateString, forKey: .iso) -}) - -internal extension Date { - func parseFormatted() -> String { - return dateFormatter.string(from: self) - } - var parseRepresentation: [String: String] { - return ["__type": "Date", "iso": parseFormatted()] - } -} - -let dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .custom({ (dec) -> Date in - do { - let container = try dec.singleValueContainer() - let decodedString = try container.decode(String.self) - return dateFormatter.date(from: decodedString)! - } catch let error { - let container = try dec.container(keyedBy: DateEncodingKeys.self) - if let decoded = try container.decodeIfPresent(String.self, forKey: .iso) { - return dateFormatter.date(from: decoded)! - } - } - throw ParseError(code: .unknownError, message: "unable to decode") -}) - -func getJSONEncoder() -> JSONEncoder { - let encoder = JSONEncoder() - encoder.dateEncodingStrategy = dateEncodingStrategy - return encoder -} - -private let forbiddenKeys = ["createdAt", "updatedAt", "objectId", "className"] - -func getParseEncoder() -> ParseEncoder { - return getParseEncoder(skipKeys: true) -} - -func getParseEncoderWithoutSkippingKeys() -> ParseEncoder { - return getParseEncoder(skipKeys: false) -} - -internal func getParseEncoder(skipKeys: Bool) -> ParseEncoder { - let encoder = ParseEncoder() - encoder.dateEncodingStrategy = parseDateEncodingStrategy - encoder.shouldEncodeKey = { (key, path) -> Bool in - if path.count == 0 // top level - && forbiddenKeys.firstIndex(of: key) != nil - && skipKeys { - return false - } - return true - } - return encoder -} - -extension JSONEncoder { - func encodeAsString(_ value: T) throws -> String where T: Encodable { - guard let string = String(data: try encode(value), encoding: .utf8) else { - throw ParseError(code: .unknownError, message: "Unable to encode object...") - } - return string - } -} - -func getDecoder() -> JSONDecoder { - let encoder = JSONDecoder() - encoder.dateDecodingStrategy = dateDecodingStrategy - return encoder -} - -public extension ObjectType { - func save(options: API.Options) throws -> Self { - return try saveCommand().execute(options: options) - } - - func save(options: API.Options, callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - saveCommand().executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) - } - - func fetch(options: API.Options) throws -> Self { - return try fetchCommand().execute(options: options) - } - - func fetch(options: API.Options, callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - do { - try fetchCommand().executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) - } catch let error as ParseError { - completion(.failure(error)) - } catch { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) - } - } - - internal func saveCommand() -> API.Command { - return API.Command.saveCommand(self) - } - - internal func fetchCommand() throws -> API.Command { - return try API.Command.fetchCommand(self) - } -} - -extension ObjectType { - var endpoint: API.Endpoint { - if let objectId = objectId { - return .object(className: className, objectId: objectId) - } - return .objects(className: className) - } - - var isSaved: Bool { - return objectId != nil - } -} - -internal struct FindResult: Codable where T: ObjectType { - let results: [T] - let count: Int? -} - -public extension ObjectType { - var mutationContainer: ParseMutationContainer { - return ParseMutationContainer(target: self) - } -} diff --git a/Sources/ParseSwift/Objects Protocols/UserType.swift b/Sources/ParseSwift/Objects Protocols/UserType.swift deleted file mode 100644 index 4e13bec36..000000000 --- a/Sources/ParseSwift/Objects Protocols/UserType.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// ParseUserType.swift -// ParseSwift -// -// Created by Florent Vilmart. -// Copyright © 2020 Parse Community. All rights reserved. -// - -import Foundation - -internal struct CurrentUserInfo { - static var currentUser: Any? - static var currentSessionToken: String? -} - -public protocol UserType: ObjectType { - var username: String? { get set } - var email: String? { get set } - var password: String? { get set } -} - -public extension UserType { - var sessionToken: String? { - if let currentUser = CurrentUserInfo.currentUser as? Self, - currentUser.objectId != nil && self.objectId != nil && - currentUser.objectId == self.objectId { - return CurrentUserInfo.currentSessionToken - } - return nil - } - - static var className: String { - return "_User" - } -} - -public extension UserType { - static var current: Self? { - return CurrentUserInfo.currentUser as? Self - } - - static func login(username: String, - password: String) throws -> Self { - return try loginCommand(username: username, password: password) - .execute(options: []) - } - - static func login(username: String, password: String, callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - return loginCommand(username: username, password: password) - .executeAsync(options: [], callbackQueue: callbackQueue, completion: completion) - } - - static func signup(username: String, - password: String) throws -> Self { - return try signupCommand(username: username, password: password) - .execute(options: []) - } - - static func signup(username: String, password: String, callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - return signupCommand(username: username, password: password) - .executeAsync(options: [], callbackQueue: callbackQueue, completion: completion) - } - - static func logout() throws { - _ = try logoutCommand() - .execute(options: []) - } - - static func logout(callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { - logoutCommand() - .executeAsync(options: [], callbackQueue: callbackQueue) { result in - completion(result.map { true }) - } - } - - func signup() throws -> Self { - return try signupCommand() - .execute(options: []) - } -} - -private extension UserType { - private static func loginCommand(username: String, - password: String) -> API.Command { - let params = [ - "username": username, - "password": password - ] - return API.Command(method: .GET, - path: .login, - params: params) { (data) -> Self in - let user = try getDecoder().decode(Self.self, from: data) - let response = try getDecoder().decode(LoginSignupResponse.self, from: data) - CurrentUserInfo.currentUser = user - CurrentUserInfo.currentSessionToken = response.sessionToken - return user - } - } - - private static func signupCommand(username: String, - password: String) -> API.Command { - let body = SignupBody(username: username, password: password) - return API.Command(method: .POST, path: .signup, body: body) { (data) -> Self in - let response = try getDecoder().decode(LoginSignupResponse.self, from: data) - var user = try getDecoder().decode(Self.self, from: data) - user.username = username - user.password = password - user.updatedAt = response.updatedAt ?? response.createdAt - - // Set the current user - CurrentUserInfo.currentUser = user - CurrentUserInfo.currentSessionToken = response.sessionToken - return user - } - } - - private func signupCommand() -> API.Command { - var user = self - return API.Command(method: .POST, path: .signup, body: user) { (data) -> Self in - let response = try getDecoder().decode(LoginSignupResponse.self, from: data) - user.updatedAt = response.updatedAt ?? response.createdAt - user.createdAt = response.createdAt - // Set the current user - CurrentUserInfo.currentUser = user - CurrentUserInfo.currentSessionToken = response.sessionToken - return user - } - } - - private static func logoutCommand() -> API.Command { - return API.Command(method: .POST, path: .logout) { (_) -> Void in - CurrentUserInfo.currentUser = nil - CurrentUserInfo.currentSessionToken = nil - } - } -} - -public struct SignupBody: Codable { - let username: String - let password: String -} - -private struct LoginSignupResponse: Codable { - let createdAt: Date - let objectId: String - let sessionToken: String - var updatedAt: Date? -} diff --git a/Sources/ParseSwift/Objects/Fetchable.swift b/Sources/ParseSwift/Objects/Fetchable.swift new file mode 100644 index 000000000..745e73093 --- /dev/null +++ b/Sources/ParseSwift/Objects/Fetchable.swift @@ -0,0 +1,20 @@ +// +// Fetchable.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse. All rights reserved. +// + +public protocol Fetchable: Codable { + associatedtype FetchingType + + func fetch(options: API.Options) throws -> FetchingType + func fetch() throws -> FetchingType +} + +extension Fetchable { + public func fetch() throws -> FetchingType { + return try fetch(options: []) + } +} diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift new file mode 100644 index 000000000..e6d24089f --- /dev/null +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -0,0 +1,155 @@ +// +// ParseObject.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse. All rights reserved. +// + +import Foundation + +// MARK: ParseObject +public protocol ParseObject: Fetchable, Saveable, CustomDebugStringConvertible { + static var className: String { get } + + var objectId: String? { get set } + var createdAt: Date? { get set } + var updatedAt: Date? { get set } + var ACL: ACL? { get set } +} + +// MARK: Default Implementations +extension ParseObject { + public static var className: String { + let classType = "\(type(of: self))" + return classType.components(separatedBy: ".").first! // strip .Type + } + + public var className: String { + return Self.className + } + + public func hasSameObjectId(as other: T) -> Bool { + return other.className == className && other.objectId == objectId && objectId != nil + } +} + +// MARK: Batch Support +public extension Sequence where Element: ParseObject { + func saveAll(options: API.Options = []) throws -> [(Result)] { + let commands = map { $0.saveCommand() } + return try API.Command + .batch(commands: commands) + .execute(options: options) + } + + func saveAll( + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result<[(Result)], ParseError>) -> Void + ) { + let commands = map { $0.saveCommand() } + API.Command + .batch(commands: commands) + .executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) + } +} + +// MARK: Convenience +extension ParseObject { + var endpoint: API.Endpoint { + if let objectId = objectId { + return .object(className: className, objectId: objectId) + } + + return .objects(className: className) + } + + var isSaved: Bool { + return objectId != nil + } +} + +// MARK: CustomDebugStringConvertible +extension ParseObject { + public var debugDescription: String { + guard let descriptionData = try? ParseCoding.jsonEncoder().encode(self), + let descriptionString = String(data: descriptionData, encoding: .utf8) else { + return "\(className) ()" + } + + return "\(className) (\(descriptionString))" + } +} + +// MARK: Fetchable +extension ParseObject { + public func fetch(options: API.Options) throws -> Self { + return try fetchCommand().execute(options: options) + } + + public func fetch( + options: API.Options, + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + do { + try fetchCommand().executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) + } catch let error as ParseError { + completion(.failure(error)) + } catch { + completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + } + } + + internal func fetchCommand() throws -> API.Command { + return try API.Command.fetchCommand(self) + } +} + +// MARK: Mutations +public extension ParseObject { + var mutationContainer: ParseMutationContainer { + return ParseMutationContainer(target: self) + } +} + +// MARK: Queryable +public extension ParseObject { + static func find() throws -> [Self] { + return try query().find() + } + + static func query() -> Query { + return Query() + } + + static func query(_ constraints: QueryConstraint...) -> Query { + return Query(constraints) + } +} + +// MARK: Saveable +extension ParseObject { + public func save(options: API.Options) throws -> Self { + return try saveCommand().execute(options: options) + } + + func save( + options: API.Options, + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + saveCommand().executeAsync(options: options, callbackQueue: callbackQueue, completion: completion) + } + + internal func saveCommand() -> API.Command { + return API.Command.saveCommand(self) + } +} + +public extension ParseObject { + func toPointer() -> Pointer { + return Pointer(self) + } +} diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift new file mode 100644 index 000000000..d4e01ff43 --- /dev/null +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -0,0 +1,169 @@ +import Foundation + +// MARK: CurrentUserContainer +struct CurrentUserContainer: Codable { + var currentUser: T? + var sessionToken: String? +} + +// MARK: ParseUser +public protocol ParseUser: ParseObject { + var username: String? { get set } + var email: String? { get set } + var password: String? { get set } +} + +// MARK: Default Implementations +public extension ParseUser { + static var className: String { + return "_User" + } +} + +// MARK: Current User Support +extension ParseUser { + static var currentUserContainer: CurrentUserContainer? { + get { try? ParseStorage.shared.get(valueFor: ParseStorage.Keys.currentUser) } + set { try? ParseStorage.shared.set(newValue, for: ParseStorage.Keys.currentUser) } + } + + public static var current: Self? { + get { Self.currentUserContainer?.currentUser } + set { Self.currentUserContainer?.currentUser = newValue } + } + + public var sessionToken: String? { + Self.currentUserContainer?.sessionToken + } +} + +// MARK: Logging In +extension ParseUser { + public static func login(username: String, + password: String) throws -> Self { + return try loginCommand(username: username, password: password).execute(options: []) + } + + public static func login( + username: String, + password: String, + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + return loginCommand(username: username, password: password) + .executeAsync(options: [], callbackQueue: callbackQueue, completion: completion) + } + + private static func loginCommand(username: String, + password: String) -> API.Command { + let params = [ + "username": username, + "password": password + ] + + return API.Command(method: .GET, + path: .login, + params: params) { (data) -> Self in + let user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) + let response = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data) + + Self.currentUserContainer = .init( + currentUser: user, + sessionToken: response.sessionToken + ) + + return user + } + } +} + +// MARK: Logging Out +extension ParseUser { + public static func logout() throws { + _ = try logoutCommand().execute(options: []) + } + + static func logout(callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { + logoutCommand().executeAsync(options: [], callbackQueue: callbackQueue) { result in + completion(result.map { true }) + } + } + + private static func logoutCommand() -> API.Command { + return API.Command(method: .POST, path: .logout) { (_) -> Void in + currentUserContainer = nil + } + } +} + +// MARK: Signing Up +extension ParseUser { + public static func signup(username: String, + password: String) throws -> Self { + return try signupCommand(username: username, password: password).execute(options: []) + } + + public func signup() throws -> Self { + return try signupCommand().execute(options: []) + } + + public static func signup( + username: String, + password: String, + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void + ) { + return signupCommand(username: username, password: password) + .executeAsync(options: [], callbackQueue: callbackQueue, completion: completion) + } + + private static func signupCommand(username: String, + password: String) -> API.Command { + + let body = SignupBody(username: username, password: password) + return API.Command(method: .POST, path: .signup, body: body) { (data) -> Self in + let response = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data) + var user = try ParseCoding.jsonDecoder().decode(Self.self, from: data) + user.username = username + user.password = password + user.updatedAt = response.updatedAt ?? response.createdAt + + currentUserContainer = .init( + currentUser: user, + sessionToken: response.sessionToken + ) + + return user + } + } + + private func signupCommand() -> API.Command { + var user = self + return API.Command(method: .POST, path: .signup, body: user) { (data) -> Self in + let response = try ParseCoding.jsonDecoder().decode(LoginSignupResponse.self, from: data) + user.updatedAt = response.updatedAt ?? response.createdAt + user.createdAt = response.createdAt + + Self.currentUserContainer = .init( + currentUser: user, + sessionToken: response.sessionToken + ) + + return user + } + } +} + +// MARK: LoginSignupResponse +private struct LoginSignupResponse: Codable { + let createdAt: Date + let objectId: String + let sessionToken: String + var updatedAt: Date? +} + +// MARK: SignupBody +public struct SignupBody: Codable { + let username: String + let password: String +} diff --git a/Sources/ParseSwift/Objects/Queryable.swift b/Sources/ParseSwift/Objects/Queryable.swift new file mode 100644 index 000000000..a84b8d38d --- /dev/null +++ b/Sources/ParseSwift/Objects/Queryable.swift @@ -0,0 +1,29 @@ +// +// Queryable.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse. All rights reserved. +// + +public protocol Queryable { + associatedtype ResultType + + func find(options: API.Options) throws -> [ResultType] + func first(options: API.Options) throws -> ResultType? + func count(options: API.Options) throws -> Int +} + +extension Queryable { + func find() throws -> [ResultType] { + return try find(options: []) + } + + func first() throws -> ResultType? { + return try first(options: []) + } + + func count() throws -> Int { + return try count(options: []) + } +} diff --git a/Sources/ParseSwift/Objects/Saveable.swift b/Sources/ParseSwift/Objects/Saveable.swift new file mode 100644 index 000000000..ed80538f0 --- /dev/null +++ b/Sources/ParseSwift/Objects/Saveable.swift @@ -0,0 +1,20 @@ +// +// Saveable.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse. All rights reserved. +// + +public protocol Saveable: Codable { + associatedtype SavingType + + func save(options: API.Options) throws -> SavingType + func save() throws -> SavingType +} + +extension Saveable { + public func save() throws -> SavingType { + return try save(options: []) + } +} diff --git a/Sources/ParseSwift/Types/ACL.swift b/Sources/ParseSwift/Parse Types/ACL.swift similarity index 100% rename from Sources/ParseSwift/Types/ACL.swift rename to Sources/ParseSwift/Parse Types/ACL.swift diff --git a/Sources/ParseSwift/Parse Types/BaseParseUser.swift b/Sources/ParseSwift/Parse Types/BaseParseUser.swift new file mode 100644 index 000000000..6da18af38 --- /dev/null +++ b/Sources/ParseSwift/Parse Types/BaseParseUser.swift @@ -0,0 +1,20 @@ +// +// File 2.swift +// +// +// Created by Pranjal Satija on 7/19/20. +// + +import Foundation + +/// Used internally to form a concrete type representing `ParseUser`. +/// `ParseUser` itself has associatedtype requirements, so it's often awkard to use it directly. +struct BaseParseUser: ParseUser { + var username: String? + var email: String? + var password: String? + var objectId: String? + var createdAt: Date? + var updatedAt: Date? + var ACL: ACL? +} diff --git a/Sources/ParseSwift/Types/File.swift b/Sources/ParseSwift/Parse Types/File.swift similarity index 96% rename from Sources/ParseSwift/Types/File.swift rename to Sources/ParseSwift/Parse Types/File.swift index 4d933021a..47e25e945 100644 --- a/Sources/ParseSwift/Types/File.swift +++ b/Sources/ParseSwift/Parse Types/File.swift @@ -1,6 +1,6 @@ import Foundation -public struct File: Saving, Fetching { +public struct File: Saveable, Fetchable { private let __type: String = "File" // swiftlint:disable:this identifier_name public var data: Data? diff --git a/Sources/ParseSwift/Parse Types/FindResult.swift b/Sources/ParseSwift/Parse Types/FindResult.swift new file mode 100644 index 000000000..31b115ee7 --- /dev/null +++ b/Sources/ParseSwift/Parse Types/FindResult.swift @@ -0,0 +1,12 @@ +// +// FindResult.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse Community. All rights reserved. +// + +internal struct FindResult: Codable where T: ParseObject { + let results: [T] + let count: Int? +} diff --git a/Sources/ParseSwift/Types/GeoPoint.swift b/Sources/ParseSwift/Parse Types/GeoPoint.swift similarity index 100% rename from Sources/ParseSwift/Types/GeoPoint.swift rename to Sources/ParseSwift/Parse Types/GeoPoint.swift diff --git a/Sources/ParseSwift/Parse Types/NoBody.swift b/Sources/ParseSwift/Parse Types/NoBody.swift new file mode 100644 index 000000000..42e175d6a --- /dev/null +++ b/Sources/ParseSwift/Parse Types/NoBody.swift @@ -0,0 +1,9 @@ +// +// NoBody.swift +// ParseSwift +// +// Created by Florent Vilmart on 17-07-24. +// Copyright © 2020 Parse. All rights reserved. +// + +public struct NoBody: Codable {} diff --git a/Sources/ParseSwift/Types/ParseError.swift b/Sources/ParseSwift/Parse Types/ParseError.swift similarity index 100% rename from Sources/ParseSwift/Types/ParseError.swift rename to Sources/ParseSwift/Parse Types/ParseError.swift diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Parse Types/Pointer.swift similarity index 84% rename from Sources/ParseSwift/Types/Pointer.swift rename to Sources/ParseSwift/Parse Types/Pointer.swift index f74f93d55..114d55d1a 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Parse Types/Pointer.swift @@ -1,13 +1,13 @@ import Foundation -private func getObjectId(target: T) -> String { +private func getObjectId(target: T) -> String { guard let objectId = target.objectId else { fatalError("Cannot set a pointer to an unsaved object") } return objectId } -public struct Pointer: Fetching, Codable { +public struct Pointer: Fetchable, Codable { public typealias FetchingType = T private let __type: String = "Pointer" // swiftlint:disable:this identifier_name @@ -34,7 +34,7 @@ extension Pointer { let path = API.Endpoint.object(className: className, objectId: objectId) return try API.Command(method: .GET, path: path) { (data) -> T in - try getDecoder().decode(T.self, from: data) + try ParseCoding.jsonDecoder().decode(T.self, from: data) }.execute(options: options) } } diff --git a/Sources/ParseSwift/Types/Query.swift b/Sources/ParseSwift/Parse Types/Query.swift similarity index 95% rename from Sources/ParseSwift/Types/Query.swift rename to Sources/ParseSwift/Parse Types/Query.swift index 379d3d3c2..3008eddea 100644 --- a/Sources/ParseSwift/Types/Query.swift +++ b/Sources/ParseSwift/Parse Types/Query.swift @@ -93,7 +93,7 @@ public func == (key: String, value: T) -> QueryConstraint where T: Encodable return QueryConstraint(key: key, value: value, comparator: .equals) } -private struct InQuery: Encodable where T: ObjectType { +private struct InQuery: Encodable where T: ParseObject { let query: Query var className: String { return T.className @@ -136,7 +136,7 @@ internal struct QueryWhere: Encodable { } } -public struct Query: Encodable where T: ObjectType { +public struct Query: Encodable where T: ParseObject { // interpolate as GET private let method: String = "GET" private var limit: Int = 100 @@ -256,7 +256,7 @@ extension Query: Querying { private extension Query { private func findCommand() -> API.Command, [ResultType]> { return API.Command(method: .POST, path: endpoint, body: self) { - try getDecoder().decode(FindResult.self, from: $0).results + try ParseCoding.jsonDecoder().decode(FindResult.self, from: $0).results } } @@ -264,7 +264,7 @@ private extension Query { var query = self query.limit = 1 return API.Command(method: .POST, path: endpoint, body: query) { - try getDecoder().decode(FindResult.self, from: $0).results.first + try ParseCoding.jsonDecoder().decode(FindResult.self, from: $0).results.first } } @@ -273,7 +273,7 @@ private extension Query { query.limit = 1 query.isCount = true return API.Command(method: .POST, path: endpoint, body: query) { - try getDecoder().decode(FindResult.self, from: $0).count ?? 0 + try ParseCoding.jsonDecoder().decode(FindResult.self, from: $0).count ?? 0 } } } diff --git a/Sources/ParseSwift/Parse.swift b/Sources/ParseSwift/Parse.swift index 68c86f45a..8f26cb50e 100644 --- a/Sources/ParseSwift/Parse.swift +++ b/Sources/ParseSwift/Parse.swift @@ -8,10 +8,13 @@ internal struct ParseConfiguration { static var mountPath: String! } -public func initialize(applicationId: String, - clientKey: String? = nil, - masterKey: String? = nil, - serverURL: URL) { +public func initialize( + applicationId: String, + clientKey: String? = nil, + masterKey: String? = nil, + serverURL: URL, + primitiveObjectStore: PrimitiveObjectStore? = nil +) { ParseConfiguration.applicationId = applicationId ParseConfiguration.clientKey = clientKey ParseConfiguration.masterKey = masterKey @@ -19,4 +22,6 @@ public func initialize(applicationId: String, ParseConfiguration.mountPath = "/" + serverURL.pathComponents .filter { $0 != "/" } .joined(separator: "/") + + ParseStorage.shared.use(primitiveObjectStore ?? CodableInMemoryPrimitiveObjectStore()) } diff --git a/Sources/ParseSwift/Storage/ParseStorage.swift b/Sources/ParseSwift/Storage/ParseStorage.swift new file mode 100644 index 000000000..3f2f2008f --- /dev/null +++ b/Sources/ParseSwift/Storage/ParseStorage.swift @@ -0,0 +1,46 @@ +// +// ParseStorage.swift +// +// +// Created by Pranjal Satija on 7/19/20. +// + +// MARK: ParseStorage +public struct ParseStorage { + public static var shared = ParseStorage() + + private var backingStore: PrimitiveObjectStore! + + mutating func use(_ store: PrimitiveObjectStore) { + self.backingStore = store + } + + private mutating func requireBackingStore() { + guard backingStore != nil else { + print("You can't use ParseStorage without a backing store. An in-memory store is being used as a fallback.") + return + } + } + + enum Keys { + static let currentUser = "_currentUser" + } +} + +// MARK: PrimitiveObjectStore +extension ParseStorage: PrimitiveObjectStore { + public mutating func delete(valueFor key: String) throws { + requireBackingStore() + return try backingStore.delete(valueFor: key) + } + + public mutating func get(valueFor key: String) throws -> T? where T: Decodable { + requireBackingStore() + return try backingStore.get(valueFor: key) + } + + public mutating func set(_ object: T, for key: String) throws where T: Encodable { + requireBackingStore() + return try backingStore.set(object, for: key) + } +} diff --git a/Sources/ParseSwift/Storage/PrimitiveObjectStore.swift b/Sources/ParseSwift/Storage/PrimitiveObjectStore.swift new file mode 100644 index 000000000..c34fc7be6 --- /dev/null +++ b/Sources/ParseSwift/Storage/PrimitiveObjectStore.swift @@ -0,0 +1,53 @@ +// +// PrimitiveObjectStore.swift +// +// +// Created by Pranjal Satija on 7/19/20. +// + +import Foundation + +// MARK: PrimitiveObjectStore +public protocol PrimitiveObjectStore { + mutating func delete(valueFor key: String) throws + mutating func get(valueFor key: String) throws -> T? + mutating func set(_ object: T, for key: String) throws +} + +/// A `PrimitiveObjectStore` that lives in memory for unit testing purposes. +/// It works by encoding / decoding all values just like a real `Codable` store would +/// but it stores all values as `Data` blobs in memory. +struct CodableInMemoryPrimitiveObjectStore: PrimitiveObjectStore { + var decoder = JSONDecoder() + var encoder = JSONEncoder() + var storage = [String: Data]() + + mutating func delete(valueFor key: String) throws { + storage[key] = nil + } + + mutating func get(valueFor key: String) throws -> T? where T: Decodable { + guard let data = storage[key] else { return nil } + return try decoder.decode(T.self, from: data) + } + + mutating func set(_ object: T, for key: String) throws where T: Encodable { + let data = try encoder.encode(object) + storage[key] = data + } +} + +// MARK: KeychainStore + PrimitiveObjectStore +extension KeychainStore: PrimitiveObjectStore { + func delete(valueFor key: String) throws { + _ = removeObject(forKey: key) + } + + func get(valueFor key: String) throws -> T? where T: Decodable { + object(forKey: key) + } + + func set(_ object: T, for key: String) throws where T: Encodable { + _ = set(object: object, forKey: key) + } +} diff --git a/Tests/ParseSwiftTests/ParseEncoderTests.swift b/Tests/ParseSwiftTests/ParseEncoderTests.swift new file mode 100644 index 000000000..17b4797ea --- /dev/null +++ b/Tests/ParseSwiftTests/ParseEncoderTests.swift @@ -0,0 +1,86 @@ +// +// ParseEncoderTests.swift +// ParseSwiftTests +// +// Created by Pranjal Satija on 8/7/20. +// Copyright © 2020 Parse Community. All rights reserved. +// + +import XCTest +@testable import ParseSwift + +// This is necessary because of the .sortedKeys output formatting given to the JSONEncoder +// That API is only available on macOS 10.13 and up. +@available(macOS 13, *) +class ParseEncoderTests: XCTestCase { + struct Address: Codable { + let street: String + let city: String + } + + struct Name: Codable { + let first: String + let last: String + } + + struct Person: Codable { + let addresses: [String: Address] + let age: Int + let name: Name + let nicknames: [Name] + let phoneNumbers: [String] + } + + func parseEncoding(for object: T) -> Data { + let encoder = ParseEncoder() + encoder.jsonEncoder.outputFormatting = .sortedKeys + + guard let encoding = try? encoder.encode(object) else { + XCTFail("Couldn't get a Parse encoding.") + return Data() + } + + return encoding + } + + func referenceEncoding(for object: T) -> Data { + let encoder = JSONEncoder() + encoder.outputFormatting = .sortedKeys + + guard let encoding = try? encoder.encode(object) else { + XCTFail("Couldn't get a reference encoding.") + return Data() + } + + return encoding + } + + func test_encodingScalarValue() { + let encoded = parseEncoding(for: 5) + let reference = referenceEncoding(for: ["": 5]) + XCTAssertEqual(encoded, reference) + } + + func test_encodingComplexValue() { + let value = Person( + addresses: [ + "home": Address(street: "Parse St.", city: "San Francisco"), + "work": Address(street: "Server Ave.", city: "Seattle") + ], + age: 21, + name: Name(first: "Parse", last: "User"), + nicknames: [ + Name(first: "Swift", last: "Developer"), + Name(first: "iOS", last: "Engineer") + ], + phoneNumbers: [ + "1-800-PARSE", + "1-999-SWIFT" + ] + ) + + let encoded = parseEncoding(for: value) + let reference = referenceEncoding(for: value) + XCTAssertEqual(encoded, reference) + } +} diff --git a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift index d3e365d17..765e0fe49 100755 --- a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift @@ -12,7 +12,7 @@ import XCTest class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_length - struct GameScore: ParseSwift.ObjectType { + struct GameScore: ParseObject { //: Those are required for Object var objectId: String? var createdAt: Date? @@ -65,11 +65,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le BatchResponseItem(success: scoreOnServer2, error: nil)] let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -87,7 +87,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch saved[0] { case .success(let first): - XCTAssertEqual(first, scoreOnServer) + XCTAssert(first.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = first.createdAt, let savedUpdatedAt = first.updatedAt else { XCTFail("Should unwrap dates") @@ -108,7 +108,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch saved[1] { case .success(let second): - XCTAssertEqual(second, scoreOnServer2) + XCTAssert(second.hasSameObjectId(as: scoreOnServer2)) guard let savedCreatedAt = second.createdAt, let savedUpdatedAt = second.updatedAt else { XCTFail("Should unwrap dates") @@ -136,7 +136,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch saved[0] { case .success(let first): - XCTAssertEqual(first, scoreOnServer) + XCTAssert(first.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = first.createdAt, let savedUpdatedAt = first.updatedAt else { XCTFail("Should unwrap dates") @@ -157,7 +157,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch saved[1] { case .success(let second): - XCTAssertEqual(second, scoreOnServer2) + XCTAssert(second.hasSameObjectId(as: scoreOnServer2)) guard let savedCreatedAt = second.createdAt, let savedUpdatedAt = second.updatedAt else { XCTFail("Should unwrap dates") @@ -197,7 +197,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode([scoreOnServer, scoreOnServer2]) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode([scoreOnServer, scoreOnServer2]) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -249,11 +249,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le BatchResponseItem(success: scoreOnServer2, error: nil)] let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -391,7 +391,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode([scoreOnServer, scoreOnServer2]) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode([scoreOnServer, scoreOnServer2]) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -441,11 +441,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -463,7 +463,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch saved[0] { case .success(let first): - XCTAssertEqual(first, scoreOnServer) + XCTAssert(first.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = first.createdAt, let savedUpdatedAt = first.updatedAt else { XCTFail("Should unwrap dates") @@ -558,7 +558,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch firstObject { case .success(let first): - XCTAssertEqual(first, scoreOnServer) + XCTAssert(first.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = first.createdAt, let savedUpdatedAt = first.updatedAt else { XCTFail("Should unwrap dates") @@ -580,7 +580,7 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le switch secondObject { case .success(let second): - XCTAssertEqual(second, scoreOnServer2) + XCTAssert(second.hasSameObjectId(as: scoreOnServer2)) guard let savedCreatedAt = second.createdAt, let savedUpdatedAt = second.updatedAt else { XCTFail("Should unwrap dates") @@ -688,11 +688,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le BatchResponseItem(success: scoreOnServer2, error: nil)] let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -729,11 +729,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le BatchResponseItem(success: scoreOnServer2, error: nil)] let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -901,11 +901,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { @@ -944,11 +944,11 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(response) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(response) //Get dates in correct format from ParseDecoding strategy - let encoded1 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + let encoded1 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded1) - let encoded2 = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer2) + let encoded2 = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer2) scoreOnServer2 = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded2) } catch { diff --git a/Tests/ParseSwiftTests/ParseObjectCommandTests.swift b/Tests/ParseSwiftTests/ParseObjectCommandTests.swift index 18afe0ea1..2e05aec5f 100644 --- a/Tests/ParseSwiftTests/ParseObjectCommandTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectCommandTests.swift @@ -12,7 +12,7 @@ import XCTest class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_length - struct GameScore: ParseSwift.ObjectType { + struct GameScore: ParseObject { //: Those are required for Object var objectId: String? var createdAt: Date? @@ -74,7 +74,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.ACL = nil let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -87,7 +87,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ } do { let fetched = try score.fetch(options: []) - XCTAssertEqual(fetched, scoreOnServer) + XCTAssert(fetched.hasSameObjectId(as: scoreOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -107,7 +107,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ do { let fetched = try score.fetch(options: [.useMasterKey]) - XCTAssertEqual(fetched, scoreOnServer) + XCTAssert(fetched.hasSameObjectId(as: scoreOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -134,7 +134,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ switch result { case .success(let fetched): - XCTAssertEqual(fetched, scoreOnServer) + XCTAssert(fetched.hasSameObjectId(as: scoreOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -159,7 +159,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ expectation2.fulfill() switch result { case .success(let fetched): - XCTAssertEqual(fetched, scoreOnServer) + XCTAssert(fetched.hasSameObjectId(as: scoreOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -192,7 +192,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -220,7 +220,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.ACL = nil let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -273,7 +273,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -286,7 +286,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ } do { let saved = try score.save() - XCTAssertEqual(saved, scoreOnServer) + XCTAssert(saved.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = saved.createdAt, let savedUpdatedAt = saved.updatedAt else { XCTFail("Should unwrap dates") @@ -306,7 +306,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ do { let saved = try score.save(options: [.useMasterKey]) - XCTAssertEqual(saved, scoreOnServer) + XCTAssert(saved.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = saved.createdAt, let savedUpdatedAt = saved.updatedAt else { XCTFail("Should unwrap dates") @@ -337,7 +337,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -397,7 +397,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ switch result { case .success(let saved): - XCTAssertEqual(saved, scoreOnServer) + XCTAssert(saved.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = saved.createdAt, let savedUpdatedAt = saved.updatedAt else { XCTFail("Should unwrap dates") @@ -424,7 +424,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ switch result { case .success(let saved): - XCTAssertEqual(saved, scoreOnServer) + XCTAssert(saved.hasSameObjectId(as: scoreOnServer)) guard let savedCreatedAt = saved.createdAt, let savedUpdatedAt = saved.updatedAt else { XCTFail("Should unwrap dates") @@ -455,7 +455,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.ACL = nil let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -481,7 +481,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.ACL = nil let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -562,7 +562,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.updatedAt = Date() let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { @@ -589,7 +589,7 @@ class ParseObjectCommandTests: XCTestCase { // swiftlint:disable:this type_body_ scoreOnServer.updatedAt = Date() let encoded: Data! do { - encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(scoreOnServer) + encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(scoreOnServer) //Get dates in correct format from ParseDecoding strategy scoreOnServer = try scoreOnServer.getTestDecoder().decode(GameScore.self, from: encoded) } catch { diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index ab4f8ac87..09188c03a 100755 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -12,7 +12,7 @@ import XCTest class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length - struct GameScore: ParseSwift.ObjectType { + struct GameScore: ParseObject { //: Those are required for Object var objectId: String? var createdAt: Date? @@ -64,7 +64,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -78,7 +78,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length XCTFail("Should unwrap first object found") return } - XCTAssertEqual(score, scoreOnServer) + XCTAssert(score.hasSameObjectId(as: scoreOnServer)) } catch { XCTFail(error.localizedDescription) } @@ -98,7 +98,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length XCTFail("Should unwrap score count") return } - XCTAssertEqual(score, scoreOnServer) + XCTAssert(score.hasSameObjectId(as: scoreOnServer)) case .failure(let error): XCTFail(error.localizedDescription) } @@ -117,7 +117,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -139,7 +139,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -158,7 +158,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -172,7 +172,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length XCTFail("Should unwrap first object found") return } - XCTAssertEqual(score, scoreOnServer) + XCTAssert(score.hasSameObjectId(as: scoreOnServer)) } catch { XCTFail(error.localizedDescription) } @@ -188,7 +188,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length switch result { case .success(let score): - XCTAssertEqual(score, scoreOnServer) + XCTAssert(score.hasSameObjectId(as: scoreOnServer)) case .failure(let error): XCTFail(error.localizedDescription) @@ -207,7 +207,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -229,7 +229,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -248,7 +248,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -294,7 +294,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -316,7 +316,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let results = FindResult(results: [scoreOnServer], count: 1) MockURLProtocol.mockRequests { _ in do { - let encoded = try scoreOnServer.getEncoderWithoutSkippingKeys().encode(results) + let encoded = try scoreOnServer.getEncoder(skipKeys: false).encode(results) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil diff --git a/Tests/ParseSwiftTests/ParseSwiftTests.swift b/Tests/ParseSwiftTests/ParseSwiftTests.swift deleted file mode 100644 index a8865d549..000000000 --- a/Tests/ParseSwiftTests/ParseSwiftTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// ParseTests.swift -// ParseTests -// -// Created by Florent Vilmart on 17-07-23. -// Copyright © 2017 Parse. All rights reserved. -// - -import XCTest -@testable import ParseSwift - -class ParseTests: XCTestCase {} diff --git a/Tests/ParseSwiftTests/ParseUserCommandTests.swift b/Tests/ParseSwiftTests/ParseUserCommandTests.swift index 42e5aafd7..34f2980a0 100644 --- a/Tests/ParseSwiftTests/ParseUserCommandTests.swift +++ b/Tests/ParseSwiftTests/ParseUserCommandTests.swift @@ -12,7 +12,7 @@ import XCTest class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_length - struct User: ParseSwift.UserType { + struct User: ParseUser { //: Those are required for Object var objectId: String? var createdAt: Date? @@ -28,7 +28,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le var customKey: String? } - struct LoginSignupResponse: ParseSwift.UserType { + struct LoginSignupResponse: ParseUser { var objectId: String? var createdAt: Date? var sessionToken: String @@ -102,7 +102,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le userOnServer.ACL = nil let encoded: Data! do { - encoded = try userOnServer.getEncoderWithoutSkippingKeys().encode(userOnServer) + encoded = try userOnServer.getEncoder(skipKeys: false).encode(userOnServer) //Get dates in correct format from ParseDecoding strategy userOnServer = try userOnServer.getTestDecoder().decode(User.self, from: encoded) } catch { @@ -115,7 +115,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le do { let fetched = try user.fetch() - XCTAssertEqual(fetched, userOnServer) + XCTAssert(fetched.hasSameObjectId(as: userOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -135,7 +135,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le do { let fetched = try user.fetch(options: [.useMasterKey]) - XCTAssertEqual(fetched, userOnServer) + XCTAssert(fetched.hasSameObjectId(as: userOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -164,7 +164,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le switch result { case .success(let fetched): - XCTAssertEqual(fetched, userOnServer) + XCTAssert(fetched.hasSameObjectId(as: userOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -191,7 +191,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le switch result { case .success(let fetched): - XCTAssertEqual(fetched, userOnServer) + XCTAssert(fetched.hasSameObjectId(as: userOnServer)) guard let fetchedCreatedAt = fetched.createdAt, let fetchedUpdatedAt = fetched.updatedAt else { XCTFail("Should unwrap dates") @@ -223,7 +223,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le userOnServer.ACL = nil let encoded: Data! do { - encoded = try userOnServer.getEncoderWithoutSkippingKeys().encode(userOnServer) + encoded = try userOnServer.getEncoder(skipKeys: false).encode(userOnServer) //Get dates in correct format from ParseDecoding strategy userOnServer = try userOnServer.getTestDecoder().decode(User.self, from: encoded) } catch { @@ -280,7 +280,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le let encoded: Data! do { - encoded = try userOnServer.getEncoderWithoutSkippingKeys().encode(userOnServer) + encoded = try userOnServer.getEncoder(skipKeys: false).encode(userOnServer) //Get dates in correct format from ParseDecoding strategy userOnServer = try userOnServer.getTestDecoder().decode(User.self, from: encoded) } catch { @@ -397,7 +397,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le userOnServer.updatedAt = Date() let encoded: Data! do { - encoded = try userOnServer.getEncoderWithoutSkippingKeys().encode(userOnServer) + encoded = try userOnServer.getEncoder(skipKeys: false).encode(userOnServer) //Get dates in correct format from ParseDecoding strategy userOnServer = try userOnServer.getTestDecoder().decode(User.self, from: encoded) } catch { @@ -425,7 +425,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le userOnServer.updatedAt = Date() let encoded: Data! do { - encoded = try userOnServer.getEncoderWithoutSkippingKeys().encode(userOnServer) + encoded = try userOnServer.getEncoder(skipKeys: false).encode(userOnServer) //Get dates in correct format from ParseDecoding strategy userOnServer = try userOnServer.getTestDecoder().decode(User.self, from: encoded) } catch { @@ -444,7 +444,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -500,7 +500,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -517,7 +517,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -532,7 +532,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -588,7 +588,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -605,7 +605,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -620,7 +620,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -655,7 +655,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil @@ -672,7 +672,7 @@ class ParseUserCommandTests: XCTestCase { // swiftlint:disable:this type_body_le MockURLProtocol.mockRequests { _ in do { - let encoded = try loginResponse.getEncoderWithoutSkippingKeys().encode(loginResponse) + let encoded = try loginResponse.getEncoder(skipKeys: false).encode(loginResponse) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil