aws-amplify / amplify-swift Goto Github PK
View Code? Open in Web Editor NEWA declarative library for application development using cloud services.
License: Apache License 2.0
A declarative library for application development using cloud services.
License: Apache License 2.0
After finalizing on the different storage error types, and messages, take a pass to clean up
ConnectionProvider provides events for connections and subscriptions. in this case, only a SubscriptionConnection cares about the ConnectionProvider events, while holding onto all of the subscribers for the connection.
for example, when connection provider sends a connected
event, connection will listen to this and react to this by checking if there are any subscribers in memory which need to start the subscription process.
when we move over to the case where SubscriptionConnection creates SubscriptionOperations which manage their own subscription to the connection provider, then there is see a need to register multiple listeners on the connection provider.
in the SubscriptionConnection and SubscriptionOperation model. the SubscriptionConnection holds a queue of subscriptionOperations. SubscriptionOperation is instantiated with the connection provider. Connection registers a listener with the connection provider and reacts to connection related events such as disconnected, try reconnect. Subscriptions also register their own listener with the connection provider and reacts to the connection provider events in the way that is relevant for them. ie. when disconnected event comes, it is responsible for setting its own subscription state to disconnected. when subscriptionDisconnect event comes, it is responsible for removing the listener from the connection provider, and calling finish()
on the operation (which then removes it from the queue inside the SubscriptionConnection.
We do a lot of work on DispatchQueues (good), but don't assign target queues to them (bad), which could result in an explosion of threads for some cases (e.g., Hub, which uses a SynchronizedDictionary for each channel, each with its own concurrency queue).
We need to audit our use of DispatchQueues and make sure we're only using concurrent queues where needed, and set targets on serial queues where appropriate.
maybe this is a subject for a deeper discussion, but should we depend on these legacy utilities? The more we depend on stuff like NSDate().aws_stringValue
the harder will be to remove these dependencies to make Amplify leaner and less dependent on AWSCore
.
Maybe this could be a better fit? https://developer.apple.com/documentation/foundation/iso8601dateformatter
Originally posted by @drochetti in #58
Unfortunately there's no consistent behavior in the current GraphQL transformer for plural handling. The list
queries just append an "s" whereas the sync
queries use the actual plural form: listPersons
vs syncPeople
. This will be changed in the future and we will need to handle the fallback logic accordingly.
Originally posted by @drochetti in #175
How will storage return operations that can be used with Hub on App restart or just another place in the app?
[Storage] rename Put to UploadFile and PutData
Per discussion #134 (comment)
We should investigate whether we can make the static AnyModel.schema
property getter fail with a fatalError("AnyModel.schema is not supported. Use the instance property, which reflects the schema of the underlying instance.")
Given the following type:
public struct MutationEvent: Model {
public enum MutationType: String, Codable {
case create
case update
case delete
}
public let id: Identifier
public let modelName: String
public let json: String
public let mutationType: MutationType
public let createdAt: Date
public init(id: Identifier = UUID().uuidString,
modelName: String,
data: String,
mutationType: MutationType,
createdAt: Date = Date()) {
self.id = id
self.modelName = modelName
self.json = data
self.mutationType = mutationType
self.createdAt = createdAt
}
}
extension MutationEvent {
// MARK: - CodingKeys
public enum CodingKeys: String, ModelKey {
case id
case modelName
case json
case mutationType
case createdAt
}
public static let keys = CodingKeys.self
// MARK: - ModelSchema
public static let schema = defineSchema { definition in
let mutation = MutationEvent.keys
definition.attributes(.isSyncable, .isSystem)
definition.fields(
.id(),
.field(mutation.modelName, is: .required, ofType: .string),
.field(mutation.json, is: .required, ofType: .string),
.field(mutation.mutationType,
is: .required,
ofType: .enum(type: MutationType.self)),
.field(mutation.createdAt, is: .required, ofType: .dateTime)
)
}
}
Registering the type as a model fails:
2019-11-18 16:19:40.219199-0800 AmplifyTestApp[21061:3952734] Fatal error: Model with name MutationType could not be found.: file /Users/schmelte/src/github-repos/amplify-ios/Amplify/Categories/DataStore/Model/ModelSchema+Definition.swift, line 70
It looks like it's not going to be as simple as just detecting the type
in ModelField.typeDefinition
, since the incoming type
on the field is MutationType
, and I don't see any path to find that it's actually an enum
.
Currently we're using the existence of syncable models to start syncing; instead use the Android method of detecting whether an API is configured
AWSS3StoragePluginAPIBehaviorTests should be renamed to ClientBehaviorTests
Rename existing classes to agreed upon class names to be in line with Android. Need to check where we have defined this structure and naming, cross check with Android's code base.
off the top of my head;
we may have to link "via the Hub" to the Hub
category or even to a guide. Not something that needs to be addressed right now but maybe we could create a task to track it? We will need to do it not only here, but it just occurred to me, that we should assume that some devs might now know what "via the Hub" means, or how to do it, when they are reading the docs of other categories.
Originally posted by @drochetti in #93
In one of the next PRs, we should move this service to an AWSPluginCommon package, since all of our plugins will use AWSMobileClient. That will also pave the way for us to (eventually) have an actual Auth plugin.
Originally posted by @palpatim in https://github.com/_render_node/MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDMyODM3OTc0Mg==/comments/review_comment
Add revenue event
public struct AnalyticsRevenueEvent: AnalyticsEvent {
/// Name of the event
public var name: String
/// Identifier of the product
public var productId: String
/// Value of the product
public var value: Double
/// Revenue of the sale
public var revenue: Double
/// Amount sold of the product
public var quantity: Int
/// Currency of the sale
public var currency: String
/// Properties of the event
public var properties: [String: AnalyticsPropertyValue]?
public init(name: String,
productId: String,
value: Double,
revenue: Double,
quantity: Int,
currency: String,
properties: [String: AnalyticsPropertyValue]? = nil) {
self.name = name
self.productId = productId
self.value = value
self.revenue = revenue
self.quantity = quantity
self.currency = currency
self.properties = properties
}
}
Talk to android team, agree if removing unknown and notInProces AsyncEvent cases--these aren't useful, and add to clutter at call sites
Functional testing on these operations or integration tests on the Amplify.Predictions methods
Right now, it is impossible for callers to use the Logging or Hub categories until we've called Amplify.configure
. This needs to be changed so that default plugins can use the logger and hub during initialization and configuration flows.
We've made partial progress toward that--the Hub and Logging categories are the first to be configure
d, but that isn't sufficient for (for example) setting up a Plugin that uses the logger during its initialization.
I was thinking of adding each Protocol conformance in its own file, so Model
-based and GraphQLRequest
-based APIs are defined and implemented in isolation. Thoughts?
Originally posted by @drochetti in #133
the caveat here is that we are looking for a single endpoint config when apiName
is not passed in and throws configuration error if more than one (since it does not know which to pick). developer needs to call the right APIs (get/post/post/delete or mutate/query/subscribe) for the right endpoint (api gateway endpoints or appsync graphql endpoints).
The final solution should include a proposal for CLI team to add a type of endpoint. then our code will look under the graphql type of restAPI types and find the single one. or if it finds more than one, return error
Current implementation of the model registry is a top level var accessible via top-level un-namespaced methods. Need to encapsulate this in a type, probably DataStore
.
Currently, GraphQLDocuments and SQLStatements generate their code with nicely formatted, indented, human-readable code. That makes debugging easier, but is potentially costly, and definitely unnecessary, for machine-to-machine communication. We should separate the generated code into human-readable and machine-consumable components, perhaps by using debugDescription
to provide custom representations for human readable use cases.
update putData to uploadData
update getData to downloadData
We need to support Swift Package Manager as a distribution mechanism for Amplify and bundled plugins.
To reproduce:
2019-11-27T03:10:52Z
Expected results:
Actual results:
dataCorrupted(Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 269", intValue: 269), CodingKeys(stringValue: "updatedAt", intValue: nil)], debugDescription: "Date string does not match format expected by formatter.", underlyingError: nil))
Check if all plugins use weak reference to services that they use - https://github.com/aws-amplify/amplify-ios/pull/80/files?file-filters%5B%5D=.swift#r344970193
List API usability testing is required.
Currently in line with JS library; uses LLC s3 to retrieve list of keys.
According to S3 documentation, only returns 1000 items.
This issue is test usability of List API, document any limitations (like the 1000 returned items) and make any changes necessary to the usability that we want
Functions like get region from configuration is used by different plugins and can be moved to a common place - https://github.com/aws-amplify/amplify-ios/pull/80/files?file-filters%5B%5D=.swift#r344967847
JS returns key, eTag, lastModified, size
We should just stick with this until we decide anything different, ie. different naming, removing something.
One of the intents of the AWSAuthorizationConfiguration
is to obviate the need for config objects to directly know about auth service. Let's talk about this to see if we can remove the dependency. Perhaps we can create a configuration factory with the auth service, that can then be used to instantiate configurations when needed.
Originally posted by @palpatim in https://github.com/_render_node/MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzEyNTc2OTcx/pull_request_reviews/more_threads
can you expand on this a bit? I am creating a structure for getting different url request constants like
struct URLRequestContants {
static let appSyncServiceName = "appsync"
struct Header {
static let xAmzDate = "X-Amz-Date"
static let contentType = "Content-Type"
static let userAgent = "User-Agent"
static let xApiKey = "x-api-key"
}
struct ContentType {
static let applicationJson = "application/json"
}
struct UserAgent {
static let amplify = "amplify-ios/0.0.1 Amplify"
}
}
maybe this just needs a renaming? or should i change the structure here as well?
Originally posted by @lawmicha in https://github.com/_render_node/MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzEyNTc2OTcx/pull_request_reviews/more_threads
is this blocking? If interceptors execute asynchronous operations I believe they should be asynchronous by nature. I believe we should avoid using any type of semaphores to turn asynchronous operations into synchronous ones unless strictly necessary.
So instead of:
func intercept(_ request: URLRequest) throws -> URLRequest
The API could be:
func intercept(_ request: URLRequest, next: URLRequestInterceptor) throws
// or in a functional way where the `next` refers to the next interceptors `intercept` function
func intercept(_ request: URLRequest, next: (URLRequest) -> Void) throws
Once an interceptor is done processing an URLRequest
, it calls next
to continue the interceptor chain.
Originally posted by @drochetti in https://github.com/_render_node/MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzEyMjY0MzE4/pull_request_reviews/more_threads
does this work when creating the API. I believe I remember @palpatim calling out that this should be a Float
on AppSync. Related to this, should we represent GraphQL's Float with Decimal
on Swift, given your callout regarding double precision when serializing?
Originally posted by @drochetti in #133
Investigate:
JS experience, how it is currently enabled by the developer. Can transferAcceleration be configured on per request basis? ie. only enable for downloads and not uploads, for some reason. Native experience, how is it enabled on transferUtility now?
CLI changes
Library changes
Extract TransferAcceleration
and propagate into setting up dependencies (transferUtility/s3)
Need to add more unit tests for the AWS3StorageService class. Some happy path cases can be added now, while some failure cases can come after finalizing on error mapping. ie. Mock dependency to return a failed task with HttpStatus error with code 403, verify that the StorageError coming from AWS3StorageService is AccessDenied. etc.
implement the rest of the REST APIs, and clean up the code for get
and post
this gave a huge performance boost to SQL result set -> model conversion. I believe this is still need more testing and most likely there are more performance improvements to be made, but I would say that so far so good. I query of 1300 rows, including a joint table with 50 rows took less than 80ms to de-serialize.
That said, I'm not 100% happy with this logic, readability and caching strategy
Originally posted by @drochetti in #153
[Storage] Should default access level be 'public'
Capturing some of the TODO's in an Issue. In JS, it is by default public: https://aws-amplify.github.io/docs/js/storage#file-access-levels
I find the 'public' directory kind of odd use case, i'm not sure when we would ever upload to a public directory since anyone can delete from there. I could extract someone's amplifyconfiguration.json, reconstruct my own app using those configuration values, and call remove on all of the keys in the public folder
When setting up an Amplify project for iOS, I would like for the CLI to automatically update the Cocoapods Podfile
as I add or remove categories in the CLI.
For example, if I want to add Auth to my app with the default "basic auth", there are known AWS SDK dependencies that need to be added to my Podfile to install the AWS SDK on the client.
Here's the scenario:
Amplify init as an iOS project
amplify add auth
- select the default option which is just basic auth (username/password)
amplify push
The CLI updates my awsconfiguration.json with any new endpoints.
At this point I need to go back to the iOS docs and figure out what dependencies are needed for adding the auth SDK to my app. In the case of the default auth here, the following three dependencies should be added to my Podfile:
pod 'AWSUserPoolsSignIn', '~> 2.8.0' # Optional dependency required to use drop-in UI
pod 'AWSMobileClient', '~> 2.8.0' # Required dependency for Auth with AWS SDK for iOS
pod 'AWSAuthUI', '~> 2.8.0' # (Optional) dependency is required when using drop-in UI
So, with this feature implemented, after an amplify push, the Podfile is automatically updated and I simply run pod install
.
List API returns size as Int, investigate if should be uint64 or int64
That's a good question and the part I struggled with. Right now the depth is not specified. This logic will even break in case or circular references.
How to decode missing properties to a Struct that require them? I'm having a hard time keeping the data model types consistent but optimize for performance and different use cases of associations (lazy vs eager, required vs optional)
Originally posted by @drochetti in #164
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.