Coder Social home page Coder Social logo

appstoreconnect-swift-sdk's Introduction

appstoreconnect-swift-sdk's People

Contributors

adamjcampbell avatar andyj-at-aspin avatar avdlee avatar balestrapatrick avatar barrault01 avatar dechengma avatar heestand-xyz avatar instanceof avatar karrysodhi avatar liamnichols avatar lvalenta avatar marcelmendesfilho avatar marcprux avatar mat1th avatar mathiasemil avatar mkj-is avatar mortengregersen avatar orj avatar pakko972 avatar ristkof avatar rnystrom avatar ruipfcosta avatar schwmi avatar scottlemke avatar shakarang avatar sherlouk avatar swiftleebot avatar valeriaalampi avatar viktorianec avatar workshed avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

appstoreconnect-swift-sdk's Issues

Not a valid filter type in ListBuilds.swift

Hi there, when I was trying to get builds by APIEndpoint.builds and input some filters like preReleaseVersion or preReleaseVersionVersion I will get { 'prereleaseversion' is not a valid filter type } error.

After checking the API, found there are some mismatches and the params might be case sensitive. so some cleanups need to be done in ListBuild.Filter

Implement JWT Token generation support

As soon as we have access to the API, we should start implementing authentication support with JWT.

A great way to do this, for now, is by using Alamofire's RequestAdapter:

extension Authenticator: RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        guard let bearer = bearer else {
            return urlRequest
        }
        var urlRequest = urlRequest
        urlRequest.setValue("Bearer " + bearer, forHTTPHeaderField: Authenticator.authorizationHeaderKey)
        return urlRequest
    }
}

In combination with the RequestRetrier:

extension Authenticator: RequestRetrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        guard request.request?.url?.path != Endpoints.authorize.path else {
            completion(false, 0.0)
            return
        }

        lock.lock() ; defer { lock.unlock() }
        
        if let bearer = bearer, bearer.isExpired == false {
            completion(false, 0.0)
        } else {
            requestsToRetry.append(completion)
            
            refreshToken { [weak self] (result) in
                guard let strongSelf = self else { return }
                strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }

                let succeeded = result.error == nil
                strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) }
                strongSelf.requestsToRetry.removeAll()
            }
        }
    }
}

note: above code is copied from the WeTransfer iOS app and can be used as inspiration for this implementation.

Gimme them tags?

IIUC - SPM requires semantic versioning and tagging to know how to pull the right version of a dependency.

Is there any plan to add tags to this project? I'd love to use it, but I don't think SPM will be able to pull down this project without tags.

From swift.org:

Each dependency specifies a source URL and version requirements. The source URL is a URL accessible to the current user that resolves to a Git repository. The version requirements, which follow Semantic Versioning (SemVer) conventions, are used to determine which Git tag to check out and use to build the dependency. For the FisherYates dependency, the most recent version with a major version equal to 2 (for example, 2.0.4) will be used. Similarly, the PlayingCard dependency will use the most recent version with a major version equal to 3.

When I add this project as a dependency in my Package.swift like you describe in the README, I receive this error:

Screen Shot 2019-05-01 at 3 48 03 PM

It looks like SPM thinks I am trying to use a local dependency since there is no from in my package dependency to tell SPM which tag to pull.

I'm using the latest Xcode (10.2.1 as of today), and here's the output of swift --version:

Screen Shot 2019-05-01 at 3 50 30 PM

I would paste my Package.swift here, but its literally one library, with only this one dependency.

Question: How to download Profiles?

Hello, I am enjoying using this SDK through a cli.

I am using the listProfiles endpoint provider.request(.listProfiles()), and I am getting back the list of profiles correctly.

However, I don't know how should I save each profile as .mobileProvision files on disk. I thought I could write profile.attributes.profileContent (of type string) to a file, but unfortunately doesn't work.

Could you provide with any guidance if you have faced this problem?

Thank you in advance!

Naming mismatch for `include` field in Response

Hi there, thanks for the SDK. I just found there are some naming mismatches for struct response, which will lead to response.include returns null

According to the API documentation, the field should be named as follows

include -> included

image

Retrieving certificates list - no results

Hi guys,

I have an issue with retrieving the certificates list from my account, I was wondering if you experience the same issue. I have in my account three certificates: one development, one distribution, and one APNS.

This is the response I receive :

CertificatesResponse(data: [], links: AppStoreConnect_Swift_SDK.PagedDocumentLinks(first: nil, next: nil, self: https://api.appstoreconnect.apple.com/v1/certificates), meta: Optional(AppStoreConnect_Swift_SDK.PagingInformation(paging: AppStoreConnect_Swift_SDK.PagingInformation.Paging(total: 0, limit: 20))))

The other endpoints are working just fine.

Thank you.

User.Relationships.VisibleApps.Data always nil

I'm trying to collect a list of apps to whom a user is eligible, but unfortunately the result is always nil. This is the snippet:

        let provider = APIProvider(configuration: configuration)
        var out: [UserModel] = []
        provider.request(APIEndpoint.users()) { result in
            switch result {
            case .success(let userResponse):
                for u in userResponse.data {
                    let uid = u.id
                    let roles = (u.attributes?.roles ?? []).map{$0.rawValue}
                    let username = u.attributes?.username ?? ""
                    let firstName = u.attributes?.firstName ?? ""
                    let lastName = u.attributes?.lastName ?? ""
                    let provisioningAllowed = u.attributes?.provisioningAllowed ?? false
                    let allAppsVisible = u.attributes?.allAppsVisible ?? false
                    let visibleApps = u.relationships?.visibleApps?.data?.compactMap{$0.id} ?? []
                    let user = UserModel(uid: uid,
                                         username: username,
                                         firstName: firstName,
                                         lastName: lastName,
                                         provisioningAllowed: provisioningAllowed,
                                         allAppsVisible: allAppsVisible,
                                         roles: roles,
                                         visibleApps: visibleApps)
                    out.append(user)
                }
                completion(out.sorted{$0.firstName < $1.firstName})
            case .failure(let error):
                print(error)
            }

Then debugging a single user:

po u.relationships

data is always nil:

▿ Optional<Relationships>
  ▿ some : Relationships
    ▿ visibleApps : Optional<VisibleApps>
      ▿ some : VisibleApps
        - data : nil
        ▿ links : Optional<Links>
          ▿ some : Links
            ▿ related : Optional<URL>
              ▿ some : https://api.appstoreconnect.apple.com/v1/users/0289f9b9-301e-40cf-8bc3-023df002c221/visibleApps
                - _url : https://api.appstoreconnect.apple.com/v1/users/0289f9b9-301e-40cf-8bc3-023df002c221/visibleApps
            ▿ self : Optional<URL>
              ▿ some : https://api.appstoreconnect.apple.com/v1/users/0289f9b9-301e-40cf-8bc3-023df002c221/relationships/visibleApps
                - _url : https://api.appstoreconnect.apple.com/v1/users/0289f9b9-301e-40cf-8bc3-023df002c221/relationships/visibleApps
        - meta : nil

Suggestions

Please consider introducing these (potentially breaking) changes:

  • Consistent Naming: select:/fields:, filter:/filters:, limit:/limits:.
    select is probably a typo or SQL-mindset mistake because there are only 2 methods with this parameter. Should just get rid of it. Since fields and limits aren't directly included in the API request (rather they form a JSONPath-like key along with referenced types), I suggest making all collection (e.g. [Limit]) parameters plural and single-type (e.g. Int) parameters singular.

  • Consolidate Data and Link Models: There really isn't a single reason that each response should inline their own named Data and Link structures since all have exactly the same properties. Apple's documentation isn't source code per-se so they give each structure a different name for readability, but in programming sense doing this is a huge waste of resources and hammers interoperability.

I can make these changes and PR (and adding some missing APIs along the way) but I'd like to ask for opinions first.

APIEndpoint.downloadSalesAndTrendsReports doesn't work

Hello

This "APIEndpoint.apps" was working well.
APIEndpoint.apps(
select: [.apps([.name]), .builds([.version, .processingState, .uploadedDate])],
include: [.builds],
sortBy: [.bundleIdAscending],
limits: [.apps(1)])

But "APIEndpoint.downloadSalesAndTrendsReports" is not working.
let endpoint = APIEndpoint.downloadSalesAndTrendsReports(
filter: [
.reportType([.SALES]),
.reportSubType([.SUMMARY]),
.frequency([.WEEKLY]),
.reportDate(["2019-01-01"]),
.vendorNumber(["myVendorNumber"]),
])
provider.request(endpoint) {
switch $0 {
case .success(let res):
print("success (res)")
case .failure(let error):
print("Something went wrong fetching the apps: (error)")
}
}
Output window.
▿ FAILURE: requestFailure(400, nil)
▿ failure : Error
▿ requestFailure : 2 elements
- .0 : 400
- .1 : nil

So I test by cURL. cURL was working well. (I used JWTRequestsAuthenticator.createToken() for replace [token])
curl -v -H 'Authorization: Bearer [token]' --globoff "https://api.appstoreconnect.apple.com/v1/salesReports?filter[reportType]=SALES&filter[reportSubType]=SUMMARY&filter[frequency]=DAILY&filter[reportDate]=2019-01-01&filter[vendorNumber]=myVendorNumber" > output.gz

How to deduce BetaTester lifecycle?

In Apple's app store connect web UI, BetaTesters in a BetaGroup (MyApps -> "AnApp" -> TestFlight -> All Testers) show up in 3 possible states:

  1. Invited: with a link to re-invite and an invitation date
  2. Accepted: with a date
  3. Installed + version/build number: with a date

screenshot 2019-01-09 at 00 07 52

You can get all the Apps, BetaGroups, BetaTesters and relations from the API, but nowhere did I find dates (for the invitation, for the acceptance of the invitation, for the install of a build).

Do you have any idea how to find this information?

Thank you

The PagedDocumentLinks doesn't have `limit` field

Step to reproduce

  1. when calling APIEndpoint.listDevice() with no limit, the next field we get in the response is
    cursor=eyJv123ZzZXQi123CJ9&limit=20

  2. so if I want to get next page I tried
    APIEndpoint.listDevices(next: response.links)
    but the &limit=20 in the link will be omitted by the current logic, so the request URL will be
    v1/devices?cursor=eyJv123ZzZXQi123CJ9 -> will get the same response in 1

Possible Solution:

extension PagedDocumentLinks {
    /// Returns the `cursor` value which can be used for pagination.
    var nextCursor: String? {
        return next?.value(for: "cursor")
    }
    /// Add a limit here
    var limit: String? {
        return next?.value(for: "limit")
    }
}

/// then add the limit to params here
if let limitFromNext = next?.limit { parameters["limit"] = limitFromNext }

Modify Provisioning Profiles

Hi,

we have many profiles in our apple dev portal and it is always annoying when a new device has been bought. Because then we have to add the device for every profile in order for our QA to be able to install it.

My idea is to automated this step. I looked into your framework and used it to download the provisioning profiles. But I saw no way to change them.

I am not sure that this is even possible.

If it is possible I would like to implement it for you. I never worked on a public git repo, so I am not sure what the process will be. Can I just push a new branch in this repo and you will review it?

Greetings,

Sascha Sanzenbacher

Add Mac support

As of now, mac support isn't added while it should be very simple with just some configurations in the Xcode Project and podspec. We use Alamofire as a dependency which supports it as well.

APIProvider fails to authorize requests 25 minutes after sending the first request

How to reproduce:

  1. Create an APIProvider and call any request.
  2. Sleep for 25 minutes and call another request.

Why it happens:

APIProvider holds a reference to JWTRequestsAuthenticator instance that manages requests authorization.

private lazy var requestsAuthenticator = JWTRequestsAuthenticator(apiConfiguration: self.configuration)

When JWTRequestsAuthenticator is instantiated it instantiates a JWT object that in turn creates a payload set with an expirationTime = Date().addingTimeInterval(expireDuration).timeIntervalSince1970.

self.jwtCreator = JWT(keyIdentifier: apiConfiguration.privateKeyID, issuerIdentifier: apiConfiguration.issuerID, expireDuration: 60 * 20)

public init(keyIdentifier: String, issuerIdentifier: String, expireDuration: TimeInterval, baseDate: Date = Date()) {
header = Header(keyIdentifier: keyIdentifier)
payload = Payload(issuerIdentifier: issuerIdentifier, expirationTime: baseDate.addingTimeInterval(expireDuration).timeIntervalSince1970)

The expirationTime is thus constant. When JWTRequestsAuthenticator renews the cachedToken, the new token is created with the original expirationTime which makes the jwt token invalid.

Build fail when run cocoapods example project

  1. When I run the example project, it shows an error 'PhaseScriptExecution failed with a nonzero exit code'. After I delete the script in Build Phase-Swift lint and the error is gone.

  2. I want to know whether the 'Xcode 10 Build System Caching Fix' Item in Build Phases is to fix the bug that after changing some code in development pods and run project under new build system but it doesn't work, unless clean the project first and build.

Trying to build a command line tool

When creating a command line tool in Xcode 11.3.1 and adding the package dependency for appstoreconnect-swift-sdk as specified in the README, compilation fails with errors of API's that are only available in macOS 10.12 or newer (iso8601 JSON decoding strategy for instance).

My deployment target for my command line tool is macOS 10.15 so I don't see why that should be a problem.

I do not know how to change the appstoreconnect-swift-sdk package to have a minimum deployment target of macOS 10.15 so I do not know how to resolve this.

Generating Token - Still getting Unauthorized response

I am trying to generate a JWT only and I’m getting a 401 error. My p8, kid, and issuer id are all correct.

let privateKey = try String(contentsOfFile: "private_keys/AuthKey_XXXXXX.p8").cleaned()

let creator = JWT(
  keyIdentifier: "XXXXXX", 
  issuerIdentifier: "000000-000000-000000-000000-000000", 
  expireDuration: 60 * 20
)
let token = try creator.signedToken(using: privateKey)

I can generate a proper JWT using a ruby script.

require "base64"
require "jwt"

ISSUER_ID = "000000-000000-000000-000000-000000"
KEY_ID = "XXXXXX"
private_key = OpenSSL::PKey.read(File.read('private_keys/AuthKey_XXXXXX.p8'))

token = JWT.encode(
   {
    iss: ISSUER_ID,
    exp: Time.now.to_i + 20 * 60,
    aud: "appstoreconnect-v1"
   },
   private_key,
   "ES256",
   header_fields={
     kid: KEY_ID }
 )

puts token

Not sure how to debug.

bug: failed to decode date

Hey there 👋

I'm currently getting the following error when trying to make a request to the appStoreVersions endpoint.

Error: Failed to decode date: 2020-11-05T13:28:41-08:00.

If I override the fields and don't include createdDate then everything works as expected.

One would assume it relates to the supported date time formats here:

decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
if let date = formatter.date(from: dateStr) {
return date
}
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
if let date = formatter.date(from: dateStr) {
return date
}
throw APIProvider.Error.dateDecodingError(dateStr)
})
but unsure exactly why - if I can be of more help then please let me know!

Extension to APIProvider to use the API with Future (Combine framework)

I'm working on a SwiftUI App that uses AppStoreConnect-Swift-SDK with Combine.Future.

For that I have developed an extension to APIProvider that I can contribute back if you are interested.

Here is an extract:

/// Performs a data request to the given API endpoint using Combine.Future
///
/// - Parameter endpoint: The API endpoint to request.
/// - Returns: A Future that produces the requested T value when connected to a Subscriber. 
public func request<T: Decodable>(_ endpoint: APIEndpoint<T>) -> Future <T, Swift.Error> {
    return Future() { promise in
        self.request(endpoint) { (result) in
            promise(result)
        }
    }
}

There are also:

public func request(_ endpoint: APIEndpoint<Void>) -> Future <Void, Swift.Error>
public func request<T: Decodable>(_ resourceLinks: ResourceLinks<T>) -> Future <T, Swift.Error>

You can then get an item in a reactive way, for example the list of Apps:

let cancellable = apiProvider.request(.apps())
    .sink( receiveCompletion: { completion in
        switch completion {
        case .failure(let error):
            // handle error
        case .finished: ()
        }
    },
    receiveValue: { appsResponse in
        // do what you want with the apps
    })

Let me know if you want to integrate this, if so I'll make a PR with the extension and tests.

Replace hand crafted code with code generated from OpenAPI Spec

The AppStoreConnect API team have published a OpenAPI 3 spec (https://developer.apple.com/sample-code/app-store-connect/app-store-connect-openapi-specification.zip). It seems to me that instead of handcrafting a client SDK we should be using Swagger code-gen to produce a client based on this specification.

Thoughts?

My suggestion that this would be a major v2 release of this project as it would likely be a massively breaking change.

Not able to initialize the models (User)

What I still miss is to initialize the models from my app e.g. to hand the user object to the network layer for updating. Could you add public initializers to the API? What do you think?

I need this to have a single point for creating a dictionary from the user object:

let attributes = User.Attributes(firstName: firstName, lastName: lastName, roles: roles, provisioningAllowed: provisioningAllowed, allAppsVisible: allAppsVisible, email: email)
let user = User(attributes: attributes, type: .userInvitations)
let parameters = user.serialized()

I have to do this in 2 places: 1) when creating it (invitation) and 2) when updating a user. For 1) I create a user from its details and after this create a dictionary from it and for 2) I clone the existing user object, change some attributes and serialize this. That way I have the serialization code in one place. Also for the clone function I need to be able to initialize my models.

I solved this that way:

init(id: String = "", attributes: User.Attributes?, type: ResourceType) {
      self.id = id
      self.type = type
      self.links = ResourceLinks(self: URL(string: "www.dumy.com")!)
      self.attributes = attributes
      self.relationships = nil
}

One hint: I would create another enum from type like my ResourceType in the code above. This way it gets reusable.

Linux Support

To allow users to deploy the sdk on Linux servers, CommonCrypto needs to be replaced (or complemented) by a Crypto-Lib which is available on Linux (e.g OpenSSL).
Linux compatibility opens up another set of use cases, e.g. giving your SlackBot the skill to invite a new Testflight user, get Build information, etc …
Our company bot marvin - which lives in a linux environment - already uses the sdk with these changes schwmi#3. Nevertheless, if we want to merge these changes back in, we should integrate it more nuanced s.t. iOS+macOS still uses CommonCrypto and OpenSSL is only used in a Linux environment.

What do you think @AvdLee?

JWT Token Expiration

Hi!
Thanks for this SDK.

Sometimes AppStoreConnect API return 401 error with this json:
{ "errors": [{ "status": "401", "code": "NOT_AUTHORIZED", "title": "Authentication credentials are missing or invalid.", "detail": "Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens" }] }

I think it occurred during an API request when the time is close to expiration.
decodedBearer.expiryDate.compare(Date()) != ComparisonResult.orderedDescending
Maybe change this compare logic and create new token a little earlier? (ex. expiryDate - 5sec)

App Store Review Submissions

Guys, are there any clues on the new section of App Store Connect API - App Store Review Submissions? I think it is an essential functionality for this SDK. Is anyone working on this or I should consider contributing new endpoints?

how to support control Phased Release

we all know that App Store connect have a function is called "Release a version update in phases"
can you sdk support Release a version update in phases? thank you

Define endpoints as static on Endpoint for nicer syntax

We could end up with code like:

provider.request(.apps()) { (result) in
    switch result {
    case .success(let appsResponse):
        print("Did fetch \(appsResponse.data.count) apps")
    case .failure(let error):
        print("Something went wrong fetching the apps: \(error)")
    }
}

Instead of provider.request(Endpoints.apps())

enhancement: Support Phased Releases

https://developer.apple.com/documentation/appstoreconnectapi/app_store_version_phased_releases

For our use case we need to be able to load the phased release information for a given version of the app (the URL is available through a relationship link but no data).

GET https://api.appstoreconnect.apple.com/v1/appStoreVersions/UUID/appStoreVersionPhasedRelease

{
  "data": {
    "type": "appStoreVersionPhasedReleases",
    "id": "UUID",
    "attributes": {
      "phasedReleaseState": "ACTIVE",
      "startDate": "2020-12-14T10:16:45Z",
      "totalPauseDuration": 0,
      "currentDayNumber": 1
    },
    "links": {
      "self": "https://api.appstoreconnect.apple.com/v1/appStoreVersionPhasedReleases/UUID"
    }
  },
  "links": {
    "self": "https://api.appstoreconnect.apple.com/v1/appStoreVersions/UUID/appStoreVersionPhasedRelease"
  }
}

Q:Extending expire duration in JWTRequestsAuthenticator

Hi,

This is really helpful extension. I am currently working with pods.

I would like to set my expire duration instead of depending on pod expire duration.

It is set inside JWTRequestsAuthenticator

init(apiConfiguration: APIConfiguration) {
        self.apiConfiguration = apiConfiguration
        self.jwtCreator = JWT(keyIdentifier: apiConfiguration.privateKeyID, issuerIdentifier: apiConfiguration.issuerID, expireDuration: **60 * 20**)
    }

I would like to change 60 * 20 to something I want like 120 * 60.

/// The JWT Creator to use for creating the JWT token. Can be overriden for test use cases.
var jwtCreator: JWTCreatable

This lines says we can override for test cases but I would like to still use pods and just expose expireDuration property only instead of adding project manually into workspace.

Could you help me with this?

What is a requestGeneration error?

When I execute the following code, I get a requestGeneration error. It's not clear to me what that means.

		let configuration = APIConfiguration(issuerID: issuerID, privateKeyID: keyID, privateKey: key)
		let provider = APIProvider(configuration: configuration)
		provider.request(.apps())
		{ inResult in
			switch inResult
			{
				case .success(let appResponse):
					for app in appResponse.data
					{
						debugLog("App: \(app)")
					}
					
				case .failure(let e):
					debugLog("Error fetching apps: \(e)")
			}
		}

Disable certain SwiftLint warnings

We get several warnings for long class names, but we can't really do different for our models as the structure is just quite nice like it is now.

Warnings:

12 Warnings
⚠️ Capabilities for AppStoreConnect-Swift-SDK_Example may not function correctly because its entitlements use a placeholder team ID. To resolve this, select a development team in the build settings editor. (in target ‘AppStoreConnect-Swift-SDK_Example’)
⚠️ ld: directory not found for option ‘-F/Applications/Xcode-10.1.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/Developer/Library/Frameworks’
⚠️ ld:
⚠️ Sources/Models/AppEncryptionDeclarationAppLinkageResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘AppEncryptionDeclarationAppLinkageResponse’
⚠️ Sources/Models/AppEncryptionDeclarationBuildsLinkagesRequest.swift#L11 - Type name should be between 3 and 40 characters long: ‘AppEncryptionDeclarationBuildsLinkagesRequest’
⚠️ Sources/Models/BetaAppReviewSubmissionBuildLinkageResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘BetaAppReviewSubmissionBuildLinkageResponse’
⚠️ Sources/Models/BetaBuildLocalizationBuildLinkageResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘BetaBuildLocalizationBuildLinkageResponse’
⚠️ Sources/Models/BuildAppEncryptionDeclarationLinkageRequest.swift#L11 - Type name should be between 3 and 40 characters long: ‘BuildAppEncryptionDeclarationLinkageRequest’
⚠️ Sources/Models/BuildAppEncryptionDeclarationLinkageResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘BuildAppEncryptionDeclarationLinkageResponse’
⚠️ Sources/Models/BuildBetaAppReviewSubmissionLinkageResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘BuildBetaAppReviewSubmissionLinkageResponse’
⚠️ Sources/Models/BuildBetaBuildLocalizationsLinkagesResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘BuildBetaBuildLocalizationsLinkagesResponse’
⚠️ Sources/Models/UserInvitationVisibleAppsLinkagesResponse.swift#L11 - Type name should be between 3 and 40 characters long: ‘UserInvitationVisibleAppsLinkagesResponse’
1 Message
📖 Executed 4 tests, with 0 failures (0 unexpected) in 0.058 (0.123) seconds

response 403: don't have access, but I had agreed Program License Agreement.

Something went wrong fetching the apps: Request failed with status code 403 and response {
"errors": [{
"status": "403",
"code": "FORBIDDEN",
"title": "Access Unavailable",
"detail": "You currently don't have access to this resource. To resolve this issue, the Account Holder must agree to the latest Program License Agreement in their account on the Apple Developer website. https://developer.apple.com/account"
}]
}
).

Date decoding issue

Hi,

It seems that Apple has recently(?) changed the way it encodes dates on certain API objects.

For example in builds: "uploadedDate": "2019-08-14T10:02:14-07:00" (no change) but in betaGroups: "createdDate": "2019-07-16T09:31:04.677Z" (added milliseconds to the time).

Both are valid ISO8601 date formats, but it seems that the Swift4 implementation only supports a limited set of versions. See this stack overflow issue.

Now the APIProvider uses a reusable JSONDecoder with decoder.dateDecodingStrategy = .iso8601. As per the Swift4 limitation above, this fails on betaGroups.createdDate.

I tried to replace the decoding strategy with a custom date formatter:

        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        decoder.dateDecodingStrategy = .formatted(formatter)

it then works on betaGroups but fails on others like builds.

Solutions could be:

  1. Have different JSONDecoder with different dateDecodingStrategy depending on the API call.
  2. Overwrite the auto-generated Decodable for betaGroups so that createdDate gets decoded with the proper date formatter.
  3. Something else?

I am not familiar enough with the code to decide which is the best strategy.
I could work on a fix but I would need recommendation from the owners.

Also 1 & 2 are "hard coding" the current situation. It may be that Apple starts changing the other dates format in the future, so this would not survive another API change.
Ideally the fix should be at the Swift level (Swift5 maybe), where the date decoder should support all ISO8601 formats. I'm not sure how to do that either.

Testflight What to test

I’m trying to collect and update the so called “What to test” field of a Testflight build.

Here an example (sorry for the Italian): the field in the picture is “Elementi da testare”.

5B3D1DF9-C671-4D81-AF52-A029D7637F25

Could you please enlighten me about how to do that by your SDK ?

Thanks a lot in advance !!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.