mirror of
https://github.com/tappollo/WWDC.git
synced 2026-01-12 17:12:18 +08:00
Added API client, test server and related tests
This commit is contained in:
169
ConfCore/AppleAPIClient.swift
Normal file
169
ConfCore/AppleAPIClient.swift
Normal file
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// AppleAPIClient.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Siesta
|
||||
import SwiftyJSON
|
||||
|
||||
// MARK: - Initialization and configuration
|
||||
|
||||
final class AppleAPIClient {
|
||||
|
||||
fileprivate let environment: Environment
|
||||
fileprivate let service: Service
|
||||
|
||||
init(environment: Environment) {
|
||||
self.environment = environment
|
||||
self.service = Service(baseURL: environment.baseURL)
|
||||
|
||||
configureService()
|
||||
}
|
||||
|
||||
private let jsonParser = ResponseContentTransformer { JSON($0.content as AnyObject) }
|
||||
|
||||
private func configureService() {
|
||||
service.configure("**") { config in
|
||||
config.pipeline[.parsing].add(self.jsonParser, contentTypes: ["*/json"])
|
||||
}
|
||||
|
||||
service.configureTransformer(environment.newsPath) { [weak self] (entity: Entity<JSON>) throws -> [NewsItem]? in
|
||||
let json = entity.content as JSON
|
||||
|
||||
guard let newsItemsJson = json["items"].array else {
|
||||
throw APIError.adapter
|
||||
}
|
||||
|
||||
return try self?.failableAdaptCollection(newsItemsJson, using: NewsItemsJSONAdapter())
|
||||
}
|
||||
|
||||
service.configureTransformer(environment.sessionsPath) { [weak self] (entity: Entity<JSON>) throws -> ScheduleResponse? in
|
||||
let json = entity.content as JSON
|
||||
|
||||
return try self?.failableAdapt(json, using: ScheduleResponseAdapter())
|
||||
}
|
||||
|
||||
service.configureTransformer(environment.videosPath) { [weak self] (entity: Entity<JSON>) throws -> SessionsResponse? in
|
||||
let json = entity.content as JSON
|
||||
|
||||
return try self?.failableAdapt(json, using: SessionsResponseAdapter())
|
||||
}
|
||||
|
||||
service.configureTransformer(environment.liveVideosPath) { [weak self] (entity: Entity<JSON>) throws -> [SessionAsset]? in
|
||||
let json = entity.content as JSON
|
||||
|
||||
guard let sessionsDict = json["live_sessions"].dictionary else {
|
||||
throw APIError.adapter
|
||||
}
|
||||
|
||||
let sessionsArray = sessionsDict.flatMap { key, value -> JSON? in
|
||||
guard let id = JSON.init(rawValue: key) else { return nil }
|
||||
|
||||
var v = value
|
||||
|
||||
v["sessionId"] = id
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
return try self?.failableAdaptCollection(sessionsArray, using: LiveVideosAdapter())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Resources
|
||||
|
||||
fileprivate lazy var liveVideoAssets: Resource = {
|
||||
return self.service.resource(self.environment.liveVideosPath)
|
||||
}()
|
||||
|
||||
fileprivate lazy var sessions: Resource = {
|
||||
return self.service.resource(self.environment.videosPath)
|
||||
}()
|
||||
|
||||
fileprivate lazy var schedule: Resource = {
|
||||
return self.service.resource(self.environment.sessionsPath)
|
||||
}()
|
||||
|
||||
fileprivate lazy var news: Resource = {
|
||||
return self.service.resource(self.environment.newsPath)
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Standard API requests
|
||||
|
||||
extension AppleAPIClient {
|
||||
|
||||
func fetchLiveVideoAssets(completion: @escaping (Result<[SessionAsset], APIError>) -> Void) {
|
||||
liveVideoAssets.addObserver(owner: self) { [weak self] resource, event in
|
||||
self?.process(resource, event: event, with: completion)
|
||||
}.loadIfNeeded()
|
||||
}
|
||||
|
||||
func fetchSchedule(completion: @escaping (Result<ScheduleResponse, APIError>) -> Void) {
|
||||
schedule.addObserver(owner: self) { [weak self] resource, event in
|
||||
self?.process(resource, event: event, with: completion)
|
||||
}.loadIfNeeded()
|
||||
}
|
||||
|
||||
func fetchSessions(completion: @escaping (Result<SessionsResponse, APIError>) -> Void) {
|
||||
sessions.addObserver(owner: self) { [weak self] resource, event in
|
||||
self?.process(resource, event: event, with: completion)
|
||||
}.loadIfNeeded()
|
||||
}
|
||||
|
||||
func fetchNewsItems(completion: @escaping (Result<[NewsItem], APIError>) -> Void) {
|
||||
news.addObserver(owner: self) { [weak self] resource, event in
|
||||
self?.process(resource, event: event, with: completion)
|
||||
}.loadIfNeeded()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - API results processing
|
||||
|
||||
extension AppleAPIClient {
|
||||
|
||||
/// Convenience method to use a model adapter as a method that returns the model(s) or throws an error
|
||||
fileprivate func failableAdapt<A: Adapter, T>(_ input: JSON, using adapter: A) throws -> T where A.InputType == JSON, A.OutputType == T {
|
||||
let result = adapter.adapt(input)
|
||||
|
||||
switch result {
|
||||
case .error(let error):
|
||||
throw error
|
||||
case .success(let output):
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience method to use a model adapter as a method that returns the model(s) or throws an error
|
||||
fileprivate func failableAdaptCollection<A: Adapter, T>(_ input: [JSON], using adapter: A) throws -> [T] where A.InputType == JSON, A.OutputType == T {
|
||||
let result = adapter.adapt(input)
|
||||
|
||||
switch result {
|
||||
case .error(let error):
|
||||
throw error
|
||||
case .success(let output):
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func process<M>(_ resource: Resource, event: ResourceEvent, with completion: @escaping (Result<M, APIError>) -> ()) {
|
||||
switch event {
|
||||
case .error:
|
||||
completion(.error(resource.error))
|
||||
case .newData(_), .notModified:
|
||||
if let results: M = resource.typedContent() {
|
||||
completion(.success(results))
|
||||
} else {
|
||||
completion(.error(.adapter))
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
29
ConfCore/Environment.swift
Normal file
29
ConfCore/Environment.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Environment.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Environment {
|
||||
|
||||
let baseURL: String
|
||||
let videosPath: String
|
||||
let sessionsPath: String
|
||||
let newsPath: String
|
||||
let liveVideosPath: String
|
||||
|
||||
}
|
||||
|
||||
extension Environment {
|
||||
|
||||
static let test = Environment(baseURL: "http://localhost:9042",
|
||||
videosPath: "/videos.json",
|
||||
sessionsPath: "/sessions.json",
|
||||
newsPath: "/news.json",
|
||||
liveVideosPath: "/videos_live.json")
|
||||
|
||||
}
|
||||
39
ConfCore/Resource+Error.swift
Normal file
39
ConfCore/Resource+Error.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Resource+Error.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Siesta
|
||||
|
||||
enum APIError: Error {
|
||||
case http(Error)
|
||||
case adapter
|
||||
case unknown
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .http(let error):
|
||||
return error.localizedDescription
|
||||
case .adapter:
|
||||
return "Unable to process the data returned by the server"
|
||||
case .unknown:
|
||||
return "An unknown networking error occurred"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Resource {
|
||||
|
||||
var error: APIError {
|
||||
if let underlyingError = self.latestError {
|
||||
return .http(underlyingError)
|
||||
} else {
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
ConfCore/ScheduleResponse.swift
Normal file
17
ConfCore/ScheduleResponse.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// ScheduleResponse.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ScheduleResponse {
|
||||
|
||||
let rooms: [Room]
|
||||
let tracks: [Track]
|
||||
let instances: [SessionInstance]
|
||||
|
||||
}
|
||||
57
ConfCore/ScheduleResponseAdapter.swift
Normal file
57
ConfCore/ScheduleResponseAdapter.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// ScheduleResponseAdapter.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
private enum ScheduleKeys: String, JSONSubscriptType {
|
||||
case response, rooms, tracks, sessions
|
||||
|
||||
var jsonKey: JSONKey {
|
||||
return JSONKey.key(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
final class ScheduleResponseAdapter: Adapter {
|
||||
|
||||
typealias InputType = JSON
|
||||
typealias OutputType = ScheduleResponse
|
||||
|
||||
func adapt(_ input: JSON) -> Result<ScheduleResponse, AdapterError> {
|
||||
guard let roomsJson = input[ScheduleKeys.response][ScheduleKeys.rooms].array else {
|
||||
return .error(.missingKey(ScheduleKeys.rooms))
|
||||
}
|
||||
|
||||
guard let tracksJson = input[ScheduleKeys.response][ScheduleKeys.tracks].array else {
|
||||
return .error(.missingKey(ScheduleKeys.rooms))
|
||||
}
|
||||
|
||||
guard let instancesJson = input[ScheduleKeys.response][ScheduleKeys.sessions].array else {
|
||||
return .error(.missingKey(ScheduleKeys.rooms))
|
||||
}
|
||||
|
||||
guard case .success(let rooms) = RoomsJSONAdapter().adapt(roomsJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
guard case .success(let tracks) = TracksJSONAdapter().adapt(tracksJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
guard case .success(let instances) = SessionInstancesJSONAdapter().adapt(instancesJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
let response = ScheduleResponse(rooms: rooms,
|
||||
tracks: tracks,
|
||||
instances: instances)
|
||||
|
||||
return .success(response)
|
||||
}
|
||||
|
||||
}
|
||||
17
ConfCore/SessionsResponse.swift
Normal file
17
ConfCore/SessionsResponse.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// SessionsResponse.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SessionsResponse {
|
||||
|
||||
let events: [Event]
|
||||
let sessions: [Session]
|
||||
let assets: [SessionAsset]
|
||||
|
||||
}
|
||||
51
ConfCore/SessionsResponseAdapter.swift
Normal file
51
ConfCore/SessionsResponseAdapter.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// SessionsResponseAdapter.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
private enum SessionResponseKeys: String, JSONSubscriptType {
|
||||
case events, sessions
|
||||
|
||||
var jsonKey: JSONKey {
|
||||
return JSONKey.key(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
final class SessionsResponseAdapter: Adapter {
|
||||
|
||||
typealias InputType = JSON
|
||||
typealias OutputType = SessionsResponse
|
||||
|
||||
func adapt(_ input: JSON) -> Result<SessionsResponse, AdapterError> {
|
||||
guard let eventsJson = input[SessionResponseKeys.events].array else {
|
||||
return .error(.missingKey(SessionResponseKeys.events))
|
||||
}
|
||||
|
||||
guard let sessionsJson = input[SessionResponseKeys.sessions].array else {
|
||||
return .error(.missingKey(SessionResponseKeys.sessions))
|
||||
}
|
||||
|
||||
guard case .success(let events) = EventsJSONAdapter().adapt(eventsJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
guard case .success(let sessions) = SessionsJSONAdapter().adapt(sessionsJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
guard case .success(let assets) = SessionAssetsJSONAdapter().adapt(sessionsJson) else {
|
||||
return .error(.invalidData)
|
||||
}
|
||||
|
||||
let response = SessionsResponse(events: events, sessions: sessions, assets: assets.flatMap({$0}))
|
||||
|
||||
return .success(response)
|
||||
}
|
||||
|
||||
}
|
||||
88
ConfCoreTests/NetworkingTests.swift
Normal file
88
ConfCoreTests/NetworkingTests.swift
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// NetworkingTests.swift
|
||||
// WWDC
|
||||
//
|
||||
// Created by Guilherme Rambo on 21/02/17.
|
||||
// Copyright © 2017 Guilherme Rambo. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import ConfCore
|
||||
|
||||
class NetworkingTests: XCTestCase {
|
||||
|
||||
let client = AppleAPIClient(environment: .test)
|
||||
|
||||
func testNewsItemEndpoint() {
|
||||
let exp = expectation(description: "News items response")
|
||||
|
||||
client.fetchNewsItems { result in
|
||||
switch result {
|
||||
case .error(let error):
|
||||
XCTFail(error.localizedDescription)
|
||||
case .success(let items):
|
||||
XCTAssertEqual(items.count, 16)
|
||||
}
|
||||
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 10)
|
||||
}
|
||||
|
||||
func testScheduleEndpoint() {
|
||||
let exp = expectation(description: "Schedule response")
|
||||
|
||||
client.fetchSchedule { result in
|
||||
switch result {
|
||||
case .error(let error):
|
||||
XCTFail(error.localizedDescription)
|
||||
case .success(let response):
|
||||
XCTAssertEqual(response.tracks.count, 8)
|
||||
XCTAssertEqual(response.rooms.count, 29)
|
||||
XCTAssertEqual(response.instances.count, 316)
|
||||
}
|
||||
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 10)
|
||||
}
|
||||
|
||||
func testSessionsAndAssetsEndpoint() {
|
||||
let exp = expectation(description: "Sessions and assets response")
|
||||
|
||||
client.fetchSessions { result in
|
||||
switch result {
|
||||
case .error(let error):
|
||||
XCTFail(error.localizedDescription)
|
||||
case .success(let response):
|
||||
XCTAssertEqual(response.events.count, 5)
|
||||
XCTAssertEqual(response.sessions.count, 550)
|
||||
XCTAssertEqual(response.assets.count, 2947)
|
||||
}
|
||||
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 10)
|
||||
}
|
||||
|
||||
func testLiveVideosEndpoint() {
|
||||
let exp = expectation(description: "Live videos response")
|
||||
|
||||
client.fetchLiveVideoAssets { result in
|
||||
switch result {
|
||||
case .error(let error):
|
||||
XCTFail(error.localizedDescription)
|
||||
case .success(let assets):
|
||||
XCTAssertEqual(assets.count, 111)
|
||||
}
|
||||
|
||||
exp.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 10)
|
||||
}
|
||||
|
||||
}
|
||||
13
Fixtures/testserver.sh
Executable file
13
Fixtures/testserver.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! type "npm" > /dev/null; then
|
||||
echo "NPM is not installed, installing...\n"
|
||||
curl -L https://www.npmjs.com/install.sh | sh
|
||||
fi
|
||||
|
||||
if ! type "http-server" > /dev/null; then
|
||||
echo "http-server module is not installed, installing...\n"
|
||||
npm install http-server -g
|
||||
fi
|
||||
|
||||
http-server -p 9042 ./json
|
||||
@@ -69,6 +69,14 @@
|
||||
DDAE7C4D1E5BA4D100CEA205 /* TranscriptsJSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C4C1E5BA4D100CEA205 /* TranscriptsJSONAdapter.swift */; };
|
||||
DDAE7C4F1E5BA4E300CEA205 /* transcript.json in Resources */ = {isa = PBXBuildFile; fileRef = DDAE7C4E1E5BA4E300CEA205 /* transcript.json */; };
|
||||
DDAE7C551E5BC6CD00CEA205 /* Siesta.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDAE7C541E5BC6CD00CEA205 /* Siesta.framework */; };
|
||||
DDAE7C581E5C629800CEA205 /* AppleAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C571E5C629800CEA205 /* AppleAPIClient.swift */; };
|
||||
DDAE7C5A1E5C652600CEA205 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C591E5C652600CEA205 /* Environment.swift */; };
|
||||
DDAE7C5C1E5C707A00CEA205 /* NetworkingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C5B1E5C707A00CEA205 /* NetworkingTests.swift */; };
|
||||
DDAE7C5E1E5C723A00CEA205 /* Resource+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C5D1E5C723A00CEA205 /* Resource+Error.swift */; };
|
||||
DDAE7C601E5C7E3400CEA205 /* ScheduleResponseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C5F1E5C7E3400CEA205 /* ScheduleResponseAdapter.swift */; };
|
||||
DDAE7C631E5C7E7800CEA205 /* ScheduleResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C621E5C7E7800CEA205 /* ScheduleResponse.swift */; };
|
||||
DDAE7C651E5CA81C00CEA205 /* SessionsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C641E5CA81C00CEA205 /* SessionsResponse.swift */; };
|
||||
DDAE7C6B1E5CA97A00CEA205 /* SessionsResponseAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAE7C6A1E5CA97A00CEA205 /* SessionsResponseAdapter.swift */; };
|
||||
DDD3797D1E4AB655005FE876 /* TracksJSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD3797C1E4AB655005FE876 /* TracksJSONAdapter.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -191,6 +199,14 @@
|
||||
DDAE7C4E1E5BA4E300CEA205 /* transcript.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = transcript.json; sourceTree = "<group>"; };
|
||||
DDAE7C541E5BC6CD00CEA205 /* Siesta.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Siesta.framework; path = Carthage/Build/Mac/Siesta.framework; sourceTree = "<group>"; };
|
||||
DDAE7C561E5BC6E300CEA205 /* Siesta.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Siesta.framework.dSYM; path = Carthage/Build/Mac/Siesta.framework.dSYM; sourceTree = "<group>"; };
|
||||
DDAE7C571E5C629800CEA205 /* AppleAPIClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleAPIClient.swift; sourceTree = "<group>"; };
|
||||
DDAE7C591E5C652600CEA205 /* Environment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
|
||||
DDAE7C5B1E5C707A00CEA205 /* NetworkingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkingTests.swift; sourceTree = "<group>"; };
|
||||
DDAE7C5D1E5C723A00CEA205 /* Resource+Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Resource+Error.swift"; sourceTree = "<group>"; };
|
||||
DDAE7C5F1E5C7E3400CEA205 /* ScheduleResponseAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduleResponseAdapter.swift; sourceTree = "<group>"; };
|
||||
DDAE7C621E5C7E7800CEA205 /* ScheduleResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduleResponse.swift; sourceTree = "<group>"; };
|
||||
DDAE7C641E5CA81C00CEA205 /* SessionsResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionsResponse.swift; sourceTree = "<group>"; };
|
||||
DDAE7C6A1E5CA97A00CEA205 /* SessionsResponseAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionsResponseAdapter.swift; sourceTree = "<group>"; };
|
||||
DDD3797C1E4AB655005FE876 /* TracksJSONAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TracksJSONAdapter.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -323,6 +339,7 @@
|
||||
DD5DEC231E4AA8C800426FA6 /* Fixtures */,
|
||||
DD36A4D71E478D7E00B2EA88 /* DatabaseTests.swift */,
|
||||
DD34DA061E4AB0E400F25C45 /* AdapterTests.swift */,
|
||||
DDAE7C5B1E5C707A00CEA205 /* NetworkingTests.swift */,
|
||||
DD36A4D91E478D7E00B2EA88 /* Info.plist */,
|
||||
);
|
||||
path = ConfCoreTests;
|
||||
@@ -331,20 +348,10 @@
|
||||
DD5DEC1C1E4AA68F00426FA6 /* Adapters */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD5DEC1F1E4AA6F800426FA6 /* Adapter.swift */,
|
||||
DD5DEC2E1E4AAC9300426FA6 /* DateAdapter.swift */,
|
||||
DD5DEC211E4AA7F100426FA6 /* EventsJSONAdapter.swift */,
|
||||
DD54CD131E4AC21F000BF5F2 /* SessionAssetsAdapter.swift */,
|
||||
DD6477301E55231A00386D93 /* LiveVideosAdapter.swift */,
|
||||
DD34DA0A1E4AB3E400F25C45 /* RoomsJSONAdapter.swift */,
|
||||
DDD3797C1E4AB655005FE876 /* TracksJSONAdapter.swift */,
|
||||
DD54CD0F1E4AB8ED000BF5F2 /* KeywordsJSONAdapter.swift */,
|
||||
DD54CD111E4AB95F000BF5F2 /* FocusesJSONAdapter.swift */,
|
||||
DD6477321E5535DE00386D93 /* SessionsJSONAdapter.swift */,
|
||||
DD0353481E5545B300D5E343 /* SessionInstancesJSONAdapter.swift */,
|
||||
DD3C4D281E5622950093BBD0 /* PhotosJSONAdapter.swift */,
|
||||
DD3C4D2A1E564AE80093BBD0 /* NewsItemsJSONAdapter.swift */,
|
||||
DDAE7C4C1E5BA4D100CEA205 /* TranscriptsJSONAdapter.swift */,
|
||||
DDAE7C681E5CA84B00CEA205 /* Apple */,
|
||||
DDAE7C671E5CA84200CEA205 /* Base */,
|
||||
DDAE7C691E5CA85900CEA205 /* ASCIIWWDC */,
|
||||
DDAE7C661E5CA83500CEA205 /* Responses */,
|
||||
);
|
||||
name = Adapters;
|
||||
sourceTree = "<group>";
|
||||
@@ -405,6 +412,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD5DEC1D1E4AA6B800426FA6 /* Result.swift */,
|
||||
DDAE7C611E5C7E6A00CEA205 /* Responses */,
|
||||
DD65FD6E1E48BE2F0054DD35 /* Apple */,
|
||||
DD65FD6F1E48BE400054DD35 /* ASCIIWWDC */,
|
||||
DD65FD701E48BE480054DD35 /* UserDefined */,
|
||||
@@ -451,6 +459,9 @@
|
||||
DDAE7C521E5BC6B000CEA205 /* Networking */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDAE7C5D1E5C723A00CEA205 /* Resource+Error.swift */,
|
||||
DDAE7C591E5C652600CEA205 /* Environment.swift */,
|
||||
DDAE7C571E5C629800CEA205 /* AppleAPIClient.swift */,
|
||||
);
|
||||
name = Networking;
|
||||
sourceTree = "<group>";
|
||||
@@ -462,6 +473,59 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAE7C611E5C7E6A00CEA205 /* Responses */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDAE7C621E5C7E7800CEA205 /* ScheduleResponse.swift */,
|
||||
DDAE7C641E5CA81C00CEA205 /* SessionsResponse.swift */,
|
||||
);
|
||||
name = Responses;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAE7C661E5CA83500CEA205 /* Responses */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDAE7C5F1E5C7E3400CEA205 /* ScheduleResponseAdapter.swift */,
|
||||
DDAE7C6A1E5CA97A00CEA205 /* SessionsResponseAdapter.swift */,
|
||||
);
|
||||
name = Responses;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAE7C671E5CA84200CEA205 /* Base */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD5DEC1F1E4AA6F800426FA6 /* Adapter.swift */,
|
||||
DD5DEC2E1E4AAC9300426FA6 /* DateAdapter.swift */,
|
||||
);
|
||||
name = Base;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAE7C681E5CA84B00CEA205 /* Apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD5DEC211E4AA7F100426FA6 /* EventsJSONAdapter.swift */,
|
||||
DD54CD131E4AC21F000BF5F2 /* SessionAssetsAdapter.swift */,
|
||||
DD6477301E55231A00386D93 /* LiveVideosAdapter.swift */,
|
||||
DD34DA0A1E4AB3E400F25C45 /* RoomsJSONAdapter.swift */,
|
||||
DDD3797C1E4AB655005FE876 /* TracksJSONAdapter.swift */,
|
||||
DD54CD0F1E4AB8ED000BF5F2 /* KeywordsJSONAdapter.swift */,
|
||||
DD54CD111E4AB95F000BF5F2 /* FocusesJSONAdapter.swift */,
|
||||
DD6477321E5535DE00386D93 /* SessionsJSONAdapter.swift */,
|
||||
DD0353481E5545B300D5E343 /* SessionInstancesJSONAdapter.swift */,
|
||||
DD3C4D281E5622950093BBD0 /* PhotosJSONAdapter.swift */,
|
||||
DD3C4D2A1E564AE80093BBD0 /* NewsItemsJSONAdapter.swift */,
|
||||
);
|
||||
name = Apple;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAE7C691E5CA85900CEA205 /* ASCIIWWDC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDAE7C4C1E5BA4D100CEA205 /* TranscriptsJSONAdapter.swift */,
|
||||
);
|
||||
name = ASCIIWWDC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -654,6 +718,9 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DD5DEC2F1E4AAC9300426FA6 /* DateAdapter.swift in Sources */,
|
||||
DDAE7C5E1E5C723A00CEA205 /* Resource+Error.swift in Sources */,
|
||||
DDAE7C5A1E5C652600CEA205 /* Environment.swift in Sources */,
|
||||
DDAE7C651E5CA81C00CEA205 /* SessionsResponse.swift in Sources */,
|
||||
DD65FD591E48A0C90054DD35 /* Photo.swift in Sources */,
|
||||
DD65FD6B1E48BD9F0054DD35 /* Favorite.swift in Sources */,
|
||||
DD65FD511E48A05C0054DD35 /* Session.swift in Sources */,
|
||||
@@ -661,12 +728,15 @@
|
||||
DD65FD531E48A06A0054DD35 /* NewsItem.swift in Sources */,
|
||||
DDD3797D1E4AB655005FE876 /* TracksJSONAdapter.swift in Sources */,
|
||||
DD65FD5D1E48A11F0054DD35 /* Track.swift in Sources */,
|
||||
DDAE7C631E5C7E7800CEA205 /* ScheduleResponse.swift in Sources */,
|
||||
DD5DEC201E4AA6F800426FA6 /* Adapter.swift in Sources */,
|
||||
DD5DEC221E4AA7F100426FA6 /* EventsJSONAdapter.swift in Sources */,
|
||||
DDAE7C601E5C7E3400CEA205 /* ScheduleResponseAdapter.swift in Sources */,
|
||||
DD65FD6D1E48BE270054DD35 /* Bookmark.swift in Sources */,
|
||||
DD5DEC1E1E4AA6B800426FA6 /* Result.swift in Sources */,
|
||||
DD65FD5B1E48A1080054DD35 /* Focus.swift in Sources */,
|
||||
DD54CD141E4AC21F000BF5F2 /* SessionAssetsAdapter.swift in Sources */,
|
||||
DDAE7C581E5C629800CEA205 /* AppleAPIClient.swift in Sources */,
|
||||
DD3C4D291E5622950093BBD0 /* PhotosJSONAdapter.swift in Sources */,
|
||||
DD65FD671E48BAAD0054DD35 /* Transcript.swift in Sources */,
|
||||
DD0353491E5545B300D5E343 /* SessionInstancesJSONAdapter.swift in Sources */,
|
||||
@@ -680,6 +750,7 @@
|
||||
DD65FD5F1E48A3900054DD35 /* SessionInstance.swift in Sources */,
|
||||
DD65FD571E48A0BD0054DD35 /* Event.swift in Sources */,
|
||||
DD6477311E55231A00386D93 /* LiveVideosAdapter.swift in Sources */,
|
||||
DDAE7C6B1E5CA97A00CEA205 /* SessionsResponseAdapter.swift in Sources */,
|
||||
DD65FD611E48A86A0054DD35 /* PhotoRepresentation.swift in Sources */,
|
||||
DD6477331E5535DE00386D93 /* SessionsJSONAdapter.swift in Sources */,
|
||||
DD54CD121E4AB95F000BF5F2 /* FocusesJSONAdapter.swift in Sources */,
|
||||
@@ -691,6 +762,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DD36A4D81E478D7E00B2EA88 /* DatabaseTests.swift in Sources */,
|
||||
DDAE7C5C1E5C707A00CEA205 /* NetworkingTests.swift in Sources */,
|
||||
DD34DA071E4AB0E400F25C45 /* AdapterTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -28,5 +28,10 @@
|
||||
<string>Main</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Reference in New Issue
Block a user