├── .gitignore ├── APNsKit.xcodeproj ├── APNsKit.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── APNsKit.xcscmblueprint ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── APNsKit.xcscmblueprint └── xcshareddata │ └── xcschemes │ └── APNsKit.xcscheme ├── APNsKit.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── APNsKit.xcscmblueprint ├── APNsKit ├── APNsKit.h ├── APNsPayload.swift ├── APNsPort.swift ├── APNsRequest.swift ├── APNsServer.swift ├── Connection.swift ├── Info.plist ├── Main.storyboard ├── PKCS12Adapter.swift └── PushViewController.playground │ ├── Contents.swift │ ├── Sources │ └── PushViewController.swift │ └── contents.xcplayground ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode (from gitignore.io) 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | *.xccheckout 13 | *.moved-aside 14 | DerivedData 15 | *.hmap 16 | *.ipa 17 | *.xcuserstate 18 | 19 | # others 20 | *.swp 21 | !.gitkeep 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /APNsKit.xcodeproj/APNsKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /APNsKit.xcodeproj/APNsKit.xcworkspace/xcshareddata/APNsKit.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" : 9223372036854775807, 8 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : 9223372036854775807 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "05E42A2B-40F8-4295-A7A4-C15C0F9F496D", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" : "APNsKit\/", 13 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : "" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "APNsKit", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "APNsKit.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/APNsKit.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/Parrot.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CA86F53ED6CB060B09097483BD033980A4C323F8" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /APNsKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C0644ABE1E78394600334798 /* APNsKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C0644ABC1E78394600334798 /* APNsKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | C0644ACA1E783A7000334798 /* APNsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC41E783A7000334798 /* APNsPayload.swift */; }; 12 | C0644ACB1E783A7000334798 /* APNsPort.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC51E783A7000334798 /* APNsPort.swift */; }; 13 | C0644ACC1E783A7000334798 /* APNsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC61E783A7000334798 /* APNsRequest.swift */; }; 14 | C0644ACD1E783A7000334798 /* APNsServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC71E783A7000334798 /* APNsServer.swift */; }; 15 | C0644ACE1E783A7000334798 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC81E783A7000334798 /* Connection.swift */; }; 16 | C0644ACF1E783A7000334798 /* PKCS12Adapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0644AC91E783A7000334798 /* PKCS12Adapter.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | C0644AB91E78394600334798 /* APNsKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = APNsKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | C0644ABC1E78394600334798 /* APNsKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = APNsKit.h; sourceTree = ""; }; 22 | C0644ABD1E78394600334798 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | C0644AC41E783A7000334798 /* APNsPayload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APNsPayload.swift; sourceTree = ""; }; 24 | C0644AC51E783A7000334798 /* APNsPort.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APNsPort.swift; sourceTree = ""; }; 25 | C0644AC61E783A7000334798 /* APNsRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APNsRequest.swift; sourceTree = ""; }; 26 | C0644AC71E783A7000334798 /* APNsServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APNsServer.swift; sourceTree = ""; }; 27 | C0644AC81E783A7000334798 /* Connection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; 28 | C0644AC91E783A7000334798 /* PKCS12Adapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS12Adapter.swift; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | C0644AB51E78394600334798 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | C0644AAF1E78394600334798 = { 43 | isa = PBXGroup; 44 | children = ( 45 | C0644ABB1E78394600334798 /* APNsKit */, 46 | C0644ABA1E78394600334798 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | C0644ABA1E78394600334798 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | C0644AB91E78394600334798 /* APNsKit.framework */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | C0644ABB1E78394600334798 /* APNsKit */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | C0644ABC1E78394600334798 /* APNsKit.h */, 62 | C0644ABD1E78394600334798 /* Info.plist */, 63 | C0644AC41E783A7000334798 /* APNsPayload.swift */, 64 | C0644AC51E783A7000334798 /* APNsPort.swift */, 65 | C0644AC61E783A7000334798 /* APNsRequest.swift */, 66 | C0644AC71E783A7000334798 /* APNsServer.swift */, 67 | C0644AC81E783A7000334798 /* Connection.swift */, 68 | C0644AC91E783A7000334798 /* PKCS12Adapter.swift */, 69 | ); 70 | path = APNsKit; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXHeadersBuildPhase section */ 76 | C0644AB61E78394600334798 /* Headers */ = { 77 | isa = PBXHeadersBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | C0644ABE1E78394600334798 /* APNsKit.h in Headers */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXHeadersBuildPhase section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | C0644AB81E78394600334798 /* APNsKit */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = C0644AC11E78394600334798 /* Build configuration list for PBXNativeTarget "APNsKit" */; 90 | buildPhases = ( 91 | C0644AB41E78394600334798 /* Sources */, 92 | C0644AB51E78394600334798 /* Frameworks */, 93 | C0644AB61E78394600334798 /* Headers */, 94 | C0644AB71E78394600334798 /* Resources */, 95 | ); 96 | buildRules = ( 97 | ); 98 | dependencies = ( 99 | ); 100 | name = APNsKit; 101 | productName = APNsKit; 102 | productReference = C0644AB91E78394600334798 /* APNsKit.framework */; 103 | productType = "com.apple.product-type.framework"; 104 | }; 105 | /* End PBXNativeTarget section */ 106 | 107 | /* Begin PBXProject section */ 108 | C0644AB01E78394600334798 /* Project object */ = { 109 | isa = PBXProject; 110 | attributes = { 111 | LastUpgradeCheck = 0820; 112 | ORGANIZATIONNAME = "Haga Masaki"; 113 | TargetAttributes = { 114 | C0644AB81E78394600334798 = { 115 | CreatedOnToolsVersion = 8.2.1; 116 | ProvisioningStyle = Automatic; 117 | }; 118 | }; 119 | }; 120 | buildConfigurationList = C0644AB31E78394600334798 /* Build configuration list for PBXProject "APNsKit" */; 121 | compatibilityVersion = "Xcode 3.2"; 122 | developmentRegion = English; 123 | hasScannedForEncodings = 0; 124 | knownRegions = ( 125 | en, 126 | ); 127 | mainGroup = C0644AAF1E78394600334798; 128 | productRefGroup = C0644ABA1E78394600334798 /* Products */; 129 | projectDirPath = ""; 130 | projectRoot = ""; 131 | targets = ( 132 | C0644AB81E78394600334798 /* APNsKit */, 133 | ); 134 | }; 135 | /* End PBXProject section */ 136 | 137 | /* Begin PBXResourcesBuildPhase section */ 138 | C0644AB71E78394600334798 /* Resources */ = { 139 | isa = PBXResourcesBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXResourcesBuildPhase section */ 146 | 147 | /* Begin PBXSourcesBuildPhase section */ 148 | C0644AB41E78394600334798 /* Sources */ = { 149 | isa = PBXSourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | C0644ACD1E783A7000334798 /* APNsServer.swift in Sources */, 153 | C0644ACF1E783A7000334798 /* PKCS12Adapter.swift in Sources */, 154 | C0644ACB1E783A7000334798 /* APNsPort.swift in Sources */, 155 | C0644ACE1E783A7000334798 /* Connection.swift in Sources */, 156 | C0644ACC1E783A7000334798 /* APNsRequest.swift in Sources */, 157 | C0644ACA1E783A7000334798 /* APNsPayload.swift in Sources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXSourcesBuildPhase section */ 162 | 163 | /* Begin XCBuildConfiguration section */ 164 | C0644ABF1E78394600334798 /* Debug */ = { 165 | isa = XCBuildConfiguration; 166 | buildSettings = { 167 | ALWAYS_SEARCH_USER_PATHS = NO; 168 | CLANG_ANALYZER_NONNULL = YES; 169 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 170 | CLANG_CXX_LIBRARY = "libc++"; 171 | CLANG_ENABLE_MODULES = YES; 172 | CLANG_ENABLE_OBJC_ARC = YES; 173 | CLANG_WARN_BOOL_CONVERSION = YES; 174 | CLANG_WARN_CONSTANT_CONVERSION = YES; 175 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 176 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 177 | CLANG_WARN_EMPTY_BODY = YES; 178 | CLANG_WARN_ENUM_CONVERSION = YES; 179 | CLANG_WARN_INFINITE_RECURSION = YES; 180 | CLANG_WARN_INT_CONVERSION = YES; 181 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 182 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 183 | CLANG_WARN_UNREACHABLE_CODE = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | CODE_SIGN_IDENTITY = "-"; 186 | COPY_PHASE_STRIP = NO; 187 | CURRENT_PROJECT_VERSION = 1; 188 | DEBUG_INFORMATION_FORMAT = dwarf; 189 | ENABLE_STRICT_OBJC_MSGSEND = YES; 190 | ENABLE_TESTABILITY = YES; 191 | GCC_C_LANGUAGE_STANDARD = gnu99; 192 | GCC_DYNAMIC_NO_PIC = NO; 193 | GCC_NO_COMMON_BLOCKS = YES; 194 | GCC_OPTIMIZATION_LEVEL = 0; 195 | GCC_PREPROCESSOR_DEFINITIONS = ( 196 | "DEBUG=1", 197 | "$(inherited)", 198 | ); 199 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 200 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 201 | GCC_WARN_UNDECLARED_SELECTOR = YES; 202 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 203 | GCC_WARN_UNUSED_FUNCTION = YES; 204 | GCC_WARN_UNUSED_VARIABLE = YES; 205 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 206 | MACOSX_DEPLOYMENT_TARGET = 10.12; 207 | MTL_ENABLE_DEBUG_INFO = YES; 208 | ONLY_ACTIVE_ARCH = YES; 209 | SDKROOT = macosx; 210 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator"; 211 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 212 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 213 | VERSIONING_SYSTEM = "apple-generic"; 214 | VERSION_INFO_PREFIX = ""; 215 | }; 216 | name = Debug; 217 | }; 218 | C0644AC01E78394600334798 /* Release */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | ALWAYS_SEARCH_USER_PATHS = NO; 222 | CLANG_ANALYZER_NONNULL = YES; 223 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 224 | CLANG_CXX_LIBRARY = "libc++"; 225 | CLANG_ENABLE_MODULES = YES; 226 | CLANG_ENABLE_OBJC_ARC = YES; 227 | CLANG_WARN_BOOL_CONVERSION = YES; 228 | CLANG_WARN_CONSTANT_CONVERSION = YES; 229 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 230 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 231 | CLANG_WARN_EMPTY_BODY = YES; 232 | CLANG_WARN_ENUM_CONVERSION = YES; 233 | CLANG_WARN_INFINITE_RECURSION = YES; 234 | CLANG_WARN_INT_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 237 | CLANG_WARN_UNREACHABLE_CODE = YES; 238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 239 | CODE_SIGN_IDENTITY = "-"; 240 | COPY_PHASE_STRIP = NO; 241 | CURRENT_PROJECT_VERSION = 1; 242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 243 | ENABLE_NS_ASSERTIONS = NO; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu99; 246 | GCC_NO_COMMON_BLOCKS = YES; 247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 249 | GCC_WARN_UNDECLARED_SELECTOR = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 251 | GCC_WARN_UNUSED_FUNCTION = YES; 252 | GCC_WARN_UNUSED_VARIABLE = YES; 253 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 254 | MACOSX_DEPLOYMENT_TARGET = 10.12; 255 | MTL_ENABLE_DEBUG_INFO = NO; 256 | SDKROOT = macosx; 257 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator"; 258 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 259 | VERSIONING_SYSTEM = "apple-generic"; 260 | VERSION_INFO_PREFIX = ""; 261 | }; 262 | name = Release; 263 | }; 264 | C0644AC21E78394600334798 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | CODE_SIGN_IDENTITY = ""; 268 | COMBINE_HIDPI_IMAGES = YES; 269 | DEFINES_MODULE = YES; 270 | DYLIB_COMPATIBILITY_VERSION = 1; 271 | DYLIB_CURRENT_VERSION = 1; 272 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 273 | FRAMEWORK_VERSION = A; 274 | INFOPLIST_FILE = APNsKit/Info.plist; 275 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 276 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 277 | PRODUCT_BUNDLE_IDENTIFIER = masaki.haga.APNsKit; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | SDKROOT = iphoneos10.2; 280 | SKIP_INSTALL = YES; 281 | SWIFT_VERSION = 3.0; 282 | }; 283 | name = Debug; 284 | }; 285 | C0644AC31E78394600334798 /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | CODE_SIGN_IDENTITY = ""; 289 | COMBINE_HIDPI_IMAGES = YES; 290 | DEFINES_MODULE = YES; 291 | DYLIB_COMPATIBILITY_VERSION = 1; 292 | DYLIB_CURRENT_VERSION = 1; 293 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 294 | FRAMEWORK_VERSION = A; 295 | INFOPLIST_FILE = APNsKit/Info.plist; 296 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 298 | PRODUCT_BUNDLE_IDENTIFIER = masaki.haga.APNsKit; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | SDKROOT = iphoneos10.2; 301 | SKIP_INSTALL = YES; 302 | SWIFT_VERSION = 3.0; 303 | }; 304 | name = Release; 305 | }; 306 | /* End XCBuildConfiguration section */ 307 | 308 | /* Begin XCConfigurationList section */ 309 | C0644AB31E78394600334798 /* Build configuration list for PBXProject "APNsKit" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | C0644ABF1E78394600334798 /* Debug */, 313 | C0644AC01E78394600334798 /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | C0644AC11E78394600334798 /* Build configuration list for PBXNativeTarget "APNsKit" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | C0644AC21E78394600334798 /* Debug */, 322 | C0644AC31E78394600334798 /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | /* End XCConfigurationList section */ 328 | }; 329 | rootObject = C0644AB01E78394600334798 /* Project object */; 330 | } 331 | -------------------------------------------------------------------------------- /APNsKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /APNsKit.xcodeproj/project.xcworkspace/xcshareddata/APNsKit.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" : 9223372036854775807, 8 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : 9223372036854775807 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "05E42A2B-40F8-4295-A7A4-C15C0F9F496D", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" : "APNsKit\/", 13 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : "" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "APNsKit", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "APNsKit.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/APNsKit.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3DA82495CA2199B9C34A4BAB2FB2ACDDD02CA769" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/Parrot.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CA86F53ED6CB060B09097483BD033980A4C323F8" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /APNsKit.xcodeproj/xcshareddata/xcschemes/APNsKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /APNsKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /APNsKit.xcworkspace/xcshareddata/APNsKit.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "D65E0F9E3FEB7C912C4B67B9F8ACCEDB6B38A9EA", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "D65E0F9E3FEB7C912C4B67B9F8ACCEDB6B38A9EA" : 9223372036854775807, 8 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : 9223372036854775807 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "05E42A2B-40F8-4295-A7A4-C15C0F9F496D", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "D65E0F9E3FEB7C912C4B67B9F8ACCEDB6B38A9EA" : "APNsKit\/", 13 | "CA86F53ED6CB060B09097483BD033980A4C323F8" : "" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "APNsKit", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "APNsKit.xcworkspace", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/Parrot.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CA86F53ED6CB060B09097483BD033980A4C323F8" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:hagmas\/APNsKit.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D65E0F9E3FEB7C912C4B67B9F8ACCEDB6B38A9EA" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /APNsKit/APNsKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // APNsKit.h 3 | // APNsKit 4 | // 5 | // Created by Haga Masaki on 14/03/2017. 6 | // Copyright © 2017 Haga Masaki. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for APNsKit. 12 | FOUNDATION_EXPORT double APNsKitVersionNumber; 13 | 14 | //! Project version string for APNsKit. 15 | FOUNDATION_EXPORT const unsigned char APNsKitVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /APNsKit/APNsPayload.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct APNsPayload { 4 | let title: String? 5 | let body: String 6 | let titleLocKey: String? 7 | let titleLocArgs: [String]? 8 | let actionLocKey: String? 9 | let locKey: String? 10 | let locArgs: [String]? 11 | let launchImage: String? 12 | 13 | let badge: Int? 14 | let sound: String? 15 | let contentAvailable: Int? 16 | let mutableContent: Int? 17 | let category: String? 18 | let threadId: String? 19 | 20 | let custom: [String: Any]? 21 | 22 | public init( 23 | title: String? = nil, 24 | body: String, 25 | titleLocKey: String? = nil, 26 | titleLocArgs: [String]? = nil, 27 | actionLocKey: String? = nil, 28 | locKey: String? = nil, 29 | locArgs: [String]? = nil, 30 | launchImage: String? = nil, 31 | 32 | badge: Int? = nil, 33 | sound: String? = nil, 34 | contentAvailable: Int? = nil, 35 | mutableContent: Int? = nil, 36 | category: String? = nil, 37 | threadId: String? = nil, 38 | 39 | custom: [String: Any]? = nil) { 40 | self.title = title 41 | self.body = body 42 | self.titleLocKey = titleLocKey 43 | self.titleLocArgs = titleLocArgs 44 | self.actionLocKey = actionLocKey 45 | self.locKey = locKey 46 | self.locArgs = locArgs 47 | self.launchImage = launchImage 48 | 49 | self.badge = badge 50 | self.sound = sound 51 | self.contentAvailable = contentAvailable 52 | self.mutableContent = mutableContent 53 | self.category = category 54 | self.threadId = threadId 55 | 56 | self.custom = custom 57 | } 58 | 59 | public var dictionary: [String: Any] { 60 | // Alert 61 | var alert: [String: Any] = ["body": body] 62 | 63 | if let title = title { 64 | alert["title"] = title 65 | } 66 | 67 | if let titleLocKey = titleLocKey { 68 | alert["title-loc-key"] = titleLocKey 69 | } 70 | 71 | if let titleLocArgs = titleLocArgs { 72 | alert["title-loc-args"] = titleLocArgs 73 | } 74 | 75 | if let actionLocKey = actionLocKey { 76 | alert["action-loc-key"] = actionLocKey 77 | } 78 | 79 | if let locKey = locKey { 80 | alert["loc-key"] = locKey 81 | } 82 | 83 | if let locArgs = locArgs { 84 | alert["loc-args"] = locArgs 85 | } 86 | 87 | if let launchImage = launchImage { 88 | alert["launch-image"] = launchImage 89 | } 90 | 91 | // APS 92 | var dictionary: [String: Any] = ["alert": alert] 93 | 94 | if let badge = badge { 95 | dictionary["badge"] = badge 96 | } 97 | 98 | if let sound = sound { 99 | dictionary["sound"] = sound 100 | } 101 | 102 | if let contentAvailable = contentAvailable { 103 | dictionary["content-available"] = contentAvailable 104 | } 105 | 106 | if let mutableContent = mutableContent { 107 | dictionary["mutable-content"] = mutableContent 108 | } 109 | 110 | if let category = category { 111 | dictionary["category"] = category 112 | } 113 | 114 | if let threadId = threadId { 115 | dictionary["thread-id"] = threadId 116 | } 117 | 118 | var payload: [String: Any] = ["aps": dictionary] 119 | 120 | // Custom 121 | custom?.forEach { 122 | payload[$0] = $1 123 | } 124 | 125 | return payload 126 | } 127 | 128 | public static func convert(parameters: Any) -> String { 129 | if let dictionary = parameters as? [String: Any] { 130 | return "{" + dictionary.map { "\"\($0.key)\":" + convert(parameters: $0.value) }.joined(separator: ",") + "}" 131 | } else if let array = parameters as? [String] { 132 | return "[" + array.joined(separator: ",") + "]" 133 | } else if let int = parameters as? Int { 134 | return "\(int)" 135 | } else { 136 | return "\"\(parameters)\"" 137 | } 138 | } 139 | 140 | public var data: Data? { 141 | return APNsPayload.convert(parameters: dictionary).data(using: .unicode) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /APNsKit/APNsPort.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum APNsPort: Int { 4 | case p443 = 443 5 | case p2197 = 2197 6 | } 7 | -------------------------------------------------------------------------------- /APNsKit/APNsRequest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct APNsRequest { 4 | public struct Header { 5 | public enum Priority: String { 6 | case p5 = "5", p10 = "10" 7 | } 8 | 9 | public let id: String 10 | public let expiration: Date 11 | public let priority: Priority 12 | public let topic: String? 13 | public let collapseId: String? 14 | 15 | public init(id: String = UUID().uuidString, 16 | expiration: Date = Date(timeIntervalSince1970: 0), 17 | priority: Priority = .p10, 18 | topic: String? = nil, 19 | collapseId: String? = nil) { 20 | self.id = id 21 | self.expiration = expiration 22 | self.priority = .p10 23 | self.topic = topic 24 | self.collapseId = collapseId 25 | } 26 | } 27 | 28 | public var port: APNsPort 29 | public var server: APNsServer 30 | public var deviceToken: String 31 | public var header: Header 32 | public var payload: APNsPayload 33 | 34 | public static let method = "POST" 35 | 36 | public init(port: APNsPort, server: APNsServer, deviceToken: String, header: Header = Header(), payload: APNsPayload) { 37 | self.port = port 38 | self.server = server 39 | self.deviceToken = deviceToken 40 | self.header = header 41 | self.payload = payload 42 | } 43 | 44 | public var url: URL? { 45 | let urlString = "https://" + server.rawValue + ":\(port.rawValue)" + "/3/device/" + deviceToken 46 | return URL(string: urlString) 47 | } 48 | 49 | public var urlRequest: URLRequest? { 50 | guard let url = url else { 51 | return nil 52 | } 53 | 54 | var request = URLRequest(url: url) 55 | request.setValue(for: header) 56 | request.httpMethod = APNsRequest.method 57 | request.httpBody = payload.data 58 | return request 59 | } 60 | } 61 | 62 | private extension URLRequest { 63 | mutating func setValue(for header: APNsRequest.Header) { 64 | self.addValue(header.id, forHTTPHeaderField: "apns-id") 65 | self.addValue("\(Int(header.expiration.timeIntervalSince1970))", forHTTPHeaderField: "apns-expiration") 66 | self.addValue(header.priority.rawValue, forHTTPHeaderField: "apns-priority") 67 | 68 | if let topic = header.topic { 69 | self.addValue(topic, forHTTPHeaderField: "apns-topic") 70 | } 71 | 72 | if let collapseId = header.collapseId { 73 | self.addValue(collapseId, forHTTPHeaderField: "apns-collapse-id") 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /APNsKit/APNsServer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum APNsServer: String { 4 | case development = "api.development.push.apple.com" 5 | case production = "api.push.apple.com" 6 | } 7 | -------------------------------------------------------------------------------- /APNsKit/Connection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class Connection: NSObject, URLSessionDelegate { 4 | public enum Result { 5 | case success 6 | case failure(errorCode: Int, message: String) 7 | } 8 | 9 | let adapter: PKCS12Adapter 10 | 11 | public init(p12FileName: String, passPhrase: String) throws { 12 | self.adapter = try PKCS12Adapter(fileName: p12FileName, passPhrase: passPhrase) 13 | super.init() 14 | } 15 | 16 | public func send(request: APNsRequest, resultHandler: @escaping (Result) -> Void) { 17 | let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) 18 | if let urlRequest = request.urlRequest { 19 | let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in 20 | // The APNs server response for the requst is returned with the data object. 21 | // If the error object is not nil, there is more likely a problem with the connection or the network. 22 | if let error = error { 23 | resultHandler(.failure(errorCode: 0, message: "Unkonwn Error Occured: \(error)")) 24 | return 25 | } 26 | 27 | guard let data = data else { 28 | resultHandler(.success) 29 | return 30 | } 31 | 32 | if data.isEmpty { 33 | resultHandler(.success) 34 | return 35 | } 36 | 37 | if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String], 38 | let reason = json?["reason"], 39 | let httpResponse = response as? HTTPURLResponse { 40 | resultHandler(.failure(errorCode: httpResponse.statusCode, message: reason)) 41 | return 42 | } else { 43 | resultHandler(.failure(errorCode: 0, message: "Unkonwn Error Occured")) 44 | return 45 | } 46 | }) 47 | task.resume() 48 | } 49 | } 50 | 51 | public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { 52 | // Nothing to do 53 | } 54 | 55 | #if os(iOS) || os(watchOS) || os(tvOS) 56 | public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { 57 | // Nothing to do 58 | } 59 | #endif 60 | 61 | public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 62 | switch challenge.protectionSpace.authenticationMethod { 63 | case NSURLAuthenticationMethodClientCertificate: 64 | let credential = URLCredential(identity: adapter.secIdentity, certificates: [adapter.secCertificate], persistence: .forSession) 65 | completionHandler(.useCredential, credential) 66 | 67 | case NSURLAuthenticationMethodServerTrust: 68 | if let serverTrust = challenge.protectionSpace.serverTrust { 69 | let credential = URLCredential(trust: serverTrust) 70 | completionHandler(.useCredential, credential) 71 | } 72 | 73 | default: 74 | break 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /APNsKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2017 Haga Masaki. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /APNsKit/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /APNsKit/PKCS12Adapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Security 3 | 4 | enum PKCS12AdapterError: Error { 5 | case unknown 6 | case fileNotFound 7 | case fileOpenFailed 8 | case secAuthFailed 9 | } 10 | 11 | public struct PKCS12Adapter { 12 | 13 | public let secCertificate: SecCertificate 14 | public let secIdentity: SecIdentity 15 | 16 | public init(fileName: String, passPhrase: String) throws { 17 | guard let path = Bundle.main.path(forResource: fileName, ofType: "p12") else { 18 | throw PKCS12AdapterError.fileNotFound 19 | } 20 | 21 | guard let pkcs12data = NSData(contentsOfFile: path) else { 22 | throw PKCS12AdapterError.fileOpenFailed 23 | } 24 | 25 | let options = [String(kSecImportExportPassphrase): passPhrase] 26 | var imported: CFArray? = nil 27 | 28 | switch SecPKCS12Import(pkcs12data, options as CFDictionary, &imported) { 29 | case noErr: 30 | let identityDictionaries = imported as! [[String:Any]] 31 | let identityRef = identityDictionaries[0][kSecImportItemIdentity as String] as! SecIdentity 32 | 33 | var _certRef: SecCertificate? 34 | SecIdentityCopyCertificate(identityRef, &_certRef) 35 | 36 | guard let certRef = _certRef else { 37 | throw PKCS12AdapterError.unknown 38 | } 39 | 40 | self.secCertificate = certRef 41 | self.secIdentity = identityRef 42 | 43 | case errSecAuthFailed: 44 | throw PKCS12AdapterError.secAuthFailed 45 | 46 | default: 47 | throw PKCS12AdapterError.unknown 48 | } 49 | } 50 | 51 | public var sslCertificates: [Any] { 52 | return [secIdentity, secCertificate] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /APNsKit/PushViewController.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | # How to use this playground 4 | 5 | 1. Build the framework with the target "Generic iOS Device". 6 | 2. Create the provider client certificate. (Follow the steps described [here](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html#//apple_ref/doc/uid/TP40012582-CH26-SW11)) 7 | 3. Export your identity from the keychain app in the Personal Information Exchange Format (.p12) and place it under the Resources folder of this playground. 8 | 4. Fill port number, developement/production server, the device token of the target device, the name of the p12 file and its pass phrase. 9 | 5. You will see a view on the playground timeline and push the button to send notifications. 10 | */ 11 | 12 | import UIKit 13 | import APNsKit 14 | import PlaygroundSupport 15 | 16 | PlaygroundPage.current.needsIndefiniteExecution = true 17 | URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil) 18 | 19 | let payload = APNsPayload(body: "Hello, World!") 20 | let request = APNsRequest(port: <#port#>, 21 | server: <#server#>, 22 | deviceToken: <#deviceToken#>, 23 | payload: payload) 24 | 25 | let pushVC: PushViewController 26 | if let _pushVC = PushViewController(p12FileName: <#p12FileName#>, 27 | passPhrase: <#passPhrase#>, 28 | request: request) { 29 | pushVC = _pushVC 30 | pushVC.view.frame = CGRect(x: 0, y: 0, width: 414, height: 736) 31 | PlaygroundPage.current.liveView = pushVC 32 | } 33 | -------------------------------------------------------------------------------- /APNsKit/PushViewController.playground/Sources/PushViewController.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | import APNsKit 4 | 5 | public class PushViewController: UIViewController { 6 | 7 | let pushButton = UIButton(type: .system) 8 | 9 | let connection: Connection 10 | let request: APNsRequest 11 | 12 | public init?(p12FileName: String, passPhrase: String, request: APNsRequest) { 13 | do { 14 | let connection = try Connection(p12FileName: p12FileName, passPhrase: passPhrase) 15 | self.connection = connection 16 | self.request = request 17 | super.init(nibName: nil, bundle: nil) 18 | } catch { 19 | print("Failed to create connection: \(error)") 20 | return nil 21 | } 22 | } 23 | 24 | required public init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | override public func loadView() { 29 | super.loadView() 30 | 31 | view.backgroundColor = UIColor.white 32 | 33 | view.autoresizingMask = [.flexibleHeight, .flexibleWidth] 34 | pushButton.setTitle( "⚡️Push⚡️", for: .normal) 35 | pushButton.titleLabel?.font = UIFont.systemFont(ofSize: 30.0) 36 | pushButton.sizeToFit() 37 | pushButton.translatesAutoresizingMaskIntoConstraints = false 38 | pushButton.addTarget(self, action: #selector(pushed), for: .touchUpInside) 39 | view.addSubview(pushButton) 40 | 41 | pushButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 42 | pushButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 43 | } 44 | 45 | func pushed() { 46 | connection.send(request: request) { 47 | switch $0 { 48 | case .success: 49 | print("Succeeded!") 50 | case .failure(let errorCode, let message): 51 | print("Failed to push! \(errorCode), \(message)") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /APNsKit/PushViewController.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Masaki Haga 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APNsKit 2 | A framework to send Apple notifications. 3 | 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | 6 | ## About APNsKit 7 | APNsKit is a swift framework to send Apple notifications to iOS devices. Especially, with Playground it lets you to create interactive tool to send push notifications for debugging purposes. Check out the Playground `PushViewController.playground` bundled with the Workspace.

8 | 9 | 10 | ## Usage 11 | ```swift 12 | import APNsKit 13 | 14 | // Setting header fields is optional. Refer (#1) for configurable header fields of APNs request. 15 | let header = APNsRequest.Header(priority: .p10, topic: "<#The topic of the remote notification#>") 16 | 17 | // Create a APNs payload. See Apple's Payload Key Reference (#2) for its specifications. 18 | let payload = APNsPayload(title: "Hello World!", body: "This is APNsKit.", contentAvailable: 1) 19 | let request = APNsRequest(port: .p2197, 20 | server: .development, 21 | deviceToken: "<#The target device token#>", 22 | header: header, 23 | payload: payload) 24 | 25 | // Create a connection that wraps up URLSession and its authentication challenges. 26 | if let connection = try? Connection(p12FileName: "<#Your p12 file name#>", passPhrase: "<#The pass phrase for the file#>") { 27 | 28 | // Send the request to the APNs server. The connection has to be retained until the server responses. 29 | connection.send(request: request, resultHandler: { result in 30 | switch result { 31 | case .success: 32 | print("Succeeded!") 33 | case .failure(let code, let message): 34 | print("Failed to send: \(code), \(message)") 35 | } 36 | }) 37 | } 38 | ``` 39 | #1 [HTTP/2 Request to APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW11) 40 | 41 | #2 [Apple's Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1) 42 | 43 | ## Requirements 44 | * Xcode 8.2.1 45 | * Swift 3.0 46 | 47 | ## Installation 48 | * APNsKit doesn't include any external depenancies. 49 | * APNsKit currently supports only Carthage. 50 | ### [Carthage](https://github.com/Carthage/Carthage) 51 | 52 | **Tested with `carthage version`: `0.20.1`** 53 | 54 | Add this to `Cartfile` 55 | 56 | ``` 57 | github "hagmas/APNsKit" 58 | ``` 59 | 60 | ```bash 61 | $ carthage update 62 | ``` 63 | --------------------------------------------------------------------------------