├── .gitignore ├── Crashlytics.framework ├── Crashlytics ├── Headers │ ├── ANSCompatibility.h │ ├── Answers.h │ ├── CLSAttributes.h │ ├── CLSLogging.h │ ├── CLSReport.h │ ├── CLSStackFrame.h │ └── Crashlytics.h ├── Info.plist ├── Modules │ └── module.modulemap ├── run ├── submit └── uploadDSYM ├── Fabric.framework ├── Fabric ├── Headers │ ├── FABAttributes.h │ └── Fabric.h ├── Info.plist ├── Modules │ └── module.modulemap ├── run └── uploadDSYM ├── FontAwesome ├── Enum.swift ├── FontAwesome.h ├── FontAwesome.otf ├── FontAwesome.swift ├── FontAwesomeBarButtonItem.swift ├── FontAwesomeImageRepresentable.swift ├── FontAwesomeImageView.swift ├── FontAwesomeSegmentedControl.swift ├── FontAwesomeStateRequirement.swift ├── FontAwesomeTabBarItem.swift ├── FontAwesomeTextRepresentable.swift └── FontAwesomeView.swift ├── IndieAuth ├── IndieAuth.swift ├── IndieAuthAccount.swift ├── IndieAuthDelegate.swift ├── IndieAuthLoginViewController.swift ├── IndieAuthProfile.swift ├── IndieAuthScope.swift └── IndieAuthTokenResponse.swift ├── IndiePass.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ ├── IndiePass.xcscheme │ └── share-micropub.xcscheme ├── IndiePass ├── App │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Main │ │ ├── AppDelegate.swift │ │ ├── Main.storyboard │ │ ├── MainViewController.swift │ │ └── ShortcutItemType.swift │ ├── Onboarding │ │ ├── Onboarding.storyboard │ │ ├── OnboardingCompletionViewController.swift │ │ ├── OnboardingTemplateViewController.swift │ │ └── OnboardingViewController.swift │ ├── Reader │ │ ├── Channel │ │ │ ├── ChannelSettingsDelegate.swift │ │ │ ├── ChannelSettingsNavigationController.swift │ │ │ ├── ChannelSettingsViewController.swift │ │ │ ├── ChannelTableViewCell.swift │ │ │ ├── ChannelViewController.swift │ │ │ ├── Command.swift │ │ │ ├── CommandDelegate.swift │ │ │ └── CommandTableViewCell.swift │ │ ├── FullView │ │ │ ├── FullArticleViewController.swift │ │ │ └── FullView.storyboard │ │ └── Timeline │ │ │ ├── Timeline.storyboard │ │ │ ├── TimelineCellDelegate.swift │ │ │ ├── TimelinePhotoTableViewCell.swift │ │ │ ├── TimelineTextTableViewCell.swift │ │ │ └── TimelineViewController.swift │ └── Settings │ │ ├── AccountDebugViewController.swift │ │ ├── AccountViewController.swift │ │ ├── Settings.storyboard │ │ └── UserSettingsTableViewController.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x-1.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x-1.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x-1.png │ │ ├── Icon-App-29x29@2x-2.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x-1.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x-1.png │ │ ├── Icon-App-40x40@2x-2.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x-1.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x-1.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── ChannelData.swift ├── ChannelDataController.swift ├── DataController.swift ├── IndiePass.entitlements ├── IndiePass.xcdatamodeld │ ├── .xccurrentversion │ └── Indigenous.xcdatamodel │ │ └── contents ├── Info.plist ├── Kanna │ ├── CSS.swift │ ├── Kanna.swift │ ├── libxmlHTMLDocument.swift │ ├── libxmlHTMLNode.swift │ └── libxmlParserOption.swift └── RelLinkParser │ └── RealLinkParser.swift ├── IndiePassTests ├── IndiePassTests.swift └── Info.plist ├── IndiePassUITests ├── IndiePassUITests.swift └── Info.plist ├── LICENSE ├── Micropub ├── Micropub.swift ├── MicropubConfig.swift ├── MicropubError.swift ├── MicropubPhoto.swift ├── MicropubPost.swift ├── MicropubResponseType.swift ├── MicropubRsvpValue.swift ├── MicropubSendType.swift ├── MicropubSyndicationQueryResponse.swift └── SyndicateTarget.swift ├── Microsub ├── Author.swift ├── Channel.swift ├── ChannelAPIResponse.swift ├── ChannelUnreadStatus.swift ├── Timeline.swift ├── TimelineApiPaging.swift ├── TimelineApiResponse.swift ├── TimelineMarkAsReadRequest.swift ├── TimelineOptions.swift ├── TimelinePost.swift └── TimelinePostContent.swift ├── Modules ├── libxml2-kanna.h └── module.modulemap ├── PostingInterface ├── PhotoUploadCollectionViewCell.swift ├── PostingView.storyboard ├── PostingViewController.swift ├── PostingViewDelegate.swift ├── SimpleSelection.storyboard ├── SimpleSelectionDelegate.swift ├── SimpleSelectionItem.swift ├── SimpleSelectionReadOnlyDelegate.swift ├── SimpleSelectionTableViewController.swift └── SimpleSelectionViewController.swift ├── Presentation └── HalfModalPresentationController.swift ├── README.md ├── SwiftExtensions ├── Data+Extension.swift ├── String.extension.swift └── StringProtocol+Extension.swift ├── Theme ├── PXColor.swift └── ThemeManager.swift ├── Transitioning ├── HalfModalInteractiveTransition.swift ├── HalfModalTransitionAnimator.swift └── HalfModalTransitioningDelegate.swift ├── Utilities ├── RandomString.swift └── UABuilder.swift ├── mf2 ├── Jf2Post.swift └── Mf2Type.swift ├── share-micropub ├── AccountSelectorTableViewController.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x-1.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x-1.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x-1.png │ │ ├── Icon-App-29x29@2x-2.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x-1.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x-1.png │ │ ├── Icon-App-40x40@2x-2.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x-1.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x-1.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── Contents.json ├── Base.lproj │ └── MainInterface.storyboard ├── Info.plist ├── MicropubShareViewController.swift ├── ModalNavController.swift ├── ReplyViewController.swift ├── ShareViewController.swift ├── mf2parsing.js └── share-micropub.entitlements ├── voice-notes └── voice-notesBeta.entitlements └── xray ├── XRayParsingResponse.swift └── xray.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | .DS_Store 67 | /.idea 68 | -------------------------------------------------------------------------------- /Crashlytics.framework/Crashlytics: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Crashlytics.framework/Crashlytics -------------------------------------------------------------------------------- /Crashlytics.framework/Headers/ANSCompatibility.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANSCompatibility.h 3 | // AnswersKit 4 | // 5 | // Copyright (c) 2015 Crashlytics, Inc. All rights reserved. 6 | // 7 | 8 | #pragma once 9 | 10 | #if !__has_feature(nullability) 11 | #define nonnull 12 | #define nullable 13 | #define _Nullable 14 | #define _Nonnull 15 | #endif 16 | 17 | #ifndef NS_ASSUME_NONNULL_BEGIN 18 | #define NS_ASSUME_NONNULL_BEGIN 19 | #endif 20 | 21 | #ifndef NS_ASSUME_NONNULL_END 22 | #define NS_ASSUME_NONNULL_END 23 | #endif 24 | 25 | #if __has_feature(objc_generics) 26 | #define ANS_GENERIC_NSARRAY(type) NSArray 27 | #define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary 28 | #else 29 | #define ANS_GENERIC_NSARRAY(type) NSArray 30 | #define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary 31 | #endif 32 | -------------------------------------------------------------------------------- /Crashlytics.framework/Headers/CLSAttributes.h: -------------------------------------------------------------------------------- 1 | // 2 | // CLSAttributes.h 3 | // Crashlytics 4 | // 5 | // Copyright (c) 2015 Crashlytics, Inc. All rights reserved. 6 | // 7 | 8 | #pragma once 9 | 10 | #define CLS_DEPRECATED(x) __attribute__ ((deprecated(x))) 11 | 12 | #if !__has_feature(nullability) 13 | #define nonnull 14 | #define nullable 15 | #define _Nullable 16 | #define _Nonnull 17 | #endif 18 | 19 | #ifndef NS_ASSUME_NONNULL_BEGIN 20 | #define NS_ASSUME_NONNULL_BEGIN 21 | #endif 22 | 23 | #ifndef NS_ASSUME_NONNULL_END 24 | #define NS_ASSUME_NONNULL_END 25 | #endif 26 | 27 | #if __has_feature(objc_generics) 28 | #define CLS_GENERIC_NSARRAY(type) NSArray 29 | #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary 30 | #else 31 | #define CLS_GENERIC_NSARRAY(type) NSArray 32 | #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary 33 | #endif 34 | -------------------------------------------------------------------------------- /Crashlytics.framework/Headers/CLSLogging.h: -------------------------------------------------------------------------------- 1 | // 2 | // CLSLogging.h 3 | // Crashlytics 4 | // 5 | // Copyright (c) 2015 Crashlytics, Inc. All rights reserved. 6 | // 7 | #ifdef __OBJC__ 8 | #import "CLSAttributes.h" 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | #endif 13 | 14 | 15 | 16 | /** 17 | * 18 | * The CLS_LOG macro provides as easy way to gather more information in your log messages that are 19 | * sent with your crash data. CLS_LOG prepends your custom log message with the function name and 20 | * line number where the macro was used. If your app was built with the DEBUG preprocessor macro 21 | * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog. 22 | * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only. 23 | * 24 | * Example output: 25 | * -[AppDelegate login:] line 134 $ login start 26 | * 27 | * If you would like to change this macro, create a new header file, unset our define and then define 28 | * your own version. Make sure this new header file is imported after the Crashlytics header file. 29 | * 30 | * #undef CLS_LOG 31 | * #define CLS_LOG(__FORMAT__, ...) CLSNSLog... 32 | * 33 | **/ 34 | #ifdef __OBJC__ 35 | #ifdef DEBUG 36 | #define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 37 | #else 38 | #define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 39 | #endif 40 | #endif 41 | 42 | /** 43 | * 44 | * Add logging that will be sent with your crash data. This logging will not show up in the system.log 45 | * and will only be visible in your Crashlytics dashboard. 46 | * 47 | **/ 48 | 49 | #ifdef __OBJC__ 50 | OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 51 | OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); 52 | 53 | /** 54 | * 55 | * Add logging that will be sent with your crash data. This logging will show up in the system.log 56 | * and your Crashlytics dashboard. It is not recommended for Release builds. 57 | * 58 | **/ 59 | OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 60 | OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); 61 | 62 | 63 | NS_ASSUME_NONNULL_END 64 | #endif 65 | -------------------------------------------------------------------------------- /Crashlytics.framework/Headers/CLSReport.h: -------------------------------------------------------------------------------- 1 | // 2 | // CLSReport.h 3 | // Crashlytics 4 | // 5 | // Copyright (c) 2015 Crashlytics, Inc. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "CLSAttributes.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /** 14 | * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details. 15 | **/ 16 | @protocol CLSCrashReport 17 | 18 | @property (nonatomic, copy, readonly) NSString *identifier; 19 | @property (nonatomic, copy, readonly) NSDictionary *customKeys; 20 | @property (nonatomic, copy, readonly) NSString *bundleVersion; 21 | @property (nonatomic, copy, readonly) NSString *bundleShortVersionString; 22 | @property (nonatomic, readonly, nullable) NSDate *crashedOnDate; 23 | @property (nonatomic, copy, readonly) NSString *OSVersion; 24 | @property (nonatomic, copy, readonly) NSString *OSBuildVersion; 25 | 26 | @end 27 | 28 | /** 29 | * The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can 30 | * use this class to get information about the event, and can also set some values after the 31 | * event has occurred. 32 | **/ 33 | @interface CLSReport : NSObject 34 | 35 | - (instancetype)init NS_UNAVAILABLE; 36 | + (instancetype)new NS_UNAVAILABLE; 37 | 38 | /** 39 | * Returns the session identifier for the report. 40 | **/ 41 | @property (nonatomic, copy, readonly) NSString *identifier; 42 | 43 | /** 44 | * Returns the custom key value data for the report. 45 | **/ 46 | @property (nonatomic, copy, readonly) NSDictionary *customKeys; 47 | 48 | /** 49 | * Returns the CFBundleVersion of the application that generated the report. 50 | **/ 51 | @property (nonatomic, copy, readonly) NSString *bundleVersion; 52 | 53 | /** 54 | * Returns the CFBundleShortVersionString of the application that generated the report. 55 | **/ 56 | @property (nonatomic, copy, readonly) NSString *bundleShortVersionString; 57 | 58 | /** 59 | * Returns the date that the report was created. 60 | **/ 61 | @property (nonatomic, copy, readonly) NSDate *dateCreated; 62 | 63 | /** 64 | * Returns the os version that the application crashed on. 65 | **/ 66 | @property (nonatomic, copy, readonly) NSString *OSVersion; 67 | 68 | /** 69 | * Returns the os build version that the application crashed on. 70 | **/ 71 | @property (nonatomic, copy, readonly) NSString *OSBuildVersion; 72 | 73 | /** 74 | * Returns YES if the report contains any crash information, otherwise returns NO. 75 | **/ 76 | @property (nonatomic, assign, readonly) BOOL isCrash; 77 | 78 | /** 79 | * You can use this method to set, after the event, additional custom keys. The rules 80 | * and semantics for this method are the same as those documented in Crashlytics.h. Be aware 81 | * that the maximum size and count of custom keys is still enforced, and you can overwrite keys 82 | * and/or cause excess keys to be deleted by using this method. 83 | **/ 84 | - (void)setObjectValue:(nullable id)value forKey:(NSString *)key; 85 | 86 | /** 87 | * Record an application-specific user identifier. See Crashlytics.h for details. 88 | **/ 89 | @property (nonatomic, copy, nullable) NSString * userIdentifier; 90 | 91 | /** 92 | * Record a user name. See Crashlytics.h for details. 93 | **/ 94 | @property (nonatomic, copy, nullable) NSString * userName; 95 | 96 | /** 97 | * Record a user email. See Crashlytics.h for details. 98 | **/ 99 | @property (nonatomic, copy, nullable) NSString * userEmail; 100 | 101 | @end 102 | 103 | NS_ASSUME_NONNULL_END 104 | -------------------------------------------------------------------------------- /Crashlytics.framework/Headers/CLSStackFrame.h: -------------------------------------------------------------------------------- 1 | // 2 | // CLSStackFrame.h 3 | // Crashlytics 4 | // 5 | // Copyright 2015 Crashlytics, Inc. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "CLSAttributes.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /** 14 | * 15 | * This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to 16 | * record information about non-ObjC/C++ exceptions. All information included here will be displayed 17 | * in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the 18 | * address property. If set, Crashlytics will attempt symbolication and could overwrite other properities 19 | * in the process. 20 | * 21 | **/ 22 | @interface CLSStackFrame : NSObject 23 | 24 | + (instancetype)stackFrame; 25 | + (instancetype)stackFrameWithAddress:(NSUInteger)address; 26 | + (instancetype)stackFrameWithSymbol:(NSString *)symbol; 27 | 28 | @property (nonatomic, copy, nullable) NSString *symbol; 29 | @property (nonatomic, copy, nullable) NSString *rawSymbol; 30 | @property (nonatomic, copy, nullable) NSString *library; 31 | @property (nonatomic, copy, nullable) NSString *fileName; 32 | @property (nonatomic, assign) uint32_t lineNumber; 33 | @property (nonatomic, assign) uint64_t offset; 34 | @property (nonatomic, assign) uint64_t address; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /Crashlytics.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Crashlytics.framework/Info.plist -------------------------------------------------------------------------------- /Crashlytics.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Crashlytics { 2 | header "Crashlytics.h" 3 | header "Answers.h" 4 | header "ANSCompatibility.h" 5 | header "CLSLogging.h" 6 | header "CLSReport.h" 7 | header "CLSStackFrame.h" 8 | header "CLSAttributes.h" 9 | 10 | export * 11 | 12 | link "z" 13 | link "c++" 14 | } 15 | -------------------------------------------------------------------------------- /Crashlytics.framework/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # run 4 | # 5 | # Copyright (c) 2015 Crashlytics. All rights reserved. 6 | 7 | # Figure out where we're being called from 8 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 9 | 10 | # Quote path in case of spaces or special chars 11 | DIR="\"${DIR}" 12 | 13 | PATH_SEP="/" 14 | VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" 15 | UPLOAD_COMMAND="uploadDSYM\" $@ run-script" 16 | 17 | # Ensure params are as expected, run in sync mode to validate 18 | eval $DIR$PATH_SEP$VALIDATE_COMMAND 19 | return_code=$? 20 | 21 | if [[ $return_code != 0 ]]; then 22 | exit $return_code 23 | fi 24 | 25 | # Verification passed, upload dSYM in background to prevent Xcode from waiting 26 | # Note: Validation is performed again before upload. 27 | # Output can still be found in Console.app 28 | eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & 29 | -------------------------------------------------------------------------------- /Crashlytics.framework/submit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Crashlytics.framework/submit -------------------------------------------------------------------------------- /Crashlytics.framework/uploadDSYM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Crashlytics.framework/uploadDSYM -------------------------------------------------------------------------------- /Fabric.framework/Fabric: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Fabric.framework/Fabric -------------------------------------------------------------------------------- /Fabric.framework/Headers/FABAttributes.h: -------------------------------------------------------------------------------- 1 | // 2 | // FABAttributes.h 3 | // Fabric 4 | // 5 | // Copyright (C) 2015 Twitter, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #pragma once 21 | 22 | #define FAB_UNAVAILABLE(x) __attribute__((unavailable(x))) 23 | 24 | #if !__has_feature(nullability) 25 | #define nonnull 26 | #define nullable 27 | #define _Nullable 28 | #define _Nonnull 29 | #endif 30 | 31 | #ifndef NS_ASSUME_NONNULL_BEGIN 32 | #define NS_ASSUME_NONNULL_BEGIN 33 | #endif 34 | 35 | #ifndef NS_ASSUME_NONNULL_END 36 | #define NS_ASSUME_NONNULL_END 37 | #endif 38 | 39 | 40 | /** 41 | * The following macros are defined here to provide 42 | * backwards compatability. If you are still using 43 | * them you should migrate to the native nullability 44 | * macros. 45 | */ 46 | #define fab_nullable nullable 47 | #define fab_nonnull nonnull 48 | #define FAB_NONNULL __fab_nonnull 49 | #define FAB_NULLABLE __fab_nullable 50 | #define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN 51 | #define FAB_END_NONNULL NS_ASSUME_NONNULL_END 52 | -------------------------------------------------------------------------------- /Fabric.framework/Headers/Fabric.h: -------------------------------------------------------------------------------- 1 | // 2 | // Fabric.h 3 | // Fabric 4 | // 5 | // Copyright (C) 2015 Twitter, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | #import "FABAttributes.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | #if TARGET_OS_IPHONE 26 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 27 | #error "Fabric's minimum iOS version is 6.0" 28 | #endif 29 | #else 30 | #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 31 | #error "Fabric's minimum OS X version is 10.7" 32 | #endif 33 | #endif 34 | 35 | /** 36 | * Fabric Base. Coordinates configuration and starts all provided kits. 37 | */ 38 | @interface Fabric : NSObject 39 | 40 | /** 41 | * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. 42 | * 43 | * For example, in Objective-C: 44 | * 45 | * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` 46 | * 47 | * Swift: 48 | * 49 | * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` 50 | * 51 | * Only the first call to this method is honored. Subsequent calls are no-ops. 52 | * 53 | * @param kitClasses An array of kit Class objects 54 | * 55 | * @return Returns the shared Fabric instance. In most cases this can be ignored. 56 | */ 57 | + (instancetype)with:(NSArray *)kitClasses; 58 | 59 | /** 60 | * Returns the Fabric singleton object. 61 | */ 62 | + (instancetype)sharedSDK; 63 | 64 | /** 65 | * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. 66 | */ 67 | @property (nonatomic, assign) BOOL debug; 68 | 69 | /** 70 | * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. 71 | */ 72 | - (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); 73 | 74 | /** 75 | * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. 76 | */ 77 | + (instancetype)new FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); 78 | 79 | @end 80 | 81 | NS_ASSUME_NONNULL_END 82 | 83 | -------------------------------------------------------------------------------- /Fabric.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Fabric.framework/Info.plist -------------------------------------------------------------------------------- /Fabric.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module Fabric { 2 | umbrella header "Fabric.h" 3 | 4 | export * 5 | module * { export * } 6 | } -------------------------------------------------------------------------------- /Fabric.framework/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # run 4 | # 5 | # Copyright (c) 2015 Crashlytics. All rights reserved. 6 | 7 | # Figure out where we're being called from 8 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 9 | 10 | # Quote path in case of spaces or special chars 11 | DIR="\"${DIR}" 12 | 13 | PATH_SEP="/" 14 | VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" 15 | UPLOAD_COMMAND="uploadDSYM\" $@ run-script" 16 | 17 | # Ensure params are as expected, run in sync mode to validate 18 | eval $DIR$PATH_SEP$VALIDATE_COMMAND 19 | return_code=$? 20 | 21 | if [[ $return_code != 0 ]]; then 22 | exit $return_code 23 | fi 24 | 25 | # Verification passed, upload dSYM in background to prevent Xcode from waiting 26 | # Note: Validation is performed again before upload. 27 | # Output can still be found in Console.app 28 | eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & 29 | -------------------------------------------------------------------------------- /Fabric.framework/uploadDSYM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/Fabric.framework/uploadDSYM -------------------------------------------------------------------------------- /FontAwesome/FontAwesome.h: -------------------------------------------------------------------------------- 1 | // FontAwesome.h 2 | // 3 | // Copyright (c) 2014-present FontAwesome.swift contributors 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | FOUNDATION_EXPORT double FontAwesomeVersionNumber; 26 | FOUNDATION_EXPORT const unsigned char FontAwesomeVersionString[]; 27 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/FontAwesome/FontAwesome.otf -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeBarButtonItem.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeBarButtonItem.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class FontAwesomeBarButtonItem: UIBarButtonItem { 26 | 27 | @IBInspectable public var isFontAwesomeCSSCode: Bool = true 28 | @IBInspectable public var size: CGFloat = 25.0 29 | 30 | public override func awakeFromNib() { 31 | super.awakeFromNib() 32 | useFontAwesome() 33 | } 34 | 35 | public override func prepareForInterfaceBuilder() { 36 | useFontAwesome() 37 | } 38 | 39 | private func useFontAwesome() { 40 | updateText { 41 | if let cssCode = title { 42 | title = String.fontAwesomeIcon(code: cssCode) 43 | } 44 | } 45 | updateFontAttributes { (state, font) in 46 | let currentAttributes = convertFromOptionalNSAttributedStringKeyDictionary(titleTextAttributes(for: state)) ?? [:] 47 | var attributes = [NSAttributedString.Key: Any]() 48 | currentAttributes.enumerated().forEach { 49 | let currentAttribute = NSAttributedString.Key(rawValue: $0.element.key) 50 | attributes[currentAttribute] = $0.element.value 51 | } 52 | attributes[NSAttributedString.Key.font] = font 53 | setTitleTextAttributes(attributes, for: state) 54 | } 55 | } 56 | 57 | } 58 | 59 | extension FontAwesomeBarButtonItem: FontAwesomeTextRepresentable { 60 | 61 | var isTextCSSCode: Bool { 62 | return isFontAwesomeCSSCode 63 | } 64 | 65 | var textSize: CGFloat { 66 | return size 67 | } 68 | 69 | static func supportedStates() -> [UIControl.State] { 70 | return [.normal, .highlighted, .disabled] 71 | } 72 | 73 | } 74 | 75 | // Helper function inserted by Swift 4.2 migrator. 76 | fileprivate func convertFromOptionalNSAttributedStringKeyDictionary(_ input: [NSAttributedString.Key: Any]?) -> [String: Any]? { 77 | guard let input = input else { return nil } 78 | return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)}) 79 | } 80 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeImageRepresentable.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeImageRepresentable.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | protocol FontAwesomeImageRepresentable: class { 26 | 27 | typealias ImageConfig = (cssIconName: String, color: UIColor?, backgroundColor: UIColor?) 28 | 29 | var imageWidth: CGFloat { get } 30 | var imageConfigs: [ImageConfig] { get } 31 | 32 | func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) 33 | } 34 | 35 | extension FontAwesomeImageRepresentable { 36 | 37 | func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) { 38 | let imgSize = imageSizeForAspectRatio() 39 | for (index, config) in imageConfigs.enumerated() { 40 | let img = createImage(config: config, size: imgSize) 41 | configurationHandler(img, index) 42 | } 43 | } 44 | 45 | private func createImage(config: ImageConfig, size: CGSize) -> UIImage? { 46 | return UIImage.fontAwesomeIcon(code: config.cssIconName, 47 | textColor: config.color ?? .black, 48 | size: size, 49 | backgroundColor: config.backgroundColor ?? .clear) 50 | } 51 | 52 | private func imageSizeForAspectRatio() -> CGSize { 53 | return CGSize(width: imageWidth, height: imageWidth / FontAwesomeConfig.fontAspectRatio) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeImageView.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeImageView.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class FontAwesomeImageView: UIImageView { 26 | 27 | @IBInspectable public var cssCode: String = "fa-square-o" 28 | @IBInspectable public var imageColor: UIColor = .black 29 | @IBInspectable public var imageBackgroundColor: UIColor = .clear 30 | 31 | public override func awakeFromNib() { 32 | super.awakeFromNib() 33 | useFontAwesomeImage() 34 | } 35 | 36 | public override func prepareForInterfaceBuilder() { 37 | useFontAwesomeImage() 38 | } 39 | 40 | private func useFontAwesomeImage() { 41 | createImages { (img, _) in 42 | image = img 43 | } 44 | } 45 | 46 | } 47 | 48 | extension FontAwesomeImageView: FontAwesomeImageRepresentable { 49 | 50 | var imageWidth: CGFloat { 51 | return frame.width 52 | } 53 | 54 | var imageConfigs: [ImageConfig] { 55 | return [(cssCode, imageColor, imageBackgroundColor)] 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeSegmentedControl.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeSegmentedControl.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class FontAwesomeSegmentedControl: UISegmentedControl { 26 | 27 | @IBInspectable public var isFontAwesomeCSSCode: Bool = true 28 | @IBInspectable public var size: CGFloat = 22.0 29 | 30 | public override func awakeFromNib() { 31 | super.awakeFromNib() 32 | useFontAwesome() 33 | } 34 | 35 | public override func prepareForInterfaceBuilder() { 36 | useFontAwesome() 37 | } 38 | 39 | private func useFontAwesome() { 40 | updateText { 41 | for i in 0 ..< numberOfSegments { 42 | if let cssCode = titleForSegment(at: i) { 43 | setTitle(String.fontAwesomeIcon(code: cssCode), forSegmentAt: i) 44 | } 45 | } 46 | } 47 | updateFontAttributes { (state, font) in 48 | var attributes = titleTextAttributes(for: state) ?? [:] 49 | attributes[NSAttributedString.Key.font] = font 50 | setTitleTextAttributes(attributes, for: state) 51 | } 52 | } 53 | 54 | } 55 | 56 | extension FontAwesomeSegmentedControl: FontAwesomeTextRepresentable { 57 | 58 | var isTextCSSCode: Bool { 59 | return isFontAwesomeCSSCode 60 | } 61 | 62 | var textSize: CGFloat { 63 | return size 64 | } 65 | 66 | static func supportedStates() -> [UIControl.State] { 67 | if #available(iOS 9.0, *) { 68 | return [.normal, .highlighted, .disabled, .focused, .selected, .application, .reserved] 69 | } else { 70 | return [.normal, .highlighted, .disabled, .selected, .application, .reserved] 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeStateRequirement.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeStateRequirement.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | protocol FontAwesomeStateRequirement: class { 26 | 27 | static func supportedStates() -> [UIControl.State] 28 | 29 | } 30 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeTabBarItem.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeTabBarItem.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | @IBDesignable public class FontAwesomeTabBarItem: UITabBarItem { 26 | 27 | @IBInspectable public var iconName: String = "fa-square-o" 28 | @IBInspectable public var selectedIconName: String = "fa-square" 29 | @IBInspectable public var size: CGFloat = 38.0 30 | 31 | public override func awakeFromNib() { 32 | super.awakeFromNib() 33 | useFontAwesomeImage() 34 | } 35 | 36 | public override func prepareForInterfaceBuilder() { 37 | useFontAwesomeImage() 38 | } 39 | 40 | private func useFontAwesomeImage() { 41 | createImages { (img, index) in 42 | if index == 0 { 43 | image = img 44 | } else { 45 | selectedImage = img 46 | } 47 | } 48 | } 49 | 50 | } 51 | 52 | extension FontAwesomeTabBarItem: FontAwesomeImageRepresentable { 53 | 54 | var imageWidth: CGFloat { 55 | return size 56 | } 57 | 58 | var imageConfigs: [ImageConfig] { 59 | return [(iconName, nil, nil), (selectedIconName, nil, nil)] 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeTextRepresentable.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeTextRepresentable.swift 2 | // 3 | // Copyright (c) 2017 Maik639 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | protocol FontAwesomeTextRepresentable : FontAwesomeStateRequirement { 26 | 27 | var textSize: CGFloat { get } 28 | var isTextCSSCode: Bool { get } 29 | 30 | func updateText(_ updateTextBlock: () -> Void) 31 | func updateFontAttributes(forStates stateBlock: (UIControl.State, UIFont) -> Void) 32 | 33 | } 34 | 35 | extension FontAwesomeTextRepresentable { 36 | 37 | public func updateText(_ updateTextBlock: () -> Void) { 38 | guard isTextCSSCode else { 39 | return 40 | } 41 | 42 | updateTextBlock() 43 | } 44 | 45 | public func updateFontAttributes(forStates stateBlock: (UIControl.State, UIFont) -> Void) { 46 | let states = type(of: self).supportedStates() 47 | let font = UIFont.fontAwesome(ofSize: textSize) 48 | 49 | for state in states { 50 | stateBlock(state, font) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /FontAwesome/FontAwesomeView.swift: -------------------------------------------------------------------------------- 1 | // FontAwesomeView.swift 2 | // 3 | // Copyright (c) 2016 Antony Alkmim 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | /// A view for FontAwesome icons. 26 | @IBDesignable public class FontAwesomeView: UIView { 27 | 28 | @IBInspectable 29 | public var iconCode: String = "" { 30 | didSet { 31 | self.iconView.text = String.fontAwesomeIcon(code: iconCode) 32 | } 33 | } 34 | 35 | private var iconView = UILabel() 36 | 37 | override init(frame: CGRect) { 38 | super.init(frame: frame) 39 | setupViews() 40 | } 41 | 42 | required public init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | setupViews() 45 | } 46 | 47 | override public func prepareForInterfaceBuilder() { 48 | setupViews() 49 | } 50 | 51 | /// Add a UILabel subview containing FontAwesome icon 52 | func setupViews() { 53 | // Fits icon in the view 54 | self.iconView.textAlignment = NSTextAlignment.center 55 | self.iconView.text = String.fontAwesomeIcon(code: self.iconCode) 56 | self.iconView.textColor = self.tintColor 57 | self.addSubview(iconView) 58 | } 59 | 60 | override public func tintColorDidChange() { 61 | self.iconView.textColor = self.tintColor 62 | } 63 | 64 | override public func layoutSubviews() { 65 | super.layoutSubviews() 66 | self.clipsToBounds = true 67 | self.iconView.font = UIFont.fontAwesome(ofSize: bounds.size.width < bounds.size.height ? bounds.size.width : bounds.size.height) 68 | self.iconView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: bounds.size.width, height: bounds.size.height)) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /IndieAuth/IndieAuthAccount.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Account.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/22/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct IndieAuthAccount: Codable { 12 | let profile: Jf2Post 13 | let access_token: String 14 | let scope: [IndieAuthScope] 15 | let me: URL 16 | let micropub_endpoint: URL 17 | let authorization_endpoint: URL 18 | let token_endpoint: URL 19 | let microsub_endpoint: URL? 20 | var micropub_config: MicropubConfig? 21 | } 22 | 23 | -------------------------------------------------------------------------------- /IndieAuth/IndieAuthDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndieAuthDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol IndieAuthDelegate : NSObjectProtocol { 12 | func loggedIn() -> Void 13 | } 14 | -------------------------------------------------------------------------------- /IndieAuth/IndieAuthProfile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndieAuthProfile.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/22/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct IndieAuthProfile: Codable { 12 | let type: Mf2Type 13 | let name: String 14 | let url: URL 15 | let photo: URL 16 | } 17 | -------------------------------------------------------------------------------- /IndieAuth/IndieAuthScope.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndieAuthScope.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/22/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum IndieAuthScope: String, Codable { 12 | case read 13 | case follow 14 | case mute 15 | case block 16 | case channels 17 | case create 18 | case update 19 | case delete 20 | case media 21 | } 22 | -------------------------------------------------------------------------------- /IndieAuth/IndieAuthTokenResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndieAuthTokenResponse.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/27/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct IndieAuthTokenResponse: Codable { 12 | let access_token: String 13 | let scope: String 14 | let me: URL 15 | } 16 | -------------------------------------------------------------------------------- /IndiePass.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IndiePass.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /IndiePass.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /IndiePass.xcodeproj/xcshareddata/xcschemes/IndiePass.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 44 | 50 | 51 | 52 | 53 | 59 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /IndiePass.xcodeproj/xcshareddata/xcschemes/share-micropub.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 59 | 61 | 67 | 68 | 69 | 70 | 77 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /IndiePass/App/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /IndiePass/App/Main/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Eddie Hinkle on 5/2/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class MainViewController: UINavigationController, IndieAuthDelegate { 13 | 14 | var loginViewController: UIViewController? = nil 15 | var backupViewControllers: [UIViewController]? = nil 16 | // var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer 17 | 18 | var dataController: DataController! 19 | 20 | func showLoginScreen() { 21 | let loginViewController = storyboard?.instantiateViewController(withIdentifier: "indieAuthLoginView") as! IndieAuthLoginViewController 22 | 23 | loginViewController.delegate = self 24 | 25 | DispatchQueue.main.async { 26 | self.viewControllers = [loginViewController] 27 | self.popViewController(animated: true) 28 | } 29 | } 30 | 31 | func hideLoginScreen() { 32 | DispatchQueue.main.async { [weak self] in 33 | if let restoreViewControllers = self?.backupViewControllers { 34 | self?.viewControllers = restoreViewControllers 35 | if let channelVC = self?.viewControllers.first as? ChannelViewController { 36 | channelVC.dataController = self?.dataController 37 | } 38 | self?.popViewController(animated: true) 39 | } 40 | } 41 | } 42 | 43 | func loggedIn() { 44 | hideLoginScreen() 45 | } 46 | 47 | override func viewDidLoad() { 48 | super.viewDidLoad() 49 | 50 | self.backupViewControllers = [self.viewControllers.first!] 51 | } 52 | 53 | override func viewWillAppear(_ animated: Bool) { 54 | super.viewWillAppear(animated) 55 | 56 | let defaults = UserDefaults(suiteName: AppGroup) 57 | let micropubAccounts = defaults?.array(forKey: "micropubAccounts") as? [Data] ?? [Data]() 58 | 59 | if micropubAccounts.count < 1 { 60 | showLoginScreen() 61 | UIApplication.shared.shortcutItems = [] 62 | } else { 63 | // todo: What we need to do if we are logged in 64 | print("Logged in") 65 | let shortcutItem = UIApplicationShortcutItem(type: ShortcutItemType.NewPost.rawValue, localizedTitle: "New Post") 66 | UIApplication.shared.shortcutItems = [shortcutItem] 67 | // let activeAccount = defaults?.integer(forKey: "activeAccount") ?? 0 68 | // let micropubAuth = micropubAccounts[activeAccount] 69 | 70 | if let channelVC = viewControllers.first as? ChannelViewController { 71 | channelVC.dataController = dataController 72 | } 73 | } 74 | } 75 | 76 | override func didReceiveMemoryWarning() { 77 | super.didReceiveMemoryWarning() 78 | // Dispose of any resources that can be recreated. 79 | } 80 | 81 | 82 | /* 83 | // MARK: - Navigation 84 | 85 | // In a storyboard-based application, you will often want to do a little preparation before navigation 86 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 87 | // Get the new view controller using segue.destinationViewController. 88 | // Pass the selected object to the new view controller. 89 | } 90 | */ 91 | 92 | } 93 | -------------------------------------------------------------------------------- /IndiePass/App/Main/ShortcutItemType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortcutItemType.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/17/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum ShortcutItemType: String { 12 | case NewPost 13 | } 14 | -------------------------------------------------------------------------------- /IndiePass/App/Onboarding/OnboardingCompletionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingCompletionViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/26/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class OnboardingCompletionViewController: OnboardingTemplateViewController { 12 | 13 | var dataController: DataController! 14 | 15 | // MARK: - Navigation 16 | // In a storyboard-based application, you will often want to do a little preparation before navigation 17 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 18 | if segue.identifier == "ShowLoginForm", 19 | let nextVC = segue.destination as? IndieAuthLoginViewController { 20 | nextVC.dataController = dataController 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /IndiePass/App/Onboarding/OnboardingTemplateViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingTemplateViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/26/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class OnboardingTemplateViewController: UIViewController { 12 | 13 | @IBOutlet weak var titleLabel: UILabel! 14 | @IBOutlet weak var contentLabel: UILabel! 15 | @IBOutlet weak var learnMore: UIButton! 16 | @IBOutlet weak var icon: UIImageView! 17 | 18 | var titleText: String? = nil 19 | var contentText: String? = nil 20 | var buttonText: String? = nil 21 | var buttonUrl: URL? = nil 22 | var primaryIcon: FontAwesome? = nil 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | view.backgroundColor = ThemeManager.currentTheme().mainColor 28 | titleLabel.text = titleText 29 | titleLabel.textColor = UIColor.white 30 | contentLabel.text = contentText 31 | contentLabel.textColor = UIColor.white 32 | learnMore.setTitleColor(UIColor.white, for: .normal) 33 | learnMore.setTitle(buttonText, for: .normal) 34 | 35 | if primaryIcon != nil { 36 | icon.image = UIImage.fontAwesomeIcon(name: primaryIcon!, textColor: UIColor.white, size: CGSize(width: 150, height: 100)) 37 | } 38 | } 39 | 40 | @IBAction func callToAction(_ sender: Any) { 41 | if let url = buttonUrl, UIApplication.shared.canOpenURL(url) { 42 | UIApplication.shared.open(url) 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /IndiePass/App/Onboarding/OnboardingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/26/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class OnboardingViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { 12 | 13 | var pages = [UIViewController]() 14 | var currentIndex = 0 15 | let startingIndex = 0 16 | var dataController: DataController! 17 | 18 | // MARK: - UIPageViewControllerDataSource Methods 19 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 20 | guard let viewControllerIndex = pages.index(of: viewController) else { 21 | return nil 22 | } 23 | 24 | let previousIndex = viewControllerIndex - 1 25 | 26 | guard previousIndex >= 0 else { 27 | return pages.last 28 | } 29 | 30 | guard pages.count > previousIndex else { 31 | return nil 32 | } 33 | return pages[previousIndex] 34 | } 35 | 36 | func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 37 | guard let viewControllerIndex = pages.index(of: viewController) else { 38 | return nil 39 | } 40 | 41 | let nextIndex = viewControllerIndex + 1 42 | 43 | guard nextIndex < pages.count else { 44 | return pages.first 45 | } 46 | 47 | guard pages.count > nextIndex else { 48 | return nil 49 | } 50 | 51 | return pages[nextIndex] 52 | } 53 | 54 | func presentationCount(for pageViewController: UIPageViewController) -> Int { 55 | return pages.count 56 | } 57 | 58 | func presentationIndex(for pageViewController: UIPageViewController) -> Int { 59 | return currentIndex 60 | } 61 | 62 | // MARK: - Life Cycle Methods 63 | override func viewDidLoad() { 64 | super.viewDidLoad() 65 | 66 | delegate = self 67 | dataSource = self 68 | view.backgroundColor = ThemeManager.currentTheme().mainColor 69 | 70 | if let introPage = storyboard?.instantiateViewController(withIdentifier: "onboardingTemplate") as? OnboardingTemplateViewController { 71 | introPage.titleText = "Open up the internet" 72 | introPage.contentText = "IndiePass allows you to engage with the Internet as you do on social media sites, but posts it all on your website. Use the built-in reader to read and respond to posts across the internet. IndiePass doesn’t track or store any of your information. You choose a service you trust or host it yourself." 73 | introPage.primaryIcon = .commentsO 74 | pages.append(introPage) 75 | } 76 | 77 | if let micropubPage = storyboard?.instantiateViewController(withIdentifier: "onboardingTemplate") as? OnboardingTemplateViewController { 78 | micropubPage.titleText = "Writing" 79 | micropubPage.contentText = "Post to any website or microblog that supports Micropub. Some popular services that can support Micropub is Micro.blog, Wordpress and Known." 80 | micropubPage.buttonText = "Learn About Micropub" 81 | micropubPage.buttonUrl = URL(string: "https://indiepass.app/ios/help/#micropub") 82 | micropubPage.primaryIcon = .commenting 83 | pages.append(micropubPage) 84 | } 85 | 86 | if let microsubPage = storyboard?.instantiateViewController(withIdentifier: "onboardingTemplate") as? OnboardingTemplateViewController { 87 | microsubPage.titleText = "Reading" 88 | microsubPage.contentText = "You can read posts within IndiePass if your website supports Microsub. The primary service that currently supports Microsub is Aperture." 89 | microsubPage.buttonText = "Learn About Microsub" 90 | microsubPage.buttonUrl = URL(string: "https://indiepass.app/ios/help/#microsub") 91 | microsubPage.primaryIcon = .newspaperO 92 | pages.append(microsubPage) 93 | } 94 | 95 | let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) 96 | if let loginPage = mainStoryboard.instantiateViewController(withIdentifier: "indieAuthLoginView") as? IndieAuthLoginViewController { 97 | loginPage.dataController = dataController 98 | pages.append(loginPage) 99 | } 100 | 101 | currentIndex = startingIndex 102 | setViewControllers([pages[currentIndex]], direction: .forward, animated: false, completion: nil) 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/ChannelSettingsDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSettingsDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/16/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ChannelSettingsDelegate : NSObjectProtocol { 12 | func markAllPostsAsRead() -> Void 13 | } 14 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/ChannelSettingsNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSettingsNavigationController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 5/24/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ChannelSettingsNavigationController: UINavigationController, HalfModalPresentable { 12 | 13 | override var preferredStatusBarStyle: UIStatusBarStyle { 14 | return isHalfModalMaximized() ? .default : .lightContent 15 | } 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | // Do any additional setup after loading the view. 21 | } 22 | 23 | override func didReceiveMemoryWarning() { 24 | super.didReceiveMemoryWarning() 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | 29 | /* 30 | // MARK: - Navigation 31 | 32 | // In a storyboard-based application, you will often want to do a little preparation before navigation 33 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 34 | // Get the new view controller using segue.destinationViewController. 35 | // Pass the selected object to the new view controller. 36 | } 37 | */ 38 | 39 | } 40 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/ChannelSettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSettingsViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 5/20/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class ChannelSettingsViewController: UIViewController, HalfModalPresentable { 13 | 14 | public var uid: String! 15 | var dataController: DataController! 16 | var context: NSManagedObjectContext? = nil 17 | var delegate: ChannelSettingsDelegate? = nil 18 | 19 | private var channelData: ChannelData? = nil 20 | 21 | @IBOutlet weak var autoReadSwitch: UISwitch! 22 | @IBOutlet weak var markAllPostsButton: UIButton! 23 | @IBOutlet weak var closeButton: UIBarButtonItem! 24 | 25 | @IBAction func switchAutoRead(_ sender: UISwitch) { 26 | channelData?.autoRead = sender.isOn 27 | // TODO: Need to track failure to save and present error 28 | try? context?.save() 29 | } 30 | 31 | @IBAction func markAllPosts(_ sender: UIButton) { 32 | delegate?.markAllPostsAsRead() 33 | dismiss(animated: true, completion: nil) 34 | } 35 | 36 | @IBAction func close(_ sender: Any) { 37 | dismiss(animated: true, completion: nil) 38 | } 39 | 40 | func updateMarkAllPostsButtonText() { 41 | if let int32Count = channelData?.unreadCount { 42 | if let unreadCount = Int(exactly: int32Count) { 43 | if unreadCount > 0 { 44 | markAllPostsButton.setTitle("Mark All Posts Read", for: .normal) 45 | } 46 | } 47 | } else { 48 | markAllPostsButton.setTitle("Mark All Posts Unread", for: .normal) 49 | } 50 | } 51 | 52 | override func viewDidLoad() { 53 | super.viewDidLoad() 54 | 55 | view.backgroundColor = ThemeManager.currentTheme().backgroundColor 56 | view.layer.cornerRadius = 10 57 | view.clipsToBounds = true 58 | navigationController?.navigationBar.backgroundColor = ThemeManager.currentTheme().backgroundColor 59 | 60 | closeButton.image = UIImage.fontAwesomeIcon(name: .times, textColor: UIColor.black, size: CGSize(width: 30, height: 30)) 61 | 62 | context = dataController.persistentContainer.viewContext 63 | context?.automaticallyMergesChangesFromParent = true 64 | context?.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump 65 | context?.perform { [weak self] in 66 | if let context = self?.context, let uid = self?.uid, let channelData = try? ChannelData.findChannel(byId: uid, in: context) { 67 | self?.channelData = channelData 68 | if let autoReadOn = self?.channelData?.autoRead { 69 | self?.autoReadSwitch.isOn = autoReadOn 70 | self?.updateMarkAllPostsButtonText() 71 | } 72 | } 73 | } 74 | } 75 | 76 | override func didReceiveMemoryWarning() { 77 | super.didReceiveMemoryWarning() 78 | // Dispose of any resources that can be recreated. 79 | } 80 | 81 | /* 82 | // MARK: - Navigation 83 | 84 | // In a storyboard-based application, you will often want to do a little preparation before navigation 85 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 86 | // Get the new view controller using segue.destinationViewController. 87 | // Pass the selected object to the new view controller. 88 | } 89 | */ 90 | 91 | } 92 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/ChannelTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelTableViewCell.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ChannelTableViewCell: UITableViewCell { 12 | 13 | var data: Channel? = nil 14 | 15 | @IBOutlet weak var channelName: UILabel! 16 | @IBOutlet weak var unreadIndicator: UILabel! 17 | 18 | override func awakeFromNib() { 19 | super.awakeFromNib() 20 | // Initialization code 21 | } 22 | 23 | override func setSelected(_ selected: Bool, animated: Bool) { 24 | super.setSelected(selected, animated: animated) 25 | 26 | // Configure the view for the selected state 27 | } 28 | 29 | func setContent(ofChannel channel: Channel) { 30 | data = channel 31 | unreadIndicator.textColor = ThemeManager.currentTheme().mainColor 32 | channelName?.text = data!.name 33 | switch (data!.unread) { 34 | case .unreadCount(let count): 35 | channelName?.font = UIFont.boldSystemFont(ofSize: 17.0) 36 | unreadIndicator.isHidden = false 37 | unreadIndicator.text = "\(count)" 38 | case .unread: 39 | channelName?.font = UIFont.boldSystemFont(ofSize: 17.0) 40 | unreadIndicator.isHidden = false 41 | unreadIndicator.text = "◉" 42 | case .read: 43 | channelName?.font = UIFont.systemFont(ofSize: 17.0) 44 | unreadIndicator.isHidden = true 45 | case .none: 46 | channelName?.font = UIFont.systemFont(ofSize: 17.0) 47 | unreadIndicator.isHidden = true 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/Command.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Command.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 2/13/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Command { 12 | let name: String 13 | let url: URL 14 | let body: [String:String] 15 | var delegate: CommandDelegate? = nil 16 | 17 | init(name: String, url: URL, body: [String:String]) { 18 | self.name = name 19 | self.url = url 20 | self.body = body 21 | self.delegate = nil 22 | } 23 | 24 | func sendCommand(callback: ((_: Bool) -> ())? = nil) { 25 | delegate?.statusUpdate(runningStatus: true) 26 | var request = URLRequest(url: url) 27 | request.httpMethod = "POST" 28 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 29 | request.setValue(UAString(), forHTTPHeaderField: "User-Agent") 30 | 31 | var bodyString = "" 32 | for (key, value) in body { 33 | bodyString += "\(key)=\(value)&" 34 | } 35 | bodyString.remove(at: bodyString.index(before: bodyString.endIndex)) 36 | 37 | let bodyData = bodyString.data(using:String.Encoding.utf8, allowLossyConversion: false) 38 | request.httpBody = bodyData 39 | 40 | // set up the session 41 | let session = URLSession(configuration: URLSessionConfiguration.default) 42 | 43 | let task = session.dataTask(with: request) { (data, response, error) in 44 | // check for any errors 45 | guard error == nil else { 46 | print("error calling POST on \(self.url)") 47 | print(error ?? "No error present") 48 | return 49 | } 50 | 51 | // Check if endpoint is in the HTTP Header fields 52 | if let httpResponse = response as? HTTPURLResponse, let body = String(data: data!, encoding: .utf8) { 53 | if httpResponse.statusCode == 200 { 54 | // TODO: Find a way to communicate command status 55 | print("Command Successfully Sent") 56 | callback?(true) 57 | } else { 58 | print("Status Code not 200") 59 | print(httpResponse) 60 | print(body) 61 | callback?(false) 62 | } 63 | } 64 | self.delegate?.statusUpdate(runningStatus: false) 65 | } 66 | 67 | task.resume() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/CommandDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 5/15/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol CommandDelegate : NSObjectProtocol { 12 | func statusUpdate(runningStatus isRunning: Bool) -> Void 13 | } 14 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Channel/CommandTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandTableViewCell.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CommandTableViewCell: UITableViewCell, CommandDelegate { 12 | 13 | @IBOutlet weak var commandName: UILabel! 14 | @IBOutlet weak var commandActivity: UIActivityIndicatorView! 15 | 16 | override func awakeFromNib() { 17 | super.awakeFromNib() 18 | // Initialization code 19 | } 20 | 21 | override func setSelected(_ selected: Bool, animated: Bool) { 22 | super.setSelected(selected, animated: animated) 23 | 24 | // Configure the view for the selected state 25 | } 26 | 27 | func setContent(ofCommand command: Command) { 28 | commandActivity.isHidden = true 29 | commandActivity.color = ThemeManager.currentTheme().mainColor 30 | self.commandName?.text = command.name 31 | command.delegate = self 32 | } 33 | 34 | func statusUpdate(runningStatus isRunning: Bool) { 35 | self.isSelected = false 36 | if isRunning { 37 | self.commandActivity.startAnimating() 38 | self.commandActivity.isHidden = false 39 | } else { 40 | self.commandActivity.isHidden = true 41 | self.commandActivity.stopAnimating() 42 | } 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/FullView/FullView.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Timeline/TimelineCellDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineCellDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/23/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public protocol TimelineCellDelegate : NSObjectProtocol { 13 | func shareUrl(url: URL) -> Void 14 | func replyToUrl(url: URL) -> Void 15 | func moreOptions(post: Jf2Post, sourceButton: UIBarButtonItem) -> Void 16 | } 17 | -------------------------------------------------------------------------------- /IndiePass/App/Reader/Timeline/TimelineTextTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineTableViewCell.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/18/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TimelineTextTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var authorName: UILabel! 14 | @IBOutlet weak var authorPhoto: UIImageView! 15 | @IBOutlet weak var postContent: UILabel! 16 | @IBOutlet weak var postDate: UILabel! 17 | 18 | func setContent(ofPost post: Jf2Post) { 19 | 20 | postContent.text = post.name ?? post.content?.text ?? post.summary ?? "Content Can't Display" 21 | authorName.text = post.author?.name ?? "Unknown" 22 | 23 | if let authorImageUrl = post.author?.photo?[0], 24 | let authorImage = post.author?.photoImage?[authorImageUrl] { 25 | authorPhoto.image = authorImage.image 26 | } else { 27 | if post.author?.photo != nil, post.author!.photo!.count > 0 { 28 | post.author?.downloadPhoto(photoIndex: 0) { returnedAuthorPhoto in 29 | DispatchQueue.main.async { 30 | self.authorPhoto.image = returnedAuthorPhoto 31 | } 32 | } 33 | } else { 34 | authorPhoto.image = nil 35 | } 36 | } 37 | 38 | if let publishedDate = post.published { 39 | // let publishedDate = ISO8601DateFormatter().date(from: dateString) { 40 | 41 | if Calendar.current.isDateInToday(publishedDate) { 42 | postDate.text = "Today at " + DateFormatter.localizedString(from: publishedDate, dateStyle: .none, timeStyle: .short) 43 | } else { 44 | postDate.text = " " + DateFormatter.localizedString(from: publishedDate, dateStyle: .medium, timeStyle: .short) 45 | } 46 | } 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /IndiePass/App/Settings/AccountDebugViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccountDebugViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/31/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AccountDebugViewController: UIViewController { 12 | 13 | @IBOutlet weak var textView: UITextView! 14 | public var debugAccount: IndieAuthAccount? = nil 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | print("debugging") 19 | if let account = debugAccount { 20 | print(String(describing: debugAccount)) 21 | textView.text = "Debugging: \(account.profile.name ?? ""): \(account.profile.url?.absoluteString ?? "")\n\n" 22 | 23 | textView.text = textView.text + "Micropub Endpoint: \(account.micropub_endpoint)" 24 | if account.microsub_endpoint != nil { 25 | textView.text = textView.text + "Microsub Endpoint: \(String(describing: account.microsub_endpoint))" 26 | } 27 | textView.text = textView.text + "Scopes: \(String(describing: account.scope))\n\n" 28 | 29 | // textView.text = textView.text + "Access Token: \(account.access_token)\n\n" 30 | 31 | if let config = account.micropub_config { 32 | if let mediaEndpoint = config.mediaEndpoint { 33 | textView.text = textView.text + "Media Endpoint: \(mediaEndpoint)\n\n" 34 | } else { 35 | textView.text = textView.text + "No Media Endpoint Found\n\n" 36 | } 37 | 38 | if let syndicationTargets = config.syndicateTo { 39 | textView.text = textView.text + "Syndication Targets: \(String(describing: syndicationTargets))\n\n" 40 | } else { 41 | textView.text = textView.text + "No Syndication Targets Found\n\n" 42 | } 43 | } 44 | 45 | 46 | } 47 | } 48 | 49 | override func didReceiveMemoryWarning() { 50 | super.didReceiveMemoryWarning() 51 | // Dispose of any resources that can be recreated. 52 | } 53 | 54 | 55 | /* 56 | // MARK: - Navigation 57 | 58 | // In a storyboard-based application, you will often want to do a little preparation before navigation 59 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 60 | // Get the new view controller using segue.destinationViewController. 61 | // Pass the selected object to the new view controller. 62 | } 63 | */ 64 | 65 | } 66 | -------------------------------------------------------------------------------- /IndiePass/App/Settings/AccountViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccountViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Eddie Hinkle on 6/10/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AccountViewController: UIViewController { 12 | 13 | let defaults = UserDefaults(suiteName: AppGroup) 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | } 18 | 19 | override func viewWillAppear(_ animated: Bool) { 20 | super.viewWillAppear(animated) 21 | let activeAccount = defaults?.integer(forKey: "activeAccount") ?? 0 22 | if let micropubAccounts = defaults?.array(forKey: "micropubAccounts") as? [Data], 23 | let micropubDetails = try? JSONDecoder().decode(IndieAuthAccount.self, from: micropubAccounts[activeAccount]) { 24 | usernameDisplay.text = micropubDetails.me.absoluteString 25 | } 26 | } 27 | 28 | override func didReceiveMemoryWarning() { 29 | super.didReceiveMemoryWarning() 30 | // Dispose of any resources that can be recreated. 31 | } 32 | 33 | @IBOutlet weak var usernameDisplay: UILabel! 34 | 35 | @IBAction func logOutAccount(_ sender: UIButton) { 36 | let activeAccount = defaults?.integer(forKey: "activeAccount") ?? 0 37 | var micropubAccounts = defaults?.array(forKey: "micropubAccounts") as? [Data] 38 | micropubAccounts?.remove(at: activeAccount) 39 | defaults?.set(0, forKey: "activeAccount") 40 | defaults?.set(micropubAccounts, forKey: "micropubAccounts") 41 | 42 | if let numberOfAccounts = micropubAccounts?.count, numberOfAccounts < 1 { 43 | if let mainVC = self.parent as? MainViewController { 44 | mainVC.showLoginScreen() 45 | } 46 | } 47 | } 48 | 49 | /* 50 | // MARK: - Navigation 51 | 52 | // In a storyboard-based application, you will often want to do a little preparation before navigation 53 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 54 | // Get the new view controller using segue.destinationViewController. 55 | // Pass the selected object to the new view controller. 56 | } 57 | */ 58 | 59 | } 60 | -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-App-20x20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-App-20x20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-App-29x29@2x.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "Icon-App-40x40@2x.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-App-40x40@3x.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "Icon-App-60x60@2x.png", 47 | "idiom" : "iphone", 48 | "scale" : "2x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-App-60x60@3x.png", 53 | "idiom" : "iphone", 54 | "scale" : "3x", 55 | "size" : "60x60" 56 | }, 57 | { 58 | "filename" : "Icon-App-20x20@1x.png", 59 | "idiom" : "ipad", 60 | "scale" : "1x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-App-20x20@2x-1.png", 65 | "idiom" : "ipad", 66 | "scale" : "2x", 67 | "size" : "20x20" 68 | }, 69 | { 70 | "filename" : "Icon-App-29x29@1x-1.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-App-29x29@2x-1.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "29x29" 80 | }, 81 | { 82 | "filename" : "Icon-App-40x40@1x.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-App-40x40@2x-1.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "40x40" 92 | }, 93 | { 94 | "filename" : "Icon-App-76x76@1x.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-App-76x76@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "76x76" 104 | }, 105 | { 106 | "filename" : "Icon-App-83.5x83.5@2x.png", 107 | "idiom" : "ipad", 108 | "scale" : "2x", 109 | "size" : "83.5x83.5" 110 | }, 111 | { 112 | "filename" : "Icon-App-60x60@2x-1.png", 113 | "idiom" : "car", 114 | "scale" : "2x", 115 | "size" : "60x60" 116 | }, 117 | { 118 | "filename" : "Icon-App-60x60@3x-1.png", 119 | "idiom" : "car", 120 | "scale" : "3x", 121 | "size" : "60x60" 122 | }, 123 | { 124 | "filename" : "Icon-App-1024x1024.png", 125 | "idiom" : "ios-marketing", 126 | "scale" : "1x", 127 | "size" : "1024x1024" 128 | }, 129 | { 130 | "idiom" : "mac", 131 | "scale" : "1x", 132 | "size" : "16x16" 133 | }, 134 | { 135 | "idiom" : "mac", 136 | "scale" : "2x", 137 | "size" : "16x16" 138 | }, 139 | { 140 | "idiom" : "mac", 141 | "scale" : "1x", 142 | "size" : "32x32" 143 | }, 144 | { 145 | "idiom" : "mac", 146 | "scale" : "2x", 147 | "size" : "32x32" 148 | }, 149 | { 150 | "idiom" : "mac", 151 | "scale" : "1x", 152 | "size" : "128x128" 153 | }, 154 | { 155 | "idiom" : "mac", 156 | "scale" : "2x", 157 | "size" : "128x128" 158 | }, 159 | { 160 | "idiom" : "mac", 161 | "scale" : "1x", 162 | "size" : "256x256" 163 | }, 164 | { 165 | "idiom" : "mac", 166 | "scale" : "2x", 167 | "size" : "256x256" 168 | }, 169 | { 170 | "idiom" : "mac", 171 | "scale" : "1x", 172 | "size" : "512x512" 173 | }, 174 | { 175 | "idiom" : "mac", 176 | "scale" : "2x", 177 | "size" : "512x512" 178 | }, 179 | { 180 | "idiom" : "watch", 181 | "role" : "notificationCenter", 182 | "scale" : "2x", 183 | "size" : "24x24", 184 | "subtype" : "38mm" 185 | }, 186 | { 187 | "idiom" : "watch", 188 | "role" : "notificationCenter", 189 | "scale" : "2x", 190 | "size" : "27.5x27.5", 191 | "subtype" : "42mm" 192 | }, 193 | { 194 | "filename" : "Icon-App-29x29@2x-2.png", 195 | "idiom" : "watch", 196 | "role" : "companionSettings", 197 | "scale" : "2x", 198 | "size" : "29x29" 199 | }, 200 | { 201 | "filename" : "Icon-App-29x29@3x-1.png", 202 | "idiom" : "watch", 203 | "role" : "companionSettings", 204 | "scale" : "3x", 205 | "size" : "29x29" 206 | }, 207 | { 208 | "idiom" : "watch", 209 | "role" : "notificationCenter", 210 | "scale" : "2x", 211 | "size" : "33x33", 212 | "subtype" : "45mm" 213 | }, 214 | { 215 | "filename" : "Icon-App-40x40@2x-2.png", 216 | "idiom" : "watch", 217 | "role" : "appLauncher", 218 | "scale" : "2x", 219 | "size" : "40x40", 220 | "subtype" : "38mm" 221 | }, 222 | { 223 | "idiom" : "watch", 224 | "role" : "appLauncher", 225 | "scale" : "2x", 226 | "size" : "44x44", 227 | "subtype" : "40mm" 228 | }, 229 | { 230 | "idiom" : "watch", 231 | "role" : "appLauncher", 232 | "scale" : "2x", 233 | "size" : "46x46", 234 | "subtype" : "41mm" 235 | }, 236 | { 237 | "idiom" : "watch", 238 | "role" : "appLauncher", 239 | "scale" : "2x", 240 | "size" : "50x50", 241 | "subtype" : "44mm" 242 | }, 243 | { 244 | "idiom" : "watch", 245 | "role" : "appLauncher", 246 | "scale" : "2x", 247 | "size" : "51x51", 248 | "subtype" : "45mm" 249 | }, 250 | { 251 | "idiom" : "watch", 252 | "role" : "appLauncher", 253 | "scale" : "2x", 254 | "size" : "54x54", 255 | "subtype" : "49mm" 256 | }, 257 | { 258 | "idiom" : "watch", 259 | "role" : "quickLook", 260 | "scale" : "2x", 261 | "size" : "86x86", 262 | "subtype" : "38mm" 263 | }, 264 | { 265 | "idiom" : "watch", 266 | "role" : "quickLook", 267 | "scale" : "2x", 268 | "size" : "98x98", 269 | "subtype" : "42mm" 270 | }, 271 | { 272 | "idiom" : "watch", 273 | "role" : "quickLook", 274 | "scale" : "2x", 275 | "size" : "108x108", 276 | "subtype" : "44mm" 277 | }, 278 | { 279 | "idiom" : "watch", 280 | "role" : "quickLook", 281 | "scale" : "2x", 282 | "size" : "117x117", 283 | "subtype" : "45mm" 284 | }, 285 | { 286 | "idiom" : "watch", 287 | "role" : "quickLook", 288 | "scale" : "2x", 289 | "size" : "129x129", 290 | "subtype" : "49mm" 291 | }, 292 | { 293 | "idiom" : "watch-marketing", 294 | "scale" : "1x", 295 | "size" : "1024x1024" 296 | } 297 | ], 298 | "info" : { 299 | "author" : "xcode", 300 | "version" : 1 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/IndiePass/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /IndiePass/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /IndiePass/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /IndiePass/ChannelData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelData.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 5/10/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class ChannelData: NSManagedObject { 13 | 14 | class func findChannel(byId uid: String, in context: NSManagedObjectContext) throws -> ChannelData? { 15 | let request: NSFetchRequest = ChannelData.fetchRequest() 16 | request.predicate = NSPredicate(format: "uid = %@", uid) 17 | 18 | do { 19 | let matches = try context.fetch(request) 20 | if matches.count > 0 { 21 | assert(matches.count == 1, "ChannelData.findChannelById -- database inconsistancy") 22 | return matches[0] 23 | } 24 | } catch { 25 | throw error 26 | } 27 | 28 | return nil 29 | } 30 | 31 | class func findOrCreateChannel(matching channelInfo: Channel, in context: NSManagedObjectContext) throws -> ChannelData { 32 | let request: NSFetchRequest = ChannelData.fetchRequest() 33 | request.predicate = NSPredicate(format: "uid = %@", channelInfo.uid) 34 | 35 | do { 36 | let matches = try context.fetch(request) 37 | if matches.count > 0 { 38 | assert(matches.count == 1, "ChannelData.findOrCreateChannel -- database inconsistancy") 39 | return matches[0] 40 | } 41 | } catch { 42 | throw error 43 | } 44 | 45 | let channelData = ChannelData(context: context) 46 | channelData.uid = channelInfo.uid 47 | channelData.name = channelInfo.name 48 | channelData.unreadStatus = channelInfo.unread.readIdentifier 49 | channelData.unreadCount = Int32(exactly: channelInfo.unread.unreadCount) ?? 0 50 | return channelData 51 | } 52 | 53 | class func updateOrCreateChannel(matching channelInfo: Channel, in context: NSManagedObjectContext) throws -> ChannelData { 54 | let request: NSFetchRequest = ChannelData.fetchRequest() 55 | request.predicate = NSPredicate(format: "uid = %@", channelInfo.uid) 56 | 57 | do { 58 | let matches = try context.fetch(request) 59 | if matches.count > 0 { 60 | assert(matches.count == 1, "ChannelData.findOrCreateChannel -- database inconsistancy") 61 | let channelData = matches[0] 62 | channelData.name = channelInfo.name 63 | channelData.unreadStatus = channelInfo.unread.readIdentifier 64 | channelData.unreadCount = Int32(exactly: channelInfo.unread.unreadCount) ?? 0 65 | return channelData 66 | } 67 | } catch { 68 | throw error 69 | } 70 | 71 | let channelData = ChannelData(context: context) 72 | channelData.uid = channelInfo.uid 73 | channelData.name = channelInfo.name 74 | channelData.unreadStatus = channelInfo.unread.readIdentifier 75 | channelData.unreadCount = Int32(exactly: channelInfo.unread.unreadCount) ?? 0 76 | return channelData 77 | } 78 | 79 | class func updateOrCreateChannel(matching channelInfo: Channel, withPosition position: Int, in context: NSManagedObjectContext) throws -> ChannelData { 80 | let request: NSFetchRequest = ChannelData.fetchRequest() 81 | request.predicate = NSPredicate(format: "uid = %@", channelInfo.uid) 82 | 83 | do { 84 | let matches = try context.fetch(request) 85 | if matches.count > 0 { 86 | assert(matches.count == 1, "ChannelData.updateOrCreateChannel -- database inconsistancy") 87 | let channelData = matches[0] 88 | channelData.name = channelInfo.name 89 | channelData.unreadStatus = channelInfo.unread.readIdentifier 90 | channelData.unreadCount = Int32(exactly: channelInfo.unread.unreadCount) ?? 0 91 | channelData.sort = Int32(position) 92 | return channelData 93 | } 94 | } catch { 95 | throw error 96 | } 97 | 98 | let channelData = ChannelData(context: context) 99 | channelData.uid = channelInfo.uid 100 | channelData.name = channelInfo.name 101 | channelData.unreadStatus = channelInfo.unread.readIdentifier 102 | channelData.unreadCount = Int32(exactly: channelInfo.unread.unreadCount) ?? 0 103 | channelData.sort = Int32(position) 104 | return channelData 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /IndiePass/ChannelDataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelDataController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 5/10/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class ChannelDataController { 13 | 14 | var container: NSPersistentContainer! 15 | 16 | func saveContext() { 17 | let context = container.viewContext 18 | context.perform { 19 | try? context.save() 20 | } 21 | } 22 | 23 | func getChannel(byId uid: String, callback: @escaping ((ChannelData?) -> ())) { 24 | container.performBackgroundTask { context in 25 | if let channelData = try? ChannelData.findChannel(byId: uid, in: context) { 26 | callback(channelData) 27 | } else { 28 | callback(nil) 29 | } 30 | } 31 | } 32 | 33 | func getChannels() -> [Channel] { 34 | return [] 35 | } 36 | 37 | func filterChannels(forSearchText searchText: String) { 38 | // searchChannels = channels.filter { channel in 39 | // if searchText.isEmpty { 40 | // return true 41 | // } 42 | // return channel.name.lowercased().contains(searchText.lowercased()) 43 | // } 44 | // 45 | // tableView.reloadSections(IndexSet(integer: 0), with: .automatic) 46 | } 47 | 48 | func getChannelData(callback: (() -> ())? = nil) {} 49 | 50 | 51 | 52 | func printDebugStats() { 53 | if let context = container?.viewContext { 54 | context.perform { 55 | if let channelCount = try? context.count(for: ChannelData.fetchRequest()) { 56 | print("\(channelCount) Channels") 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /IndiePass/DataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/5/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class DataController { 13 | let persistentContainer: NSPersistentContainer 14 | 15 | var viewContext: NSManagedObjectContext { 16 | return persistentContainer.viewContext 17 | } 18 | 19 | init(modelName: String) { 20 | persistentContainer = NSPersistentContainer(name: modelName); 21 | } 22 | 23 | func configureContexts() { 24 | viewContext.automaticallyMergesChangesFromParent = true 25 | viewContext.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump 26 | } 27 | 28 | func load(completion: (() -> Void)? = nil) { 29 | /* 30 | The persistent container for the application. This implementation 31 | creates and returns a container, having loaded the store for the 32 | application to it. This property is optional since there are legitimate 33 | error conditions that could cause the creation of the store to fail. 34 | */ 35 | persistentContainer.loadPersistentStores { [weak self] (storeDescription, error) in 36 | guard error == nil else { 37 | // Replace this implementation with code to handle the error appropriately. 38 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 39 | 40 | /* 41 | Typical reasons for an error here include: 42 | * The parent directory does not exist, cannot be created, or disallows writing. 43 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 44 | * The device is out of space. 45 | * The store could not be migrated to the current model version. 46 | Check the error message to determine what the actual problem was. 47 | */ 48 | fatalError("Unresolved error \(error)") 49 | } 50 | self?.configureContexts() 51 | completion?() 52 | } 53 | } 54 | 55 | func saveContext () { 56 | let context = persistentContainer.viewContext 57 | context.perform { 58 | if context.hasChanges { 59 | do { 60 | try context.save() 61 | } catch { 62 | // Replace this implementation with code to handle the error appropriately. 63 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 64 | let nserror = error as NSError 65 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /IndiePass/IndiePass.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.com.popcoding.i-indie 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /IndiePass/IndiePass.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Indigenous.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /IndiePass/IndiePass.xcdatamodeld/Indigenous.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /IndiePass/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.3 19 | CFBundleURLTypes 20 | 21 | 22 | CFBundleTypeRole 23 | Editor 24 | CFBundleURLName 25 | app.indipass 26 | CFBundleURLSchemes 27 | 28 | indigenous-android 29 | 30 | 31 | 32 | CFBundleVersion 33 | 21 34 | Fabric 35 | 36 | APIKey 37 | 7ec553483ab72c66d57b9e378897c03b5d2d8618 38 | Kits 39 | 40 | 41 | KitInfo 42 | 43 | KitName 44 | Crashlytics 45 | 46 | 47 | 48 | LSRequiresIPhoneOS 49 | 50 | NSAppTransportSecurity 51 | 52 | NSAllowsArbitraryLoads 53 | 54 | 55 | NSPhotoLibraryUsageDescription 56 | Upload photos from your library to your media endpoint 57 | NSUserActivityTypes 58 | 59 | app.indiepass.viewTimeline 60 | 61 | UIBackgroundModes 62 | 63 | audio 64 | 65 | UILaunchStoryboardName 66 | LaunchScreen 67 | UIRequiredDeviceCapabilities 68 | 69 | armv7 70 | 71 | UISupportedInterfaceOrientations 72 | 73 | UIInterfaceOrientationPortrait 74 | UIInterfaceOrientationLandscapeLeft 75 | UIInterfaceOrientationLandscapeRight 76 | 77 | UISupportedInterfaceOrientations~ipad 78 | 79 | UIInterfaceOrientationPortrait 80 | UIInterfaceOrientationPortraitUpsideDown 81 | UIInterfaceOrientationLandscapeLeft 82 | UIInterfaceOrientationLandscapeRight 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /IndiePass/Kanna/libxmlParserOption.swift: -------------------------------------------------------------------------------- 1 | /**@file libxmlParserOption.swift 2 | 3 | Kanna 4 | 5 | Copyright (c) 2015 Atsushi Kiwaki (@_tid_) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | import Foundation 26 | 27 | #if SWIFT_PACKAGE 28 | import SwiftClibxml2 29 | #else 30 | import libxmlKanna 31 | #endif 32 | 33 | /* 34 | Libxml2HTMLParserOptions 35 | */ 36 | public struct Libxml2HTMLParserOptions : OptionSet { 37 | public typealias RawValue = UInt 38 | private var value: UInt = 0 39 | init(_ value: UInt) { self.value = value } 40 | private init(_ opt: htmlParserOption) { self.value = UInt(opt.rawValue) } 41 | public init(rawValue value: UInt) { self.value = value } 42 | public init(nilLiteral: ()) { self.value = 0 } 43 | public static var allZeros: Libxml2HTMLParserOptions { return .init(0) } 44 | static func fromMask(raw: UInt) -> Libxml2HTMLParserOptions { return .init(raw) } 45 | public var rawValue: UInt { return self.value } 46 | 47 | public static let STRICT = Libxml2HTMLParserOptions(0) 48 | public static let RECOVER = Libxml2HTMLParserOptions(HTML_PARSE_RECOVER) 49 | public static let NODEFDTD = Libxml2HTMLParserOptions(HTML_PARSE_NODEFDTD) 50 | public static let NOERROR = Libxml2HTMLParserOptions(HTML_PARSE_NOERROR) 51 | public static let NOWARNING = Libxml2HTMLParserOptions(HTML_PARSE_NOWARNING) 52 | public static let PEDANTIC = Libxml2HTMLParserOptions(HTML_PARSE_PEDANTIC) 53 | public static let NOBLANKS = Libxml2HTMLParserOptions(HTML_PARSE_NOBLANKS) 54 | public static let NONET = Libxml2HTMLParserOptions(HTML_PARSE_NONET) 55 | public static let NOIMPLIED = Libxml2HTMLParserOptions(HTML_PARSE_NOIMPLIED) 56 | public static let COMPACT = Libxml2HTMLParserOptions(HTML_PARSE_COMPACT) 57 | public static let IGNORE_ENC = Libxml2HTMLParserOptions(HTML_PARSE_IGNORE_ENC) 58 | } 59 | 60 | /* 61 | Libxml2XMLParserOptions 62 | */ 63 | public struct Libxml2XMLParserOptions: OptionSet { 64 | public typealias RawValue = UInt 65 | private var value: UInt = 0 66 | init(_ value: UInt) { self.value = value } 67 | private init(_ opt: xmlParserOption) { self.value = UInt(opt.rawValue) } 68 | public init(rawValue value: UInt) { self.value = value } 69 | public init(nilLiteral: ()) { self.value = 0 } 70 | public static var allZeros: Libxml2XMLParserOptions { return .init(0) } 71 | static func fromMask(raw: UInt) -> Libxml2XMLParserOptions { return .init(raw) } 72 | public var rawValue: UInt { return self.value } 73 | 74 | public static let STRICT = Libxml2XMLParserOptions(0) 75 | public static let RECOVER = Libxml2XMLParserOptions(XML_PARSE_RECOVER) 76 | public static let NOENT = Libxml2XMLParserOptions(XML_PARSE_NOENT) 77 | public static let DTDLOAD = Libxml2XMLParserOptions(XML_PARSE_DTDLOAD) 78 | public static let DTDATTR = Libxml2XMLParserOptions(XML_PARSE_DTDATTR) 79 | public static let DTDVALID = Libxml2XMLParserOptions(XML_PARSE_DTDVALID) 80 | public static let NOERROR = Libxml2XMLParserOptions(XML_PARSE_NOERROR) 81 | public static let NOWARNING = Libxml2XMLParserOptions(XML_PARSE_NOWARNING) 82 | public static let PEDANTIC = Libxml2XMLParserOptions(XML_PARSE_PEDANTIC) 83 | public static let NOBLANKS = Libxml2XMLParserOptions(XML_PARSE_NOBLANKS) 84 | public static let SAX1 = Libxml2XMLParserOptions(XML_PARSE_SAX1) 85 | public static let XINCLUDE = Libxml2XMLParserOptions(XML_PARSE_XINCLUDE) 86 | public static let NONET = Libxml2XMLParserOptions(XML_PARSE_NONET) 87 | public static let NODICT = Libxml2XMLParserOptions(XML_PARSE_NODICT) 88 | public static let NSCLEAN = Libxml2XMLParserOptions(XML_PARSE_NSCLEAN) 89 | public static let NOCDATA = Libxml2XMLParserOptions(XML_PARSE_NOCDATA) 90 | public static let NOXINCNODE = Libxml2XMLParserOptions(XML_PARSE_NOXINCNODE) 91 | public static let COMPACT = Libxml2XMLParserOptions(XML_PARSE_COMPACT) 92 | public static let OLD10 = Libxml2XMLParserOptions(XML_PARSE_OLD10) 93 | public static let NOBASEFIX = Libxml2XMLParserOptions(XML_PARSE_NOBASEFIX) 94 | public static let HUGE = Libxml2XMLParserOptions(XML_PARSE_HUGE) 95 | public static let OLDSAX = Libxml2XMLParserOptions(XML_PARSE_OLDSAX) 96 | public static let IGNORE_ENC = Libxml2XMLParserOptions(XML_PARSE_IGNORE_ENC) 97 | public static let BIG_LINES = Libxml2XMLParserOptions(XML_PARSE_BIG_LINES) 98 | } 99 | -------------------------------------------------------------------------------- /IndiePassTests/IndiePassTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndiePassTests.swift 3 | // IndiePassTests 4 | // 5 | // Created by Edward Hinkle on 11/1/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import IndiePass 11 | 12 | class IndiePassTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /IndiePassTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER)${BUNDLE_ID_SUFFIX} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /IndiePassUITests/IndiePassUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndiePassUITests.swift 3 | // IndiePassUITests 4 | // 5 | // Created by Edward Hinkle on 11/1/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class IndiePassUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // 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. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /IndiePassUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER)${BUNDLE_ID_SUFFIX} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Edward Hinkle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Micropub/Micropub.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Micropub.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 11/14/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func sendMicropub(forAction: MicropubResponseType, aboutUrl: URL, forUser user: IndieAuthAccount, completion: @escaping () -> Swift.Void) { 12 | 13 | DispatchQueue.global(qos: .background).async { 14 | var entryString = "" 15 | 16 | guard let encodedUrl = aboutUrl.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)?.replacingOccurrences(of: "+", with: "%2B") else { 17 | print("Encoding URL failed") 18 | return 19 | } 20 | 21 | print("Check on encoding url") 22 | print(aboutUrl.absoluteString) 23 | print(encodedUrl) 24 | 25 | switch(forAction) { 26 | case .like: 27 | entryString = "h=entry&like-of=\(encodedUrl)&summary=Liked: \(encodedUrl)" 28 | case .repost: 29 | entryString = "h=entry&repost-of=\(encodedUrl)&summary=Reposted: \(encodedUrl)" 30 | case .bookmark: 31 | entryString = "h=entry&bookmark-of=\(encodedUrl)&summary=Bookmarked: \(encodedUrl)" 32 | case .listen: 33 | entryString = "h=entry&listen-of=\(encodedUrl)&summary=Listened: \(encodedUrl)" 34 | default: 35 | print("ERROR") 36 | } 37 | 38 | var request = URLRequest(url: user.micropub_endpoint) 39 | request.httpMethod = "POST" 40 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 41 | request.setValue(UAString(), forHTTPHeaderField: "User-Agent") 42 | let bodyString = "\(entryString)&access_token=\(user.access_token)" 43 | let bodyData = bodyString.data(using:String.Encoding.utf8, allowLossyConversion: false) 44 | request.httpBody = bodyData 45 | 46 | // set up the session 47 | let config = URLSessionConfiguration.default 48 | let session = URLSession(configuration: config) 49 | 50 | let task = session.dataTask(with: request) { (data, response, error) in 51 | completion() 52 | } 53 | task.resume() 54 | } 55 | } 56 | 57 | func sendMicropub(note: String, forUser user: IndieAuthAccount, completion: @escaping () -> Swift.Void) { 58 | DispatchQueue.global(qos: .background).async { 59 | let entryString = "h=entry&content=\(note)" 60 | 61 | var request = URLRequest(url: user.micropub_endpoint) 62 | request.httpMethod = "POST" 63 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 64 | request.setValue(UAString(), forHTTPHeaderField: "User-Agent") 65 | let bodyString = "\(entryString)&access_token=\(user.access_token)" 66 | let bodyData = bodyString.data(using:String.Encoding.utf8, allowLossyConversion: false) 67 | request.httpBody = bodyData 68 | 69 | // set up the session 70 | let config = URLSessionConfiguration.default 71 | let session = URLSession(configuration: config) 72 | 73 | let task = session.dataTask(with: request) { (data, response, error) in 74 | completion() 75 | } 76 | task.resume() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Micropub/MicropubConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubConfig.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/10/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MicropubConfig: Codable { 12 | var mediaEndpoint: URL? 13 | var syndicateTo: [SyndicateTarget]? 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case mediaEndpoint = "media-endpoint" 17 | case syndicateTo = "syndicate-to" 18 | } 19 | 20 | public init(from decoder: Decoder) throws { 21 | let container = try decoder.container(keyedBy: CodingKeys.self) 22 | 23 | self.mediaEndpoint = try? container.decode(URL.self, forKey: .mediaEndpoint) 24 | var syndicationTargets = try? container.decode([SyndicateTarget].self, forKey: .syndicateTo) 25 | 26 | if syndicationTargets == nil, let syndicationUrls = try? container.decode([URL].self, forKey: .syndicateTo) { 27 | syndicationTargets = [] 28 | for syndicateUrl in syndicationUrls { 29 | syndicationTargets?.append(SyndicateTarget(uid: syndicateUrl)) 30 | } 31 | } 32 | 33 | self.syndicateTo = syndicationTargets 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Micropub/MicropubError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubError.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 2/8/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MicropubError: Error { 12 | case missingValue(String) 13 | } 14 | -------------------------------------------------------------------------------- /Micropub/MicropubPhoto.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubPhoto.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/18/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct MicropubPhoto: Codable { 12 | var image: UIImage? = nil 13 | var uploadedUrl: URL? = nil 14 | var progressPercent: Float? = nil 15 | 16 | public enum CodingKeys: String, CodingKey { 17 | case image 18 | } 19 | 20 | public init() { 21 | 22 | } 23 | 24 | public init(from decoder: Decoder) throws { 25 | let container = try decoder.container(keyedBy: CodingKeys.self) 26 | let data = try container.decode(Data.self, forKey: CodingKeys.image) 27 | guard let image = UIImage(data: data) else { 28 | print("Image unable to decode") 29 | return 30 | } 31 | 32 | self.image = image 33 | } 34 | 35 | public func encode(to encoder: Encoder) throws { 36 | var container = encoder.container(keyedBy: CodingKeys.self) 37 | 38 | if let imageFile = image { 39 | guard let data = imageFile.pngData() else { 40 | print("Image unable to encode") 41 | return 42 | } 43 | try? container.encode(data, forKey: CodingKeys.image) 44 | } 45 | } 46 | 47 | static func == (first: MicropubPhoto, second: MicropubPhoto) -> Bool { 48 | if first.image != second.image { 49 | return false 50 | } 51 | if first.uploadedUrl != second.uploadedUrl { 52 | return false 53 | } 54 | 55 | return true 56 | } 57 | 58 | // static func != (first: MicropubPhoto, second: MicropubPhoto) -> Bool { 59 | // return !(first == second) 60 | // } 61 | } 62 | -------------------------------------------------------------------------------- /Micropub/MicropubResponseType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubResponseType.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/29/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MicropubResponseType: String, Codable { 12 | case rsvp = "RSVP" 13 | case like = "Like" 14 | case repost = "Repost" 15 | case bookmark = "Bookmark" 16 | case listen = "Listened" 17 | case watch = "Watched" 18 | case read = "Read" 19 | case reply = "Reply" 20 | } 21 | -------------------------------------------------------------------------------- /Micropub/MicropubRsvpValue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubRsvpValues.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/13/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MicropubRsvpValue: String, Codable { 12 | case yes = "Yes" 13 | case no = "No" 14 | case maybe = "Maybe" 15 | case interested = "Interested" 16 | } 17 | -------------------------------------------------------------------------------- /Micropub/MicropubSendType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubSendType.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/9/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MicropubSendType: String { 12 | case urlencoded 13 | case json 14 | } 15 | -------------------------------------------------------------------------------- /Micropub/MicropubSyndicationQueryResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubSyndicationQueryResponse.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/31/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MicropubSyndicationQueryResponse: Codable { 12 | var syndicateTo: [SyndicateTarget]? 13 | 14 | enum CodingKeys: String, CodingKey { 15 | case syndicateTo = "syndicate-to" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Micropub/SyndicateTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyndicateTargets.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/10/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SyndicateTarget: Codable { 12 | let uid: URL 13 | let name: String 14 | 15 | public init(from decoder: Decoder) throws { 16 | let container = try decoder.container(keyedBy: CodingKeys.self) 17 | 18 | let uid = try? container.decode(URL.self, forKey: .uid) 19 | var name = try? container.decode(String.self, forKey: .name) 20 | 21 | if (name == nil) { 22 | name = uid?.absoluteString 23 | } 24 | 25 | if let uidValue = uid, let nameValue = name { 26 | self.uid = uidValue 27 | self.name = nameValue 28 | } else { 29 | throw MicropubError.missingValue("UID Not Found in Config") 30 | } 31 | } 32 | 33 | public init(uid: URL) { 34 | self.uid = uid 35 | self.name = uid.absoluteString 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Microsub/Author.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Author.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Author: Codable { 12 | let type: String? 13 | let name: String? 14 | let url: String? 15 | let photo: String? 16 | } 17 | -------------------------------------------------------------------------------- /Microsub/Channel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Channel.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Channel: Codable { 12 | let uid: String 13 | let name: String 14 | let unread: ChannelUnreadStatus 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case uid 18 | case name 19 | case unread 20 | } 21 | 22 | public init(uniqueId uid: String, withName name: String) { 23 | self.uid = uid 24 | self.name = name 25 | self.unread = .none 26 | } 27 | 28 | public init(fromData data: ChannelData) { 29 | uid = data.uid! 30 | name = data.name! 31 | unread = ChannelUnreadStatus(status: data.unreadStatus!, count: Int(exactly: data.unreadCount)!) 32 | } 33 | 34 | public init(from decoder: Decoder) throws { 35 | let container = try decoder.container(keyedBy: CodingKeys.self) 36 | 37 | uid = try! container.decode(String.self, forKey: .uid) 38 | name = try! container.decode(String.self, forKey: .name) 39 | 40 | if let boolValue = try? container.decodeIfPresent(Bool.self, forKey: .unread) { 41 | if boolValue != nil { 42 | if boolValue! { 43 | unread = .unread 44 | } else { 45 | unread = .read 46 | } 47 | } else { 48 | unread = .read 49 | } 50 | } else if let intValue = try! container.decodeIfPresent(Int.self, forKey: .unread) { 51 | if intValue > 0 { 52 | unread = .unreadCount(count: intValue) 53 | } else { 54 | unread = .read 55 | } 56 | } else { 57 | unread = .none 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Microsub/ChannelAPIResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelAPIResponse.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ChannelApiResponse: Codable { 12 | let channels: [Channel] 13 | } 14 | -------------------------------------------------------------------------------- /Microsub/ChannelUnreadStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelUnreadStatus.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 3/14/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ChannelUnreadStatus: Codable { 12 | case unreadCount(count: Int) 13 | case unread 14 | case read 15 | case none 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case unreadCount 19 | case unread 20 | case read 21 | case none 22 | } 23 | 24 | var readIdentifier: String { 25 | switch self { 26 | case .none: 27 | return "none" 28 | case .read: 29 | return "read" 30 | case .unread: 31 | return "unread" 32 | case .unreadCount: 33 | return "unreadCount" 34 | } 35 | } 36 | 37 | var unreadCount: Int { 38 | switch self { 39 | case .none: 40 | return -1 41 | case .read: 42 | return 0 43 | case .unread: 44 | return 1 45 | case .unreadCount(let count): 46 | return count 47 | } 48 | } 49 | 50 | public init(status: String, count: Int) { 51 | switch status { 52 | case "none": 53 | self = .none 54 | case "read": 55 | self = .read 56 | case "unread": 57 | self = .unread 58 | case "unreadCount": 59 | self = .unreadCount(count: count) 60 | default: 61 | self = .none 62 | } 63 | } 64 | 65 | public init(from decoder: Decoder) throws { 66 | 67 | let container = try decoder.container(keyedBy: CodingKeys.self) 68 | 69 | if let intValue = try! container.decodeIfPresent(Int.self, forKey: .unreadCount) { 70 | print("int: \(intValue)") 71 | if intValue > 0 { 72 | self = .unreadCount(count: intValue) 73 | } else { 74 | self = .read 75 | } 76 | } else if let boolValue = try! container.decodeIfPresent(Bool.self, forKey: .unread) { 77 | print("bool: \(boolValue)") 78 | if boolValue { 79 | self = .unread 80 | } else { 81 | self = .read 82 | } 83 | } else { 84 | self = .none 85 | } 86 | } 87 | 88 | public func encode(to encoder: Encoder) throws { 89 | var container = encoder.container(keyedBy: CodingKeys.self) 90 | switch self { 91 | case .unreadCount(let count): 92 | try container.encode(count, forKey: .unreadCount) 93 | case .unread: 94 | try container.encode(true, forKey: .unread) 95 | case .read: 96 | try container.encode(false, forKey: .read) 97 | case .none: 98 | try container.encode("null", forKey: .none) 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Microsub/TimelineApiPaging.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineApiPaging.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/25/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelineApiPaging: Codable { 12 | let before: String? 13 | let after: String? 14 | } 15 | -------------------------------------------------------------------------------- /Microsub/TimelineApiResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineApiResponse.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelineApiResponse: Codable { 12 | let items: [Jf2Post] 13 | let paging: TimelineApiPaging? 14 | 15 | public init(from decoder: Decoder) throws { 16 | let container = try decoder.container(keyedBy: CodingKeys.self) 17 | 18 | self.items = try container.decode([Jf2Post].self, forKey: .items) 19 | 20 | if let options = try? container.decode(TimelineApiPaging.self, forKey: .paging) { 21 | self.paging = options 22 | } else { 23 | self.paging = nil 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Microsub/TimelineMarkAsReadRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineMarkAsReadRequest.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 2/19/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelineMarkAsReadRequest: Codable { 12 | let action = "timeline" 13 | let channel: String 14 | let method: TimelineMarkAsReadMethod 15 | let entries: [String]? 16 | let lastReadEntry: String? 17 | 18 | init(channel: String, method: TimelineMarkAsReadMethod, entries: [String]) { 19 | self.channel = channel 20 | self.method = method 21 | self.entries = entries 22 | self.lastReadEntry = nil 23 | } 24 | 25 | init(channel: String, method: TimelineMarkAsReadMethod, lastReadEntry: String) { 26 | self.channel = channel 27 | self.method = method 28 | self.lastReadEntry = lastReadEntry 29 | self.entries = nil 30 | } 31 | 32 | enum CodingKeys: String, CodingKey { 33 | case action 34 | case channel 35 | case method 36 | case entries = "entry" 37 | case lastReadEntry = "last_read_entry" 38 | } 39 | 40 | public func encode(to encoder: Encoder) throws { 41 | var container = encoder.container(keyedBy: CodingKeys.self) 42 | 43 | try container.encode(action, forKey: .action) 44 | try container.encode(channel, forKey: .channel) 45 | try container.encode(method, forKey: .method) 46 | 47 | if (lastReadEntry != nil) { 48 | try container.encode(lastReadEntry, forKey: .lastReadEntry) 49 | } else { 50 | try container.encode(entries, forKey: .entries) 51 | } 52 | } 53 | 54 | public func toData() -> Data? { 55 | var dataString = "action=\(action)&channel=\(channel)&method=\(method.rawValue)" 56 | if (lastReadEntry != nil) { 57 | dataString += "&\(CodingKeys.lastReadEntry.rawValue)=\(lastReadEntry!)" 58 | } else { 59 | for entry in entries! { 60 | dataString += "&\(CodingKeys.entries.rawValue)[]=\(entry)" 61 | } 62 | } 63 | print("Checking data string") 64 | print(dataString) 65 | return dataString.data(using: .utf8, allowLossyConversion: false) 66 | } 67 | } 68 | 69 | enum TimelineMarkAsReadMethod: String, Codable { 70 | case MarkRead = "mark_read" 71 | case MarkUnread = "mark_unread" 72 | } 73 | -------------------------------------------------------------------------------- /Microsub/TimelineOptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineOptions.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/25/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelineOptions: Codable { 12 | let before: String? 13 | let after: String? 14 | 15 | public init(before: String?) { 16 | self.before = before 17 | self.after = nil 18 | } 19 | 20 | public init(after: String?) { 21 | self.after = after 22 | self.before = nil 23 | } 24 | 25 | public init(before: String?, after: String?) { 26 | self.before = before 27 | self.after = after 28 | } 29 | 30 | func asDictionary() -> [String: String] { 31 | var options: [String: String] = [:] 32 | if before != nil { 33 | options["before"] = before 34 | } 35 | if after != nil { 36 | options["after"] = after 37 | } 38 | return options 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Microsub/TimelinePost.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineItem.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelinePost: Codable { 12 | let type: String? 13 | let published: String? 14 | let url: String? 15 | let name: String? 16 | let author: Author? 17 | let category: [String]? 18 | let photo: [String]? 19 | let content: TimelinePostContent? 20 | let syndication: [String]? 21 | let summary: String? 22 | } 23 | -------------------------------------------------------------------------------- /Microsub/TimelinePostContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelinePostContent.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/20/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TimelinePostContent: Codable { 12 | let text: String? 13 | let html: String? 14 | } 15 | -------------------------------------------------------------------------------- /Modules/libxml2-kanna.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | -------------------------------------------------------------------------------- /Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | module libxmlKanna [system] { 2 | link "xml2" 3 | umbrella header "libxml2-kanna.h" 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /PostingInterface/PhotoUploadCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoUploadCollectionViewCell.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/16/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PhotoUploadCollectionViewCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var imageView: UIImageView! 14 | @IBOutlet weak var progressView: UIProgressView! 15 | 16 | } 17 | -------------------------------------------------------------------------------- /PostingInterface/PostingViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostingViewDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/16/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public protocol PostingViewDelegate: NSObjectProtocol { 13 | func removePostingView() -> Void 14 | } 15 | -------------------------------------------------------------------------------- /PostingInterface/SimpleSelectionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleSelectionDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/15/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol SimpleSelectionDelegate: SimpleSelectionReadOnlyDelegate { 12 | func newCreated(item: SimpleSelectionItem) -> Void 13 | } 14 | -------------------------------------------------------------------------------- /PostingInterface/SimpleSelectionItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleSelectionItem.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/14/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SimpleSelectionItem { 12 | var label: String 13 | var selected: Bool 14 | } 15 | -------------------------------------------------------------------------------- /PostingInterface/SimpleSelectionReadOnlyDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleSelectionReadOnlyDelegate.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/15/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol SimpleSelectionReadOnlyDelegate: NSObjectProtocol { 12 | func selectionWasUpdated(currentlySelected: [Int]) -> Void 13 | } 14 | -------------------------------------------------------------------------------- /PostingInterface/SimpleSelectionTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleSelectionTableViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/14/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SimpleSelectionTableViewController: UITableViewController { 12 | 13 | public var options: [SimpleSelectionItem] = [] 14 | public var selectedOptions: [Int] = [] 15 | public var delegate: SimpleSelectionReadOnlyDelegate? = nil 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | for (index, item) in options.enumerated() { 21 | if item.selected { 22 | selectedOptions.append(index) 23 | } 24 | } 25 | 26 | // Uncomment the following line to preserve selection between presentations 27 | // self.clearsSelectionOnViewWillAppear = false 28 | 29 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 30 | // self.navigationItem.rightBarButtonItem = self.editButtonItem 31 | } 32 | 33 | override func didReceiveMemoryWarning() { 34 | super.didReceiveMemoryWarning() 35 | // Dispose of any resources that can be recreated. 36 | } 37 | 38 | func add(item: SimpleSelectionItem) { 39 | options.append(item) 40 | selectedOptions.append(options.count - 1) 41 | tableView.beginUpdates() 42 | tableView.insertRows(at: [IndexPath(row: options.count - 1, section: 0)], with: .automatic) 43 | tableView.endUpdates() 44 | } 45 | 46 | // MARK: - Table view data source 47 | 48 | override func numberOfSections(in tableView: UITableView) -> Int { 49 | return 1 50 | } 51 | 52 | // override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 53 | // return "\(section)" 54 | // } 55 | 56 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 57 | return options.count 58 | } 59 | 60 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 61 | let cell = tableView.dequeueReusableCell(withIdentifier: "simpleSelectionCell", for: indexPath) 62 | cell.textLabel?.text = options[indexPath.row].label 63 | cell.accessoryType = (options[indexPath.row].selected ? .checkmark : .none) 64 | return cell 65 | } 66 | 67 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 68 | if (options[indexPath.row].selected) { 69 | if let indexOfSelected = selectedOptions.index(of: indexPath.row) { 70 | selectedOptions.remove(at: indexOfSelected) 71 | } 72 | } else { 73 | selectedOptions.append(indexPath.row) 74 | } 75 | options[indexPath.row].selected = !options[indexPath.row].selected 76 | tableView.reloadData() 77 | delegate?.selectionWasUpdated(currentlySelected: selectedOptions) 78 | } 79 | 80 | /* 81 | // Override to support conditional editing of the table view. 82 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 83 | // Return false if you do not want the specified item to be editable. 84 | return true 85 | } 86 | */ 87 | 88 | /* 89 | // Override to support editing the table view. 90 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 91 | if editingStyle == .delete { 92 | // Delete the row from the data source 93 | tableView.deleteRows(at: [indexPath], with: .fade) 94 | } else if editingStyle == .insert { 95 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 96 | } 97 | } 98 | */ 99 | 100 | /* 101 | // Override to support rearranging the table view. 102 | override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { 103 | 104 | } 105 | */ 106 | 107 | /* 108 | // Override to support conditional rearranging of the table view. 109 | override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { 110 | // Return false if you do not want the item to be re-orderable. 111 | return true 112 | } 113 | */ 114 | 115 | /* 116 | // MARK: - Navigation 117 | 118 | // In a storyboard-based application, you will often want to do a little preparation before navigation 119 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 120 | // Get the new view controller using segue.destinationViewController. 121 | // Pass the selected object to the new view controller. 122 | } 123 | */ 124 | } 125 | -------------------------------------------------------------------------------- /PostingInterface/SimpleSelectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleSelectionViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 1/14/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SimpleSelectionViewController: UIViewController, SimpleSelectionReadOnlyDelegate, UITextFieldDelegate { 12 | 13 | public var delegate: SimpleSelectionDelegate? = nil 14 | public var options: [SimpleSelectionItem] = [] 15 | public var readOnly: Bool = false 16 | var tableViewController: SimpleSelectionTableViewController? = nil 17 | 18 | @IBOutlet weak var searchField: UITextField! 19 | @IBOutlet weak var addButton: UIButton! 20 | 21 | @IBAction func createNewItem(_ sender: UIButton) { 22 | if !readOnly { 23 | add() 24 | } 25 | } 26 | 27 | func add() { 28 | if let newItemText = searchField.text, !newItemText.isEmpty { 29 | let newItem = SimpleSelectionItem(label: newItemText, selected: true) 30 | tableViewController?.add(item: newItem) 31 | delegate?.newCreated(item: newItem) 32 | searchField.text = "" 33 | } 34 | } 35 | 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | 39 | searchField.delegate = self 40 | if readOnly { 41 | addButton.isHidden = true 42 | } else { 43 | addButton.isHidden = false 44 | } 45 | 46 | // Uncomment the following line to preserve selection between presentations 47 | // self.clearsSelectionOnViewWillAppear = false 48 | 49 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 50 | // self.navigationItem.rightBarButtonItem = self.editButtonItem 51 | } 52 | 53 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 54 | if !readOnly { 55 | add() 56 | return true 57 | } else { 58 | return false 59 | } 60 | } 61 | 62 | override func viewWillAppear(_ animated: Bool) { 63 | super.viewWillAppear(animated) 64 | 65 | searchField.becomeFirstResponder() 66 | } 67 | 68 | override func didReceiveMemoryWarning() { 69 | super.didReceiveMemoryWarning() 70 | // Dispose of any resources that can be recreated. 71 | } 72 | 73 | func selectionWasUpdated(currentlySelected: [Int]) -> Void { 74 | delegate?.selectionWasUpdated(currentlySelected: currentlySelected) 75 | } 76 | 77 | // MARK: - Navigation 78 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 79 | if segue.identifier == "displayTagsView" { 80 | if let nextVC = segue.destination as? SimpleSelectionTableViewController { 81 | tableViewController = nextVC 82 | nextVC.options = self.options 83 | nextVC.delegate = self 84 | } 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Presentation/HalfModalPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HalfModalPresentationController.swift 3 | // HalfModalPresentationController 4 | // 5 | // Created by Martin Normark on 17/01/16. 6 | // Copyright © 2016 martinnormark. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HalfModalPresentationController : UIPresentationController { 12 | var isMaximized: Bool = false 13 | var isFullscreen: Bool = false 14 | 15 | let impactFeedback = UIImpactFeedbackGenerator() 16 | 17 | func adjustToFullScreen() { 18 | if let presentedView = presentedView, let containerView = self.containerView { 19 | UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: { () -> Void in 20 | presentedView.frame = containerView.frame 21 | self.isFullscreen = true 22 | 23 | if let navController = self.presentedViewController as? UINavigationController { 24 | self.isMaximized = true 25 | 26 | navController.setNeedsStatusBarAppearanceUpdate() 27 | 28 | // Force the navigation bar to update its size 29 | navController.isNavigationBarHidden = true 30 | navController.isNavigationBarHidden = false 31 | } 32 | }, completion: { [weak self] (_) -> Void in 33 | self?.impactFeedback.impactOccurred() 34 | }) 35 | } 36 | } 37 | 38 | func adjustToHalfScreen() { 39 | if let presentedView = presentedView, let containerView = self.containerView { 40 | UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: { () -> Void in 41 | 42 | presentedView.frame.origin.y = containerView.frame.height / 2 43 | self.isFullscreen = false 44 | 45 | if let navController = self.presentedViewController as? UINavigationController { 46 | self.isMaximized = false 47 | 48 | navController.setNeedsStatusBarAppearanceUpdate() 49 | 50 | // Force the navigation bar to update its size 51 | navController.isNavigationBarHidden = true 52 | navController.isNavigationBarHidden = false 53 | } 54 | }, completion: { [weak self] (_) -> Void in 55 | self?.impactFeedback.impactOccurred() 56 | }) 57 | } 58 | } 59 | 60 | override var frameOfPresentedViewInContainerView: CGRect { 61 | return CGRect(x: 0, y: containerView!.bounds.height / 2, width: containerView!.bounds.width, height: containerView!.bounds.height / 2) 62 | } 63 | 64 | override func presentationTransitionWillBegin() { 65 | if let containerView = self.containerView, let coordinator = presentingViewController.transitionCoordinator { 66 | 67 | coordinator.animate(alongsideTransition: { (context) -> Void in 68 | self.presentingViewController.view.alpha = 0.5 69 | }, completion: { [weak self] (_) -> Void in 70 | self?.impactFeedback.impactOccurred() 71 | }) 72 | } 73 | } 74 | 75 | override func dismissalTransitionWillBegin() { 76 | if let coordinator = presentingViewController.transitionCoordinator { 77 | 78 | coordinator.animate(alongsideTransition: { (context) -> Void in 79 | self.presentingViewController.view.alpha = 1 80 | }, completion: { [weak self] (completed) -> Void in 81 | print("done dismiss animation") 82 | self?.impactFeedback.impactOccurred() 83 | }) 84 | 85 | } 86 | } 87 | 88 | override func dismissalTransitionDidEnd(_ completed: Bool) { 89 | print("dismissal did end: \(completed)") 90 | 91 | 92 | if completed { 93 | isMaximized = false 94 | } 95 | } 96 | } 97 | 98 | protocol HalfModalPresentable { } 99 | 100 | extension HalfModalPresentable where Self: UIViewController { 101 | func maximizeToFullScreen() -> Void { 102 | if let presetation = navigationController?.presentationController as? HalfModalPresentationController { 103 | presetation.adjustToFullScreen() 104 | } 105 | } 106 | func reduceToHalfScreen() -> Void { 107 | if let presetation = navigationController?.presentationController as? HalfModalPresentationController { 108 | presetation.adjustToHalfScreen() 109 | } 110 | } 111 | func isHalfModalFullscreen() -> Bool { 112 | if let presetation = navigationController?.presentationController as? HalfModalPresentationController { 113 | return presetation.isFullscreen 114 | } 115 | 116 | return false 117 | } 118 | } 119 | 120 | extension HalfModalPresentable where Self: UINavigationController { 121 | func isHalfModalMaximized() -> Bool { 122 | if let presentationController = presentationController as? HalfModalPresentationController { 123 | return presentationController.isMaximized 124 | } 125 | 126 | return false 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Indigenous for iOS 2 | 3 | ### This codebase is no longer being actively developed. 4 | ### for the new version, please see https://github.com/marksuth/indiepass 5 | 6 | Thanks to [@swentel](https://github.com/swentel) for maintaining the iOS version for the past couple of years. 7 | The initial version was originally developed and released 2017-2019 by [Eddie Hinkle](https://EddieHinkle.com). 8 | -------------------------------------------------------------------------------- /SwiftExtensions/Data+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+Extension.swift 3 | // IndiePass 4 | // 5 | // Created by Antonio Rodrigues on 5/30/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Data { 11 | var html2AttributedString: NSAttributedString? { 12 | do { 13 | return try NSAttributedString(data: self, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) 14 | } catch { 15 | print("error:", error) 16 | return nil 17 | } 18 | } 19 | var html2String: String { html2AttributedString?.string ?? "" } 20 | } 21 | -------------------------------------------------------------------------------- /SwiftExtensions/String.extension.swift: -------------------------------------------------------------------------------- 1 | extension String { 2 | /* 3 | Truncates the string to the specified length number of characters and appends an optional trailing string if longer. 4 | - Parameter length: Desired maximum lengths of a string 5 | - Parameter trailing: A 'String' that will be appended after the truncation. 6 | 7 | - Returns: 'String' object. 8 | */ 9 | func trunc(length: Int, trailing: String = "…") -> String { 10 | return (self.count > length) ? self.prefix(length) + trailing : self 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SwiftExtensions/StringProtocol+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringProtocol+Extension.swift 3 | // IndiePass 4 | // 5 | // Created by Antonio Rodrigues on 5/30/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension StringProtocol { 11 | var html2AttributedString: NSAttributedString? { 12 | Data(utf8).html2AttributedString 13 | } 14 | var html2String: String { 15 | html2AttributedString?.string ?? "" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Theme/PXColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PXColor.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 6/21/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | #if os(OSX) 10 | 11 | import Cocoa 12 | public typealias PXColor = NSColor 13 | 14 | #else 15 | 16 | import UIKit 17 | public typealias PXColor = UIColor 18 | 19 | #endif 20 | 21 | extension PXColor { 22 | 23 | func lighter(amount : CGFloat = 0.25) -> PXColor { 24 | return hueColorWithBrightness(amount: 1 + amount) 25 | } 26 | 27 | func darker(amount : CGFloat = 0.25) -> PXColor { 28 | return hueColorWithBrightness(amount: 1 - amount) 29 | } 30 | 31 | private func hueColorWithBrightness(amount: CGFloat) -> PXColor { 32 | var hue : CGFloat = 0 33 | var saturation : CGFloat = 0 34 | var brightness : CGFloat = 0 35 | var alpha : CGFloat = 0 36 | 37 | #if os(iOS) 38 | 39 | if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) { 40 | return PXColor( hue: hue, 41 | saturation: saturation, 42 | brightness: brightness * amount, 43 | alpha: alpha ) 44 | } else { 45 | return self 46 | } 47 | 48 | #else 49 | 50 | getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) 51 | return PXColor( hue: hue, 52 | saturation: saturation, 53 | brightness: brightness * amount, 54 | alpha: alpha ) 55 | 56 | #endif 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Transitioning/HalfModalInteractiveTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HalfModalInteractiveTransition.swift 3 | // HalfModalPresentationController 4 | // 5 | // Created by Martin Normark on 28/01/16. 6 | // Copyright © 2016 martinnormark. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HalfModalInteractiveTransition: UIPercentDrivenInteractiveTransition { 12 | var viewController: UIViewController 13 | var presentingViewController: UIViewController? 14 | var panGestureRecognizer: UIPanGestureRecognizer 15 | 16 | var shouldComplete: Bool = false 17 | 18 | init(viewController: UIViewController, withView view:UIView, presentingViewController: UIViewController?) { 19 | self.viewController = viewController 20 | self.presentingViewController = presentingViewController 21 | self.panGestureRecognizer = UIPanGestureRecognizer() 22 | 23 | super.init() 24 | 25 | self.panGestureRecognizer.addTarget(self, action: #selector(onPan)) 26 | view.addGestureRecognizer(panGestureRecognizer) 27 | } 28 | 29 | override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) { 30 | super.startInteractiveTransition(transitionContext) 31 | 32 | print("start interactive") 33 | } 34 | 35 | override var completionSpeed: CGFloat { 36 | get { 37 | return 1.0 - self.percentComplete 38 | } 39 | set {} 40 | } 41 | 42 | @objc func onPan(pan: UIPanGestureRecognizer) -> Void { 43 | let translation = pan.translation(in: pan.view?.superview) 44 | 45 | switch pan.state { 46 | case .began: 47 | self.presentingViewController?.dismiss(animated: true, completion: nil) 48 | 49 | break 50 | 51 | case .changed: 52 | let screenHeight = UIScreen.main.bounds.size.height - 50 53 | let dragAmount = screenHeight 54 | let threshold: Float = 0.2 55 | var percent: Float = Float(translation.y) / Float(dragAmount) 56 | 57 | percent = fmaxf(percent, 0.0) 58 | percent = fminf(percent, 1.0) 59 | 60 | update(CGFloat(percent)) 61 | 62 | shouldComplete = percent > threshold 63 | 64 | break 65 | 66 | case .ended, .cancelled: 67 | if pan.state == .cancelled || !shouldComplete { 68 | cancel() 69 | 70 | print("cancel transition") 71 | } 72 | else { 73 | finish() 74 | 75 | print("finished transition") 76 | } 77 | 78 | break 79 | 80 | default: 81 | cancel() 82 | 83 | break 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Transitioning/HalfModalTransitionAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HalfModalTransitionAnimator.swift 3 | // HalfModalPresentationController 4 | // 5 | // Created by Martin Normark on 29/01/16. 6 | // Copyright © 2016 martinnormark. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HalfModalTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { 12 | 13 | var type: HalfModalTransitionAnimatorType 14 | 15 | init(type:HalfModalTransitionAnimatorType) { 16 | self.type = type 17 | } 18 | 19 | @objc func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 20 | let _ = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) 21 | let from = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) 22 | 23 | UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { () -> Void in 24 | 25 | from!.view.frame.origin.y = 800 26 | 27 | print("animating...") 28 | 29 | }) { (completed) -> Void in 30 | print("animate completed") 31 | 32 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 33 | } 34 | } 35 | 36 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 37 | return 0.4 38 | } 39 | } 40 | 41 | internal enum HalfModalTransitionAnimatorType { 42 | case Present 43 | case Dismiss 44 | } 45 | -------------------------------------------------------------------------------- /Transitioning/HalfModalTransitioningDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HalfModalTransitioningDelegate.swift 3 | // HalfModalPresentationController 4 | // 5 | // Created by Martin Normark on 17/01/16. 6 | // Copyright © 2016 martinnormark. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HalfModalTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { 12 | var viewController: UIViewController 13 | var presentingViewController: UIViewController 14 | var interactionController: HalfModalInteractiveTransition 15 | 16 | var interactiveDismiss = false 17 | 18 | init(viewController: UIViewController, presentingViewController: UIViewController) { 19 | self.viewController = viewController 20 | self.presentingViewController = presentingViewController 21 | self.interactionController = HalfModalInteractiveTransition(viewController: self.viewController, withView: self.presentingViewController.view, presentingViewController: self.presentingViewController) 22 | 23 | super.init() 24 | } 25 | 26 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 27 | return HalfModalTransitionAnimator(type: .Dismiss) 28 | } 29 | 30 | func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 31 | return HalfModalPresentationController(presentedViewController: presented, presenting: presenting) 32 | } 33 | 34 | func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { 35 | if interactiveDismiss { 36 | return self.interactionController 37 | } 38 | 39 | return nil 40 | } 41 | 42 | } 43 | 44 | extension UIViewController { } 45 | -------------------------------------------------------------------------------- /Utilities/RandomString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RandomString.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 4/17/18. 6 | // Copyright © 2018 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func RandomString(length: Int) -> String { 12 | 13 | let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 14 | let len = UInt32(letters.length) 15 | 16 | var randomString = "" 17 | 18 | for _ in 0 ..< length { 19 | let rand = arc4random_uniform(len) 20 | var nextChar = letters.character(at: Int(rand)) 21 | randomString += NSString(characters: &nextChar, length: 1) as String 22 | } 23 | 24 | return randomString 25 | } 26 | -------------------------------------------------------------------------------- /Utilities/UABuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UABuilder.swift 3 | // NonWVApp-SWIFT 4 | // 5 | 6 | import Foundation 7 | import UIKit 8 | 9 | //eg. Darwin/16.3.0 10 | func DarwinVersion() -> String { 11 | var sysinfo = utsname() 12 | uname(&sysinfo) 13 | let dv = String(bytes: Data(bytes: &sysinfo.release, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters) 14 | return "Darwin/\(dv)" 15 | } 16 | //eg. CFNetwork/808.3 17 | func CFNetworkVersion() -> String { 18 | let dictionary = Bundle(identifier: "com.apple.CFNetwork")?.infoDictionary! 19 | let version = dictionary?["CFBundleShortVersionString"] as! String 20 | return "CFNetwork/\(version)" 21 | } 22 | 23 | //eg. iOS/10_1 24 | func deviceVersion() -> String { 25 | let currentDevice = UIDevice.current 26 | return "\(currentDevice.systemName)/\(currentDevice.systemVersion)" 27 | } 28 | 29 | //eg. MyApp/1 30 | func appNameAndVersion() -> String { 31 | let dictionary = Bundle.main.infoDictionary! 32 | let version = dictionary["CFBundleShortVersionString"] as! String 33 | let name = dictionary["CFBundleName"] as! String 34 | return "\(name)/\(version)" 35 | } 36 | 37 | func UAString() -> String { 38 | return "\(appNameAndVersion()) \(deviceVersion()) \(CFNetworkVersion()) \(DarwinVersion())" 39 | } 40 | -------------------------------------------------------------------------------- /mf2/Mf2Type.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mf2Types.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/22/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Mf2Type: String, Codable { 12 | case card 13 | case entry 14 | case event 15 | case cite 16 | case review 17 | case recipe 18 | case product 19 | case item 20 | case app 21 | case feed 22 | case repo 23 | } 24 | -------------------------------------------------------------------------------- /share-micropub/AccountSelectorTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccountSelectorTableViewController.swift 3 | // share-micropub 4 | // 5 | // Created by Edward Hinkle on 12/27/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AccountSelectorTableViewController: UITableViewController { 12 | 13 | var userAccounts: [IndieAuthAccount] = [] 14 | var activeUserAccount: Int = 0 15 | var userAccountChanged: ((_ user: Int) -> Void)? = nil 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | refreshAccountData() 21 | tableView.delegate = self 22 | tableView.dataSource = self 23 | DispatchQueue.main.async { 24 | self.tableView.reloadData() 25 | } 26 | } 27 | 28 | func refreshAccountData() { 29 | print("refreshing data") 30 | userAccounts.removeAll() 31 | let defaults = UserDefaults(suiteName: "group.com.popcoding.i-indie") 32 | let micropubAccounts = defaults?.array(forKey: "micropubAccounts") as? [Data] ?? [Data]() 33 | micropubAccounts.forEach { userData in 34 | if let newAccount = try? JSONDecoder().decode(IndieAuthAccount.self, from: userData) { 35 | userAccounts.append(newAccount) 36 | } 37 | } 38 | print("accounts?") 39 | print(userAccounts.count) 40 | } 41 | 42 | override func didReceiveMemoryWarning() { 43 | super.didReceiveMemoryWarning() 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | // MARK: - Table view data source 48 | 49 | override func numberOfSections(in tableView: UITableView) -> Int { 50 | return 1 51 | } 52 | 53 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 54 | return userAccounts.count 55 | } 56 | 57 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 58 | let cell = tableView.dequeueReusableCell(withIdentifier: "AccountCell", for: indexPath) 59 | 60 | cell.textLabel?.text = IndieAuth.getSimpleDomain(forAccount: userAccounts[indexPath.row]) 61 | 62 | if (activeUserAccount == indexPath.row) { 63 | cell.accessoryType = .checkmark 64 | } else { 65 | cell.accessoryType = .none 66 | } 67 | 68 | return cell 69 | } 70 | 71 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 72 | 73 | // let defaults = UserDefaults(suiteName: "group.software.studioh.indigenous") 74 | 75 | activeUserAccount = indexPath.row 76 | refreshAccountData() 77 | DispatchQueue.main.async { 78 | self.tableView.reloadData() 79 | } 80 | userAccountChanged?(activeUserAccount) 81 | self.navigationController?.popViewController(animated: true) 82 | } 83 | 84 | /* 85 | // Override to support conditional editing of the table view. 86 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 87 | // Return false if you do not want the specified item to be editable. 88 | return true 89 | } 90 | */ 91 | 92 | /* 93 | // Override to support editing the table view. 94 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 95 | if editingStyle == .delete { 96 | // Delete the row from the data source 97 | tableView.deleteRows(at: [indexPath], with: .fade) 98 | } else if editingStyle == .insert { 99 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 100 | } 101 | } 102 | */ 103 | 104 | /* 105 | // Override to support rearranging the table view. 106 | override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { 107 | 108 | } 109 | */ 110 | 111 | /* 112 | // Override to support conditional rearranging of the table view. 113 | override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { 114 | // Return false if you do not want the item to be re-orderable. 115 | return true 116 | } 117 | */ 118 | 119 | /* 120 | // MARK: - Navigation 121 | 122 | // In a storyboard-based application, you will often want to do a little preparation before navigation 123 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 124 | // Get the new view controller using segue.destinationViewController. 125 | // Pass the selected object to the new view controller. 126 | } 127 | */ 128 | 129 | } 130 | -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x-1.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x-1.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x-1.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x-1.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024.png", 115 | "scale" : "1x" 116 | }, 117 | { 118 | "size" : "60x60", 119 | "idiom" : "car", 120 | "filename" : "Icon-App-60x60@2x-1.png", 121 | "scale" : "2x" 122 | }, 123 | { 124 | "size" : "60x60", 125 | "idiom" : "car", 126 | "filename" : "Icon-App-60x60@3x-1.png", 127 | "scale" : "3x" 128 | }, 129 | { 130 | "size" : "24x24", 131 | "idiom" : "watch", 132 | "scale" : "2x", 133 | "role" : "notificationCenter", 134 | "subtype" : "38mm" 135 | }, 136 | { 137 | "size" : "27.5x27.5", 138 | "idiom" : "watch", 139 | "scale" : "2x", 140 | "role" : "notificationCenter", 141 | "subtype" : "42mm" 142 | }, 143 | { 144 | "size" : "29x29", 145 | "idiom" : "watch", 146 | "filename" : "Icon-App-29x29@2x-2.png", 147 | "role" : "companionSettings", 148 | "scale" : "2x" 149 | }, 150 | { 151 | "size" : "29x29", 152 | "idiom" : "watch", 153 | "filename" : "Icon-App-29x29@3x-1.png", 154 | "role" : "companionSettings", 155 | "scale" : "3x" 156 | }, 157 | { 158 | "size" : "40x40", 159 | "idiom" : "watch", 160 | "filename" : "Icon-App-40x40@2x-2.png", 161 | "scale" : "2x", 162 | "role" : "appLauncher", 163 | "subtype" : "38mm" 164 | }, 165 | { 166 | "size" : "86x86", 167 | "idiom" : "watch", 168 | "scale" : "2x", 169 | "role" : "quickLook", 170 | "subtype" : "38mm" 171 | }, 172 | { 173 | "size" : "98x98", 174 | "idiom" : "watch", 175 | "scale" : "2x", 176 | "role" : "quickLook", 177 | "subtype" : "42mm" 178 | }, 179 | { 180 | "idiom" : "watch-marketing", 181 | "size" : "1024x1024", 182 | "scale" : "1x" 183 | }, 184 | { 185 | "idiom" : "mac", 186 | "size" : "16x16", 187 | "scale" : "1x" 188 | }, 189 | { 190 | "idiom" : "mac", 191 | "size" : "16x16", 192 | "scale" : "2x" 193 | }, 194 | { 195 | "idiom" : "mac", 196 | "size" : "32x32", 197 | "scale" : "1x" 198 | }, 199 | { 200 | "idiom" : "mac", 201 | "size" : "32x32", 202 | "scale" : "2x" 203 | }, 204 | { 205 | "idiom" : "mac", 206 | "size" : "128x128", 207 | "scale" : "1x" 208 | }, 209 | { 210 | "idiom" : "mac", 211 | "size" : "128x128", 212 | "scale" : "2x" 213 | }, 214 | { 215 | "idiom" : "mac", 216 | "size" : "256x256", 217 | "scale" : "1x" 218 | }, 219 | { 220 | "idiom" : "mac", 221 | "size" : "256x256", 222 | "scale" : "2x" 223 | }, 224 | { 225 | "idiom" : "mac", 226 | "size" : "512x512", 227 | "scale" : "1x" 228 | }, 229 | { 230 | "idiom" : "mac", 231 | "size" : "512x512", 232 | "scale" : "2x" 233 | } 234 | ], 235 | "info" : { 236 | "version" : 1, 237 | "author" : "xcode" 238 | } 239 | } -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-2.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-2.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x-1.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IndiePass/indiepass-ios/447272811001b313ee42e6b355e484b7b86494ac/share-micropub/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /share-micropub/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /share-micropub/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Micropub 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0.3 21 | CFBundleVersion 22 | 21 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSExtension 29 | 30 | NSExtensionAttributes 31 | 32 | NSExtensionActivationRule 33 | 34 | NSExtensionActivationSupportsText 35 | 36 | NSExtensionActivationSupportsWebPageWithMaxCount 37 | 10 38 | NSExtensionActivationSupportsWebURLWithMaxCount 39 | 10 40 | 41 | NSExtensionJavaScriptPreprocessingFile 42 | mf2parsing 43 | 44 | NSExtensionMainStoryboard 45 | MainInterface 46 | NSExtensionPointIdentifier 47 | com.apple.share-services 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /share-micropub/MicropubShareViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MicropubShareViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Eddie Hinkle on 6/10/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Social 11 | 12 | class MicropubShareViewController: UIViewController, UINavigationControllerDelegate { 13 | 14 | var halfModalTransitioningDelegate: HalfModalTransitioningDelegate? 15 | var extensionItems: [NSExtensionItem] = [] 16 | 17 | override func viewWillAppear(_ animated: Bool) { 18 | super.viewWillAppear(animated) 19 | } 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | // TODO: Can't decide if I want to use the theme manager inside the Share Extension 25 | // ThemeManager.applyTheme(theme: .blue) 26 | 27 | let defaults = UserDefaults(suiteName: "group.app.indiepass") 28 | let micropubAccounts = defaults?.array(forKey: "micropubAccounts") as? [Data] ?? [Data]() 29 | 30 | guard micropubAccounts.count > 0 else { 31 | let alert = UIAlertController(title: "Not logged in", message: "You are not currently logged in. Please open IndiePass and log in before using the Share extension.", preferredStyle: .alert) 32 | alert.addAction(UIAlertAction(title: "Cancel", style: .default) { action in 33 | self.cancelShareSheet() 34 | }) 35 | self.present(alert, animated: true) 36 | return 37 | } 38 | 39 | extensionItems = extensionContext?.inputItems as! [NSExtensionItem] 40 | performSegue(withIdentifier: "presentModalNav", sender: nil) 41 | } 42 | 43 | override func didReceiveMemoryWarning() { 44 | super.didReceiveMemoryWarning() 45 | // Dispose of any resources that can be recreated. 46 | } 47 | 48 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 49 | super.prepare(for: segue, sender: sender) 50 | 51 | self.halfModalTransitioningDelegate = HalfModalTransitioningDelegate(viewController: self, presentingViewController: segue.destination) 52 | 53 | segue.destination.modalPresentationStyle = .custom 54 | segue.destination.transitioningDelegate = self.halfModalTransitioningDelegate 55 | 56 | if let modalNavVC = segue.destination as? ModalNavController { 57 | modalNavVC.delegate = self 58 | if let shareVC = modalNavVC.viewControllers.first as? ShareViewController { 59 | shareVC.extensionItems = extensionItems 60 | } 61 | } 62 | } 63 | 64 | public func finishedShareSheet() { 65 | self.extensionContext!.completeRequest(returningItems: nil, completionHandler: nil) 66 | } 67 | 68 | public func cancelShareSheet() { 69 | self.extensionContext!.cancelRequest(withError: NSError(domain: "app.indiepass", code: 1)) 70 | } 71 | 72 | /* 73 | // MARK: - Navigation 74 | 75 | // In a storyboard-based application, you will often want to do a little preparation before navigation 76 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 77 | // Get the new view controller using segue.destinationViewController. 78 | // Pass the selected object to the new view controller. 79 | } 80 | */ 81 | 82 | } 83 | -------------------------------------------------------------------------------- /share-micropub/ModalNavController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModalNavController.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 11/9/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ModalNavController: UINavigationController, HalfModalPresentable { 12 | override var preferredStatusBarStyle: UIStatusBarStyle { 13 | return isHalfModalMaximized() ? .default : .lightContent 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /share-micropub/ReplyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplyViewController.swift 3 | // IndiePass 4 | // 5 | // Created by Eddie Hinkle on 7/14/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ReplyViewController: UIViewController { 12 | 13 | @IBOutlet weak var replyLabel: UILabel! 14 | @IBOutlet weak var noteText: UITextView! 15 | 16 | var replyUrl: URL? = nil 17 | 18 | override func viewWillAppear(_ animated: Bool) { 19 | super.viewWillAppear(animated) 20 | 21 | self.automaticallyAdjustsScrollViewInsets = false 22 | 23 | // let keyboardToolbar = UIToolbar() 24 | // keyboardToolbar.sizeToFit() 25 | // keyboardToolbar.isTranslucent = false 26 | // keyboardToolbar.barTintColor = UIColor.white 27 | // 28 | // let addButton = UIBarButtonItem( 29 | // barButtonSystemItem: .done, 30 | // target: self, 31 | // action: #selector(buttonPressed) 32 | // ) 33 | // addButton.tintColor = UIColor.black 34 | // keyboardToolbar.items = [addButton] 35 | // noteText.inputAccessoryView = keyboardToolbar 36 | } 37 | 38 | func buttonPressed() { 39 | print("test"); 40 | } 41 | 42 | override func didReceiveMemoryWarning() { 43 | super.didReceiveMemoryWarning() 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | 48 | /* 49 | // MARK: - Navigation 50 | 51 | // In a storyboard-based application, you will often want to do a little preparation before navigation 52 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 53 | // Get the new view controller using segue.destinationViewController. 54 | // Pass the selected object to the new view controller. 55 | } 56 | */ 57 | 58 | } 59 | -------------------------------------------------------------------------------- /share-micropub/mf2parsing.js: -------------------------------------------------------------------------------- 1 | var GetURL = function() {}; 2 | GetURL.prototype = { 3 | run: function(arguments) { 4 | arguments.completionFunction({"URL": document.URL}); 5 | } 6 | }; 7 | var ExtensionPreprocessingJS = new GetURL; 8 | -------------------------------------------------------------------------------- /share-micropub/share-micropub.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.com.popcoding.i-indie 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /voice-notes/voice-notesBeta.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.software.studioh.indigenous.beta 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /xray/XRayParsingResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XRayParsingResponse.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/28/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct XRayParsingResponse: Codable { 12 | let url: URL 13 | let code: Int 14 | let data: Jf2Post 15 | } 16 | -------------------------------------------------------------------------------- /xray/xray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // xray.swift 3 | // IndiePass 4 | // 5 | // Created by Edward Hinkle on 12/28/17. 6 | // Copyright © 2017 Studio H, LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class XRay { 12 | 13 | public static func parse(url: URL, completion: @escaping (_ parsedData: XRayParsingResponse?, _ errorMessage: String?) -> ()) { 14 | 15 | let xrayUrl = URL(string: "https://xray.abode.pub/parse")! 16 | let requestBody: String = "url=\(url.absoluteString)" 17 | 18 | var request = URLRequest(url: xrayUrl) 19 | request.httpMethod = "POST" 20 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 21 | request.setValue(UAString(), forHTTPHeaderField: "User-Agent") 22 | request.httpBody = requestBody.data(using: .utf8, allowLossyConversion: false) 23 | 24 | // set up the session 25 | let config = URLSessionConfiguration.default 26 | let session = URLSession(configuration: config) 27 | 28 | let task = session.dataTask(with: request) { (data, response, error) in 29 | // check for any errors 30 | guard error == nil else { 31 | print("error calling POST on \(xrayUrl) with \(requestBody)") 32 | print(error ?? "No error present") 33 | return 34 | } 35 | 36 | if let httpResponse = response as? HTTPURLResponse { 37 | if httpResponse.statusCode == 200 { 38 | if let contentType = httpResponse.allHeaderFields["Content-Type"] as? String { 39 | if contentType == "application/json" { 40 | print("content type is application json") 41 | // print(String(data: data!, encoding: .utf8)) 42 | if let parsedPost = try? JSONDecoder().decode(XRayParsingResponse.self, from: data!) { 43 | print("finished jf2") 44 | print(parsedPost) 45 | completion(parsedPost, nil) 46 | } 47 | } else { 48 | print("content type wrong") 49 | print(httpResponse) 50 | completion(nil, "Recieved the following HTTP Status Code " + String(httpResponse.statusCode)) 51 | } 52 | } 53 | } else { 54 | print("status code wrong") 55 | print(httpResponse) 56 | completion(nil, "Recieved the following HTTP Status Code " + String(httpResponse.statusCode)) 57 | } 58 | } 59 | } 60 | task.resume() 61 | 62 | } 63 | 64 | } 65 | --------------------------------------------------------------------------------