diff --git a/.gitignore b/.gitignore index dcc5b36..cf8eed7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ /yarn-error.log .byebug_history + +Carthage/ + diff --git a/platforms/ios/Recipes/Cartfile b/platforms/ios/Recipes/Cartfile new file mode 100644 index 0000000..a3fa451 --- /dev/null +++ b/platforms/ios/Recipes/Cartfile @@ -0,0 +1 @@ +github "turbolinks/turbolinks-ios" "master" diff --git a/platforms/ios/Recipes/Cartfile.resolved b/platforms/ios/Recipes/Cartfile.resolved new file mode 100644 index 0000000..4c3ffd1 --- /dev/null +++ b/platforms/ios/Recipes/Cartfile.resolved @@ -0,0 +1 @@ +github "turbolinks/turbolinks-ios" "1d6e267dd6a60faed8e51d2cbf893bd7d7851538" diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Headers/Turbolinks-Swift.h b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Headers/Turbolinks-Swift.h new file mode 100644 index 0000000..29414b1 --- /dev/null +++ b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Headers/Turbolinks-Swift.h @@ -0,0 +1,215 @@ +// Generated by Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42) +#pragma clang diagnostic push + +#if defined(__has_include) && __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#include +#include +#include +#include + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if defined(__has_include) && __has_include() +# include +# elif !defined(__cplusplus) || __cplusplus < 201103L +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif + +#if defined(__has_attribute) && __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +#else +# define SWIFT_RUNTIME_NAME(X) +#endif +#if defined(__has_attribute) && __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +#else +# define SWIFT_COMPILE_NAME(X) +#endif +#if defined(__has_attribute) && __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +#else +# define SWIFT_METHOD_FAMILY(X) +#endif +#if defined(__has_attribute) && __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +#else +# define SWIFT_NOESCAPE +#endif +#if defined(__has_attribute) && __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +# define SWIFT_WARN_UNUSED_RESULT +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif + +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif + +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif + +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if defined(__has_attribute) && __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM) +# define SWIFT_ENUM(_type, _name) enum _name : _type _name; enum SWIFT_ENUM_EXTRA _name : _type +# if defined(__has_feature) && __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) SWIFT_ENUM(_type, _name) +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if defined(__has_feature) && __has_feature(modules) +@import Foundation; +@import ObjectiveC; +@import WebKit; +@import UIKit; +@import CoreGraphics; +#endif + +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" + +@interface NSError (SWIFT_EXTENSION(Turbolinks)) +@end + +@class WKWebView; +@class WKWebViewConfiguration; + +SWIFT_CLASS("_TtC10Turbolinks7Session") +@interface Session : NSObject +@property (nonatomic, readonly, strong) WKWebView * _Nonnull webView; +- (nonnull instancetype)initWithWebViewConfiguration:(WKWebViewConfiguration * _Nonnull)webViewConfiguration OBJC_DESIGNATED_INITIALIZER; +- (nonnull instancetype)init; +- (void)reload; +@end + +@class WKNavigationAction; + +@interface Session (SWIFT_EXTENSION(Turbolinks)) +- (void)webView:(WKWebView * _Nonnull)webView decidePolicyForNavigationAction:(WKNavigationAction * _Nonnull)navigationAction decisionHandler:(void (^ _Nonnull)(WKNavigationActionPolicy))decisionHandler; +@end + + +@interface Session (SWIFT_EXTENSION(Turbolinks)) +@end + + +@interface Session (SWIFT_EXTENSION(Turbolinks)) +@end + + +@interface Session (SWIFT_EXTENSION(Turbolinks)) +@end + +@class NSCoder; +@class UIRefreshControl; +@class UIActivityIndicatorView; + +SWIFT_CLASS("_TtC10Turbolinks13VisitableView") +@interface VisitableView : UIView +- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER; +- (nonnull instancetype)initWithFrame:(CGRect)frame OBJC_DESIGNATED_INITIALIZER; +@property (nonatomic, strong) WKWebView * _Nullable webView; +- (void)deactivateWebView; +@property (nonatomic, strong) UIRefreshControl * _Nonnull refreshControl; +@property (nonatomic) BOOL allowsPullToRefresh; +@property (nonatomic, readonly) BOOL isRefreshing; +@property (nonatomic, strong) UIActivityIndicatorView * _Nonnull activityIndicatorView; +- (void)showActivityIndicator; +- (void)hideActivityIndicator; +- (void)updateScreenshot; +- (void)showScreenshot; +- (void)hideScreenshot; +- (void)clearScreenshot; +- (void)layoutSubviews; +@end + +@class NSBundle; + +SWIFT_CLASS("_TtC10Turbolinks23VisitableViewController") +@interface VisitableViewController : UIViewController +@property (nonatomic, copy) NSURL * _Null_unspecified visitableURL; +- (nonnull instancetype)initWithUrl:(NSURL * _Nonnull)url; +@property (nonatomic, readonly, strong) VisitableView * _Null_unspecified visitableView; +- (void)visitableDidRender; +- (void)viewDidLoad; +- (void)viewWillAppear:(BOOL)animated; +- (void)viewDidAppear:(BOOL)animated; +- (nonnull instancetype)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil OBJC_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER; +@end + +#pragma clang diagnostic pop diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Info.plist b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Info.plist new file mode 100644 index 0000000..aa4fc33 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Info.plist differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftdoc b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftdoc new file mode 100644 index 0000000..5188024 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftdoc differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftmodule b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftmodule new file mode 100644 index 0000000..6f2706c Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm.swiftmodule differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftdoc b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftdoc new file mode 100644 index 0000000..2491584 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftdoc differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftmodule b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftmodule new file mode 100644 index 0000000..1732373 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/arm64.swiftmodule differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftdoc b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftdoc new file mode 100644 index 0000000..f91ddbc Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftdoc differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftmodule b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftmodule new file mode 100644 index 0000000..00b1dc3 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/i386.swiftmodule differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftdoc b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftdoc new file mode 100644 index 0000000..2572cbb Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftdoc differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftmodule b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftmodule new file mode 100644 index 0000000..ae023e5 Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/Turbolinks.swiftmodule/x86_64.swiftmodule differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/module.modulemap b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/module.modulemap new file mode 100644 index 0000000..b2d0a89 --- /dev/null +++ b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module Turbolinks { + header "Turbolinks-Swift.h" +} diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Turbolinks b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Turbolinks new file mode 100755 index 0000000..b00622f Binary files /dev/null and b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/Turbolinks differ diff --git a/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/WebView.js b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/WebView.js new file mode 100644 index 0000000..9c7755d --- /dev/null +++ b/platforms/ios/Recipes/Carthage/Build/iOS/Turbolinks.framework/WebView.js @@ -0,0 +1,115 @@ +(function() { + function WebView(controller, messageHandler) { + this.controller = controller + this.messageHandler = messageHandler + controller.adapter = this + } + + WebView.prototype = { + pageLoaded: function() { + var restorationIdentifier = this.controller.restorationIdentifier + this.postMessageAfterNextRepaint("pageLoaded", { restorationIdentifier: restorationIdentifier }) + }, + + errorRaised: function(error) { + this.postMessage("errorRaised", { error: error }) + }, + + visitLocationWithActionAndRestorationIdentifier: function(location, action, restorationIdentifier) { + this.controller.startVisitToLocationWithAction(location, action, restorationIdentifier) + }, + + // Current visit + + issueRequestForVisitWithIdentifier: function(identifier) { + if (identifier == this.currentVisit.identifier) { + this.currentVisit.issueRequest() + } + }, + + changeHistoryForVisitWithIdentifier: function(identifier) { + if (identifier == this.currentVisit.identifier) { + this.currentVisit.changeHistory() + } + }, + + loadCachedSnapshotForVisitWithIdentifier: function(identifier) { + if (identifier == this.currentVisit.identifier) { + this.currentVisit.loadCachedSnapshot() + } + }, + + loadResponseForVisitWithIdentifier: function(identifier) { + if (identifier == this.currentVisit.identifier) { + this.currentVisit.loadResponse() + } + }, + + cancelVisitWithIdentifier: function(identifier) { + if (identifier == this.currentVisit.identifier) { + this.currentVisit.cancel() + } + }, + + // Adapter interface + + visitProposedToLocationWithAction: function(location, action) { + this.postMessage("visitProposed", { location: location.absoluteURL, action: action }) + }, + + visitStarted: function(visit) { + this.currentVisit = visit + this.postMessage("visitStarted", { identifier: visit.identifier, hasCachedSnapshot: visit.hasCachedSnapshot() }) + }, + + visitRequestStarted: function(visit) { + this.postMessage("visitRequestStarted", { identifier: visit.identifier }) + }, + + visitRequestCompleted: function(visit) { + this.postMessage("visitRequestCompleted", { identifier: visit.identifier }) + }, + + visitRequestFailedWithStatusCode: function(visit, statusCode) { + this.postMessage("visitRequestFailed", { identifier: visit.identifier, statusCode: statusCode }) + }, + + visitRequestFinished: function(visit) { + this.postMessage("visitRequestFinished", { identifier: visit.identifier }) + }, + + visitRendered: function(visit) { + this.postMessageAfterNextRepaint("visitRendered", { identifier: visit.identifier }) + }, + + visitCompleted: function(visit) { + this.postMessageAfterNextRepaint("visitCompleted", { identifier: visit.identifier, restorationIdentifier: visit.restorationIdentifier }) + }, + + pageInvalidated: function() { + this.postMessage("pageInvalidated") + }, + + // Private + + postMessage: function(name, data) { + this.messageHandler.postMessage({ name: name, data: data || {} }) + }, + + postMessageAfterNextRepaint: function(name, data) { + var postMessage = this.postMessage.bind(this, name, data) + requestAnimationFrame(function() { + requestAnimationFrame(postMessage) + }) + } + } + + this.webView = new WebView(Turbolinks.controller, webkit.messageHandlers.turbolinks) + + addEventListener("error", function(event) { + var error = event.message + " (" + event.filename + ":" + event.lineno + ":" + event.colno + ")" + webView.errorRaised(error) + }, false) + + webView.pageLoaded() +})() diff --git a/platforms/ios/Recipes/Recipes.xcodeproj/project.pbxproj b/platforms/ios/Recipes/Recipes.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d6b282f --- /dev/null +++ b/platforms/ios/Recipes/Recipes.xcodeproj/project.pbxproj @@ -0,0 +1,590 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + BB76DF541F6F600300A1DAA3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF531F6F600300A1DAA3 /* AppDelegate.swift */; }; + BB76DF561F6F600300A1DAA3 /* ApplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF551F6F600300A1DAA3 /* ApplicationController.swift */; }; + BB76DF591F6F600300A1DAA3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB76DF571F6F600300A1DAA3 /* Main.storyboard */; }; + BB76DF5B1F6F600300A1DAA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB76DF5A1F6F600300A1DAA3 /* Assets.xcassets */; }; + BB76DF5E1F6F600300A1DAA3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB76DF5C1F6F600300A1DAA3 /* LaunchScreen.storyboard */; }; + BB76DF691F6F600300A1DAA3 /* RecipesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF681F6F600300A1DAA3 /* RecipesTests.swift */; }; + BB76DF741F6F600300A1DAA3 /* RecipesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF731F6F600300A1DAA3 /* RecipesUITests.swift */; }; + BB76DF831F6F62E200A1DAA3 /* Turbolinks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB76DF821F6F62E200A1DAA3 /* Turbolinks.framework */; }; + BB76DF851F6F630A00A1DAA3 /* Turbolinks.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB76DF821F6F62E200A1DAA3 /* Turbolinks.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BB76DF871F6F644D00A1DAA3 /* VisitableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF861F6F644D00A1DAA3 /* VisitableViewController.swift */; }; + BB76DF8B1F6F649300A1DAA3 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF881F6F649300A1DAA3 /* Error.swift */; }; + BB76DF8C1F6F649300A1DAA3 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF891F6F649300A1DAA3 /* ErrorView.swift */; }; + BB76DF8D1F6F649300A1DAA3 /* ErrorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB76DF8A1F6F649300A1DAA3 /* ErrorView.xib */; }; + BB76DF8F1F6F64CD00A1DAA3 /* AuthenticationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB76DF8E1F6F64CD00A1DAA3 /* AuthenticationController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BB76DF651F6F600300A1DAA3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BB76DF481F6F600300A1DAA3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BB76DF4F1F6F600300A1DAA3; + remoteInfo = Recipes; + }; + BB76DF701F6F600300A1DAA3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BB76DF481F6F600300A1DAA3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BB76DF4F1F6F600300A1DAA3; + remoteInfo = Recipes; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BB76DF841F6F62F700A1DAA3 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + BB76DF851F6F630A00A1DAA3 /* Turbolinks.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BB76DF501F6F600300A1DAA3 /* Recipes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Recipes.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BB76DF531F6F600300A1DAA3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + BB76DF551F6F600300A1DAA3 /* ApplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationController.swift; sourceTree = ""; }; + BB76DF581F6F600300A1DAA3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + BB76DF5A1F6F600300A1DAA3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BB76DF5D1F6F600300A1DAA3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BB76DF5F1F6F600300A1DAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BB76DF641F6F600300A1DAA3 /* RecipesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RecipesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BB76DF681F6F600300A1DAA3 /* RecipesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipesTests.swift; sourceTree = ""; }; + BB76DF6A1F6F600300A1DAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BB76DF6F1F6F600300A1DAA3 /* RecipesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RecipesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BB76DF731F6F600300A1DAA3 /* RecipesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipesUITests.swift; sourceTree = ""; }; + BB76DF751F6F600300A1DAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BB76DF821F6F62E200A1DAA3 /* Turbolinks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Turbolinks.framework; path = Carthage/Build/iOS/Turbolinks.framework; sourceTree = ""; }; + BB76DF861F6F644D00A1DAA3 /* VisitableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisitableViewController.swift; sourceTree = ""; }; + BB76DF881F6F649300A1DAA3 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; + BB76DF891F6F649300A1DAA3 /* ErrorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; + BB76DF8A1F6F649300A1DAA3 /* ErrorView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ErrorView.xib; sourceTree = ""; }; + BB76DF8E1F6F64CD00A1DAA3 /* AuthenticationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BB76DF4D1F6F600300A1DAA3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BB76DF831F6F62E200A1DAA3 /* Turbolinks.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF611F6F600300A1DAA3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF6C1F6F600300A1DAA3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BB76DF471F6F600300A1DAA3 = { + isa = PBXGroup; + children = ( + BB76DF521F6F600300A1DAA3 /* Recipes */, + BB76DF671F6F600300A1DAA3 /* RecipesTests */, + BB76DF721F6F600300A1DAA3 /* RecipesUITests */, + BB76DF511F6F600300A1DAA3 /* Products */, + BB76DF811F6F62E100A1DAA3 /* Frameworks */, + ); + sourceTree = ""; + }; + BB76DF511F6F600300A1DAA3 /* Products */ = { + isa = PBXGroup; + children = ( + BB76DF501F6F600300A1DAA3 /* Recipes.app */, + BB76DF641F6F600300A1DAA3 /* RecipesTests.xctest */, + BB76DF6F1F6F600300A1DAA3 /* RecipesUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BB76DF521F6F600300A1DAA3 /* Recipes */ = { + isa = PBXGroup; + children = ( + BB76DF881F6F649300A1DAA3 /* Error.swift */, + BB76DF891F6F649300A1DAA3 /* ErrorView.swift */, + BB76DF8A1F6F649300A1DAA3 /* ErrorView.xib */, + BB76DF531F6F600300A1DAA3 /* AppDelegate.swift */, + BB76DF551F6F600300A1DAA3 /* ApplicationController.swift */, + BB76DF571F6F600300A1DAA3 /* Main.storyboard */, + BB76DF5A1F6F600300A1DAA3 /* Assets.xcassets */, + BB76DF5C1F6F600300A1DAA3 /* LaunchScreen.storyboard */, + BB76DF5F1F6F600300A1DAA3 /* Info.plist */, + BB76DF861F6F644D00A1DAA3 /* VisitableViewController.swift */, + BB76DF8E1F6F64CD00A1DAA3 /* AuthenticationController.swift */, + ); + path = Recipes; + sourceTree = ""; + }; + BB76DF671F6F600300A1DAA3 /* RecipesTests */ = { + isa = PBXGroup; + children = ( + BB76DF681F6F600300A1DAA3 /* RecipesTests.swift */, + BB76DF6A1F6F600300A1DAA3 /* Info.plist */, + ); + path = RecipesTests; + sourceTree = ""; + }; + BB76DF721F6F600300A1DAA3 /* RecipesUITests */ = { + isa = PBXGroup; + children = ( + BB76DF731F6F600300A1DAA3 /* RecipesUITests.swift */, + BB76DF751F6F600300A1DAA3 /* Info.plist */, + ); + path = RecipesUITests; + sourceTree = ""; + }; + BB76DF811F6F62E100A1DAA3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BB76DF821F6F62E200A1DAA3 /* Turbolinks.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BB76DF4F1F6F600300A1DAA3 /* Recipes */ = { + isa = PBXNativeTarget; + buildConfigurationList = BB76DF781F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "Recipes" */; + buildPhases = ( + BB76DF4C1F6F600300A1DAA3 /* Sources */, + BB76DF4D1F6F600300A1DAA3 /* Frameworks */, + BB76DF4E1F6F600300A1DAA3 /* Resources */, + BB76DF841F6F62F700A1DAA3 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Recipes; + productName = Recipes; + productReference = BB76DF501F6F600300A1DAA3 /* Recipes.app */; + productType = "com.apple.product-type.application"; + }; + BB76DF631F6F600300A1DAA3 /* RecipesTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BB76DF7B1F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "RecipesTests" */; + buildPhases = ( + BB76DF601F6F600300A1DAA3 /* Sources */, + BB76DF611F6F600300A1DAA3 /* Frameworks */, + BB76DF621F6F600300A1DAA3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BB76DF661F6F600300A1DAA3 /* PBXTargetDependency */, + ); + name = RecipesTests; + productName = RecipesTests; + productReference = BB76DF641F6F600300A1DAA3 /* RecipesTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + BB76DF6E1F6F600300A1DAA3 /* RecipesUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BB76DF7E1F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "RecipesUITests" */; + buildPhases = ( + BB76DF6B1F6F600300A1DAA3 /* Sources */, + BB76DF6C1F6F600300A1DAA3 /* Frameworks */, + BB76DF6D1F6F600300A1DAA3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BB76DF711F6F600300A1DAA3 /* PBXTargetDependency */, + ); + name = RecipesUITests; + productName = RecipesUITests; + productReference = BB76DF6F1F6F600300A1DAA3 /* RecipesUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BB76DF481F6F600300A1DAA3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = github.com/boblail; + TargetAttributes = { + BB76DF4F1F6F600300A1DAA3 = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + }; + BB76DF631F6F600300A1DAA3 = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + TestTargetID = BB76DF4F1F6F600300A1DAA3; + }; + BB76DF6E1F6F600300A1DAA3 = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + TestTargetID = BB76DF4F1F6F600300A1DAA3; + }; + }; + }; + buildConfigurationList = BB76DF4B1F6F600300A1DAA3 /* Build configuration list for PBXProject "Recipes" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BB76DF471F6F600300A1DAA3; + productRefGroup = BB76DF511F6F600300A1DAA3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BB76DF4F1F6F600300A1DAA3 /* Recipes */, + BB76DF631F6F600300A1DAA3 /* RecipesTests */, + BB76DF6E1F6F600300A1DAA3 /* RecipesUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BB76DF4E1F6F600300A1DAA3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB76DF5E1F6F600300A1DAA3 /* LaunchScreen.storyboard in Resources */, + BB76DF5B1F6F600300A1DAA3 /* Assets.xcassets in Resources */, + BB76DF591F6F600300A1DAA3 /* Main.storyboard in Resources */, + BB76DF8D1F6F649300A1DAA3 /* ErrorView.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF621F6F600300A1DAA3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF6D1F6F600300A1DAA3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BB76DF4C1F6F600300A1DAA3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB76DF8C1F6F649300A1DAA3 /* ErrorView.swift in Sources */, + BB76DF8B1F6F649300A1DAA3 /* Error.swift in Sources */, + BB76DF871F6F644D00A1DAA3 /* VisitableViewController.swift in Sources */, + BB76DF8F1F6F64CD00A1DAA3 /* AuthenticationController.swift in Sources */, + BB76DF561F6F600300A1DAA3 /* ApplicationController.swift in Sources */, + BB76DF541F6F600300A1DAA3 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF601F6F600300A1DAA3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB76DF691F6F600300A1DAA3 /* RecipesTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB76DF6B1F6F600300A1DAA3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB76DF741F6F600300A1DAA3 /* RecipesUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BB76DF661F6F600300A1DAA3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BB76DF4F1F6F600300A1DAA3 /* Recipes */; + targetProxy = BB76DF651F6F600300A1DAA3 /* PBXContainerItemProxy */; + }; + BB76DF711F6F600300A1DAA3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BB76DF4F1F6F600300A1DAA3 /* Recipes */; + targetProxy = BB76DF701F6F600300A1DAA3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BB76DF571F6F600300A1DAA3 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BB76DF581F6F600300A1DAA3 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + BB76DF5C1F6F600300A1DAA3 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BB76DF5D1F6F600300A1DAA3 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BB76DF761F6F600300A1DAA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BB76DF771F6F600300A1DAA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + BB76DF791F6F600300A1DAA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Recipes/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.Recipes; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + BB76DF7A1F6F600300A1DAA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = Recipes/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.Recipes; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + BB76DF7C1F6F600300A1DAA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = RecipesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.RecipesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Recipes.app/Recipes"; + }; + name = Debug; + }; + BB76DF7D1F6F600300A1DAA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = RecipesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.RecipesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Recipes.app/Recipes"; + }; + name = Release; + }; + BB76DF7F1F6F600300A1DAA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + INFOPLIST_FILE = RecipesUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.RecipesUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_TARGET_NAME = Recipes; + }; + name = Debug; + }; + BB76DF801F6F600300A1DAA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + INFOPLIST_FILE = RecipesUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.boblail.RecipesUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_TARGET_NAME = Recipes; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BB76DF4B1F6F600300A1DAA3 /* Build configuration list for PBXProject "Recipes" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB76DF761F6F600300A1DAA3 /* Debug */, + BB76DF771F6F600300A1DAA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BB76DF781F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "Recipes" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB76DF791F6F600300A1DAA3 /* Debug */, + BB76DF7A1F6F600300A1DAA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BB76DF7B1F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "RecipesTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB76DF7C1F6F600300A1DAA3 /* Debug */, + BB76DF7D1F6F600300A1DAA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BB76DF7E1F6F600300A1DAA3 /* Build configuration list for PBXNativeTarget "RecipesUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB76DF7F1F6F600300A1DAA3 /* Debug */, + BB76DF801F6F600300A1DAA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BB76DF481F6F600300A1DAA3 /* Project object */; +} diff --git a/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..4677066 --- /dev/null +++ b/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/xcuserdata/lailrc.xcuserdatad/UserInterfaceState.xcuserstate b/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/xcuserdata/lailrc.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..f1c1b4d Binary files /dev/null and b/platforms/ios/Recipes/Recipes.xcodeproj/project.xcworkspace/xcuserdata/lailrc.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/Recipes.xcscheme b/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/Recipes.xcscheme new file mode 100644 index 0000000..3a531dc --- /dev/null +++ b/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/Recipes.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/xcschememanagement.plist b/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..82b937e --- /dev/null +++ b/platforms/ios/Recipes/Recipes.xcodeproj/xcuserdata/lailrc.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + Recipes.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + BB76DF4F1F6F600300A1DAA3 + + primary + + + BB76DF631F6F600300A1DAA3 + + primary + + + BB76DF6E1F6F600300A1DAA3 + + primary + + + + + diff --git a/platforms/ios/Recipes/Recipes/AppDelegate.swift b/platforms/ios/Recipes/Recipes/AppDelegate.swift new file mode 100644 index 0000000..906e951 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/AppDelegate.swift @@ -0,0 +1,36 @@ +import UIKit +import WebKit +import Turbolinks + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + // MARK: UIApplicationDelegate + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } +} diff --git a/platforms/ios/Recipes/Recipes/ApplicationController.swift b/platforms/ios/Recipes/Recipes/ApplicationController.swift new file mode 100644 index 0000000..7efff88 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/ApplicationController.swift @@ -0,0 +1,122 @@ +import UIKit +import WebKit +import Turbolinks + +class ApplicationController: UINavigationController { +// fileprivate let url = URL(string: "http://localhost:3000/my-recipes")! + fileprivate let url = URL(string: "https://lailrecipes.herokuapp.com/my-recipes")! + fileprivate let webViewProcessPool = WKProcessPool() + + fileprivate var application: UIApplication { + return UIApplication.shared + } + + fileprivate lazy var webViewConfiguration: WKWebViewConfiguration = { + let configuration = WKWebViewConfiguration() + configuration.processPool = self.webViewProcessPool + configuration.applicationNameForUserAgent = "RecipesApp" + return configuration + }() + + fileprivate lazy var session: Session = { + let session = Session(webViewConfiguration: self.webViewConfiguration) + session.delegate = self + return session + }() + + override func viewDidLoad() { + super.viewDidLoad() + presentVisitableForSession(session, url: url) + } + + fileprivate func presentVisitableForSession(_ session: Session, url: URL, action: Action = .Advance) { + let visitable = VisitableViewController(url: url) + + for case let previousVisitable as VisitableViewController in viewControllers { + if previousVisitable.visitableURL.path == url.path { + popToViewController(previousVisitable, animated: true) + previousVisitable.visitableURL = url + session.visit(previousVisitable) + return + } + } + + if action == .Advance { + pushViewController(visitable, animated: true) + } else if action == .Replace { + popViewController(animated: false) + pushViewController(visitable, animated: false) + } + + session.visit(visitable) + } + + func navigateTo(path: String, action: Action = .Advance) { + navigateTo(url: URL(string: url.absoluteString + path )!, action: action) + } + + func navigateTo(url: URL, action: Action = .Advance) { + presentVisitableForSession(session, url: url, action: action) + } + + fileprivate func presentAuthenticationController(url: URL) { + let authenticationController = AuthenticationController() + authenticationController.delegate = self + authenticationController.webViewConfiguration = webViewConfiguration + authenticationController.url = url + authenticationController.title = "Sign in" + + let authNavigationController = UINavigationController(rootViewController: authenticationController) + present(authNavigationController, animated: true, completion: nil) + } +} + +extension ApplicationController: SessionDelegate { + func session(_ session: Session, didProposeVisitToURL URL: Foundation.URL, withAction action: Action) { + if URL.path == "/users/auth/google_oauth2" { + presentAuthenticationController(url: URL) + } else { + print( URL.query ?? "N/A" ) + if URL.query?.contains("q=") ?? false { + presentVisitableForSession(session, url: URL, action: .Replace) + } else { + presentVisitableForSession(session, url: URL, action: action) + } + } + } + + func session(_ session: Session, didFailRequestForVisitable visitable: Visitable, withError error: NSError) { + NSLog("ERROR: %@", error) + guard let viewController = visitable as? VisitableViewController, let errorCode = ErrorCode(rawValue: error.code) else { return } + + switch errorCode { + case .httpFailure: + let statusCode = error.userInfo["statusCode"] as! Int + switch statusCode { + case 401: + presentAuthenticationController(url: url.appendingPathComponent("/users/sign_in")) + case 404: + viewController.presentError(.HTTPNotFoundError) + default: + viewController.presentError(Error(HTTPStatusCode: statusCode)) + } + case .networkFailure: + viewController.presentError(.NetworkError) + } + } + + func sessionDidStartRequest(_ session: Session) { + application.isNetworkActivityIndicatorVisible = true + } + + func sessionDidFinishRequest(_ session: Session) { + application.isNetworkActivityIndicatorVisible = false + } +} + +extension ApplicationController: AuthenticationControllerDelegate { + func authenticationControllerDidAuthenticate(_ authenticationController: AuthenticationController) { + session.reload() + dismiss(animated: true, completion: nil) + } +} diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Contents.json b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..ec997db --- /dev/null +++ b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,109 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-41.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-60.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x-1.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x-1.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-42.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-167.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-167.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-167.png new file mode 100644 index 0000000..5f6e142 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-167.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40.png new file mode 100644 index 0000000..584671f Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png new file mode 100644 index 0000000..3abd6b7 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 0000000..3abd6b7 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png new file mode 100644 index 0000000..242a8cd Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-41.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-41.png new file mode 100644 index 0000000..584671f Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-41.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-42.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-42.png new file mode 100644 index 0000000..584671f Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-42.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60.png new file mode 100644 index 0000000..50a3c66 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png new file mode 100644 index 0000000..242a8cd Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-1.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-1.png new file mode 100644 index 0000000..1ca10b6 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-60@3x-1.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..1e2fd9e Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000..f473fe5 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000..43c9ca8 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png new file mode 100644 index 0000000..e5ebe08 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000..e5ebe08 Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000..40a026f Binary files /dev/null and b/platforms/ios/Recipes/Recipes/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/platforms/ios/Recipes/Recipes/AuthenticationController.swift b/platforms/ios/Recipes/Recipes/AuthenticationController.swift new file mode 100644 index 0000000..d3296f9 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/AuthenticationController.swift @@ -0,0 +1,53 @@ +import UIKit +import WebKit + +protocol AuthenticationControllerDelegate: class { + func authenticationControllerDidAuthenticate(_ authenticationController: AuthenticationController) +} + +class AuthenticationController: UIViewController { + var url: URL? + var webViewConfiguration: WKWebViewConfiguration? + weak var delegate: AuthenticationControllerDelegate? + + lazy var webView: WKWebView = { + let configuration = self.webViewConfiguration ?? WKWebViewConfiguration() + let webView = WKWebView(frame: CGRect.zero, configuration: configuration) + webView.translatesAutoresizingMaskIntoConstraints = false + + // spoof a real browser so that Google's OAuth page doesn't block us + webView.customUserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" + + webView.navigationDelegate = self + return webView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(webView) + view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: [], metrics: nil, views: [ "view": webView ])) + view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: [], metrics: nil, views: [ "view": webView ])) + + if let url = self.url { + webView.load(URLRequest(url: url)) + } + } +} + +extension AuthenticationController: WKNavigationDelegate { + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + + let URL = navigationAction.request.url + if URL?.path == "/users/auth/google_oauth2/callback" { + decisionHandler(.cancel) + delegate?.authenticationControllerDidAuthenticate(self) + + // Have the app load this URL + (delegate as! ApplicationController).navigateTo(url: URL!, action: .Replace) + return + } + + decisionHandler(.allow) + } +} diff --git a/platforms/ios/Recipes/Recipes/Base.lproj/LaunchScreen.storyboard b/platforms/ios/Recipes/Recipes/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..6f512f4 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/ios/Recipes/Recipes/Base.lproj/Main.storyboard b/platforms/ios/Recipes/Recipes/Base.lproj/Main.storyboard new file mode 100644 index 0000000..40407f5 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/ios/Recipes/Recipes/Error.swift b/platforms/ios/Recipes/Recipes/Error.swift new file mode 100644 index 0000000..1edcb07 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/Error.swift @@ -0,0 +1,18 @@ +struct Error { + static let HTTPNotFoundError = Error(title: "Page Not Found", message: "There doesn’t seem to be anything here.") + static let NetworkError = Error(title: "Can’t Connect", message: "Recipes can’t connect to the server.") + static let UnknownError = Error(title: "Unknown Error", message: "An unknown error occurred.") + + let title: String + let message: String + + init(title: String, message: String) { + self.title = title + self.message = message + } + + init(HTTPStatusCode: Int) { + self.title = "Server Error" + self.message = "The server returned an HTTP \(HTTPStatusCode) response." + } +} diff --git a/platforms/ios/Recipes/Recipes/ErrorView.swift b/platforms/ios/Recipes/Recipes/ErrorView.swift new file mode 100644 index 0000000..0c2f1a1 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/ErrorView.swift @@ -0,0 +1,14 @@ +import UIKit + +class ErrorView: UIView { + @IBOutlet var titleLabel: UILabel! + @IBOutlet var messageLabel: UILabel! + @IBOutlet var retryButton: UIButton! + + var error: Error? { + didSet { + titleLabel.text = error?.title + messageLabel.text = error?.message + } + } +} \ No newline at end of file diff --git a/platforms/ios/Recipes/Recipes/ErrorView.xib b/platforms/ios/Recipes/Recipes/ErrorView.xib new file mode 100644 index 0000000..d6671d7 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/ErrorView.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/ios/Recipes/Recipes/Info.plist b/platforms/ios/Recipes/Recipes/Info.plist new file mode 100644 index 0000000..da9122c --- /dev/null +++ b/platforms/ios/Recipes/Recipes/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Recipes + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/platforms/ios/Recipes/Recipes/VisitableViewController.swift b/platforms/ios/Recipes/Recipes/VisitableViewController.swift new file mode 100644 index 0000000..3be4a33 --- /dev/null +++ b/platforms/ios/Recipes/Recipes/VisitableViewController.swift @@ -0,0 +1,44 @@ +import Turbolinks +import UIKit + +class VisitableViewController: Turbolinks.VisitableViewController { + var rightBarButtonUrl: URL? + + lazy var errorView: ErrorView = { + let view = Bundle.main.loadNibNamed("ErrorView", owner: self, options: nil)!.first as! ErrorView + view.translatesAutoresizingMaskIntoConstraints = false + view.retryButton.addTarget(self, action: #selector(retry(_:)), for: .touchUpInside) + return view + }() + + func presentError(_ error: Error) { + errorView.error = error + view.addSubview(errorView) + installErrorViewConstraints() + } + + func installErrorViewConstraints() { + view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: [], metrics: nil, views: [ "view": errorView ])) + view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: [], metrics: nil, views: [ "view": errorView ])) + } + + func retry(_ sender: AnyObject) { + errorView.removeFromSuperview() + reloadVisitable() + } + + override func visitableDidRender() { + super.visitableDidRender() + visitableView.webView?.evaluateJavaScript("var nav = document.querySelector('[rel=\"mobile-navigation\"]'); nav && [nav.title, nav.href]") { (result, error) -> Void in + if let titleAndUrl = result as? NSArray { + let rightBarButtonTitle = titleAndUrl[0] as! String + self.rightBarButtonUrl = URL(string: titleAndUrl[1] as! String) + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: rightBarButtonTitle, style: .plain, target: self, action: #selector(self.rightBarButtonTapped)) + } + } + } + + func rightBarButtonTapped() { + (self.navigationController as! ApplicationController).navigateTo(url: self.rightBarButtonUrl!, action: .Advance); + } +} diff --git a/platforms/ios/Recipes/RecipesTests/Info.plist b/platforms/ios/Recipes/RecipesTests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/platforms/ios/Recipes/RecipesTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/platforms/ios/Recipes/RecipesTests/RecipesTests.swift b/platforms/ios/Recipes/RecipesTests/RecipesTests.swift new file mode 100644 index 0000000..4dea628 --- /dev/null +++ b/platforms/ios/Recipes/RecipesTests/RecipesTests.swift @@ -0,0 +1,36 @@ +// +// RecipesTests.swift +// RecipesTests +// +// Created by Robert Lail on 9/17/17. +// Copyright © 2017 github.com/boblail. All rights reserved. +// + +import XCTest +@testable import Recipes + +class RecipesTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/platforms/ios/Recipes/RecipesUITests/Info.plist b/platforms/ios/Recipes/RecipesUITests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/platforms/ios/Recipes/RecipesUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/platforms/ios/Recipes/RecipesUITests/RecipesUITests.swift b/platforms/ios/Recipes/RecipesUITests/RecipesUITests.swift new file mode 100644 index 0000000..341fcf3 --- /dev/null +++ b/platforms/ios/Recipes/RecipesUITests/RecipesUITests.swift @@ -0,0 +1,36 @@ +// +// RecipesUITests.swift +// RecipesUITests +// +// Created by Robert Lail on 9/17/17. +// Copyright © 2017 github.com/boblail. All rights reserved. +// + +import XCTest + +class RecipesUITests: XCTestCase { + + override func setUp() { + super.setUp() + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + +}