diff --git a/Podfile b/Podfile index 72a0d185..d883629f 100644 --- a/Podfile +++ b/Podfile @@ -2,6 +2,8 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! +pod 'AFNetworking' +pod 'Ono', '~> 1.2' pod 'MZFayeClient', :path => '../CatchLib-iOS/MZFayeClient/' pod 'JPushSDK' pod 'Realm' diff --git a/Podfile.lock b/Podfile.lock index 3118e9fe..ab389a55 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,17 +1,41 @@ PODS: + - AFNetworking (2.5.2): + - AFNetworking/NSURLConnection (= 2.5.2) + - AFNetworking/NSURLSession (= 2.5.2) + - AFNetworking/Reachability (= 2.5.2) + - AFNetworking/Security (= 2.5.2) + - AFNetworking/Serialization (= 2.5.2) + - AFNetworking/UIKit (= 2.5.2) + - AFNetworking/NSURLConnection (2.5.2): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/NSURLSession (2.5.2): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (2.5.2) + - AFNetworking/Security (2.5.2) + - AFNetworking/Serialization (2.5.2) + - AFNetworking/UIKit (2.5.2): + - AFNetworking/NSURLConnection + - AFNetworking/NSURLSession - Base64 (1.0.1) - JPushSDK (1.8.2) - MZFayeClient (1.0.0): - Base64 (~> 1.0.1) - SocketRocket (~> 0.3.1-beta2) + - Ono (1.2.0) - Realm (0.91.1): - Realm/Headers (= 0.91.1) - Realm/Headers (0.91.1) - SocketRocket (0.3.1-beta2) DEPENDENCIES: + - AFNetworking - JPushSDK - MZFayeClient (from `../CatchLib-iOS/MZFayeClient/`) + - Ono (~> 1.2) - Realm EXTERNAL SOURCES: @@ -19,9 +43,11 @@ EXTERNAL SOURCES: :path: "../CatchLib-iOS/MZFayeClient/" SPEC CHECKSUMS: + AFNetworking: fefbce9660acb17f48ae0011292d4da0f457bf36 Base64: 4924bf3ca6fa559a5161ef717291bd450eb7bd1a JPushSDK: c68dd04c595a5c93aa003f212974010790410d8e MZFayeClient: 19df756a4c86c8f414668e6edc54fccfee54adbb + Ono: f8c2b610f28f40d411b0d75130e77fd78b92e4e1 Realm: b9c1fd9defa26aa7276ce938a8df52ec81f24528 SocketRocket: 7284ab9370a06c99aba92b2fe3a32aedd0f9a6fa diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 27fa8d1a..ea011b04 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0A02E46F1AC9208A00235DBF /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A02E46E1AC9208A00235DBF /* libxml2.dylib */; }; 0A5D83941AC5D3C8000045BF /* YepTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D83931AC5D3C8000045BF /* YepTabBarController.swift */; }; 0A5D83991AC5D91E000045BF /* ContactsSearchTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5D83981AC5D91E000045BF /* ContactsSearchTableViewController.swift */; }; 0A8F54B11AB67D11004AD60E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8F54B01AB67D11004AD60E /* AppDelegate.swift */; }; @@ -14,6 +15,9 @@ 0A8F54B81AB67D11004AD60E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0A8F54B71AB67D11004AD60E /* Images.xcassets */; }; 0A8F54BB1AB67D11004AD60E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A8F54B91AB67D11004AD60E /* LaunchScreen.xib */; }; 0A8F54C71AB67D11004AD60E /* YepTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8F54C61AB67D11004AD60E /* YepTests.swift */; }; + 0A944C251AC8BB2F00037A06 /* YepStorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A944C241AC8BB2F00037A06 /* YepStorageService.swift */; }; + 0AAFCFF91AC8CA5B00EB0EF8 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AAFCFF81AC8CA5B00EB0EF8 /* MobileCoreServices.framework */; }; + 0AAFCFFB1AC8CDFE00EB0EF8 /* 1.png in Resources */ = {isa = PBXBuildFile; fileRef = 0AAFCFFA1AC8CDFE00EB0EF8 /* 1.png */; }; 0AD7EA5E1AC3ED1200617758 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA5D1AC3ED1200617758 /* CFNetwork.framework */; }; 0AD7EA601AC3ED1900617758 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA5F1AC3ED1900617758 /* CoreFoundation.framework */; }; 0AD7EA621AC3ED2100617758 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA611AC3ED2100617758 /* CoreTelephony.framework */; }; @@ -88,6 +92,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0A02E46C1AC9205000235DBF /* KissXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KissXML.framework; path = "Pods/../build/Debug-iphoneos/Pods/KissXML.framework"; sourceTree = ""; }; + 0A02E46E1AC9208A00235DBF /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; 0A5D83931AC5D3C8000045BF /* YepTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepTabBarController.swift; path = ViewControllers/TabBar/YepTabBarController.swift; sourceTree = ""; }; 0A5D83981AC5D91E000045BF /* ContactsSearchTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContactsSearchTableViewController.swift; path = ViewControllers/Contacts/Search/ContactsSearchTableViewController.swift; sourceTree = ""; }; 0A8F54AB1AB67D11004AD60E /* Yep.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Yep.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -99,6 +105,10 @@ 0A8F54C01AB67D11004AD60E /* YepTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YepTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0A8F54C51AB67D11004AD60E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0A8F54C61AB67D11004AD60E /* YepTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YepTests.swift; sourceTree = ""; }; + 0A944C241AC8BB2F00037A06 /* YepStorageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepStorageService.swift; path = Services/YepStorageService.swift; sourceTree = ""; }; + 0AAFCFF61AC8C75900EB0EF8 /* AWSS3.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AWSS3.framework; path = "Pods/../build/Debug-iphoneos/Pods/AWSS3.framework"; sourceTree = ""; }; + 0AAFCFF81AC8CA5B00EB0EF8 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + 0AAFCFFA1AC8CDFE00EB0EF8 /* 1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 1.png; sourceTree = ""; }; 0AD7EA511AC3EA6F00617758 /* JPushSDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JPushSDK.framework; path = "Pods/../build/Debug-iphoneos/Pods/JPushSDK.framework"; sourceTree = ""; }; 0AD7EA581AC3EAF300617758 /* Yep-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Yep-Bridging-Header.h"; sourceTree = ""; }; 0AD7EA5B1AC3ECD700617758 /* libPushSDK-1.8.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPushSDK-1.8.3.a"; path = "Yep/lib/libPushSDK-1.8.3.a"; sourceTree = ""; }; @@ -173,6 +183,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0A02E46F1AC9208A00235DBF /* libxml2.dylib in Frameworks */, + 0AAFCFF91AC8CA5B00EB0EF8 /* MobileCoreServices.framework in Frameworks */, 0AD7EA741AC3EE9600617758 /* libPushSDK-1.8.3.a in Frameworks */, 0AD7EA6E1AC3ED4A00617758 /* libz.dylib in Frameworks */, 0AD7EA6C1AC3ED4300617758 /* Security.framework in Frameworks */, @@ -237,6 +249,7 @@ 0A8F54AD1AB67D11004AD60E /* Yep */ = { isa = PBXGroup; children = ( + 0AAFCFFA1AC8CDFE00EB0EF8 /* 1.png */, 0AFE75AE1AC43DE0005AA33E /* Crashlytics.framework */, 0A8F54B01AB67D11004AD60E /* AppDelegate.swift */, 0AD7EA581AC3EAF300617758 /* Yep-Bridging-Header.h */, @@ -431,6 +444,7 @@ 502AE5281AB81076005BD199 /* YepService.swift */, 50E114F81ABC137A00F13000 /* YepServiceSync.swift */, 5076C5691AC46B9F00B22952 /* FayeService.swift */, + 0A944C241AC8BB2F00037A06 /* YepStorageService.swift */, ); name = Services; sourceTree = ""; @@ -594,6 +608,10 @@ 67B708B60AAD1DEA82A6A682 /* Frameworks */ = { isa = PBXGroup; children = ( + 0A02E46E1AC9208A00235DBF /* libxml2.dylib */, + 0A02E46C1AC9205000235DBF /* KissXML.framework */, + 0AAFCFF81AC8CA5B00EB0EF8 /* MobileCoreServices.framework */, + 0AAFCFF61AC8C75900EB0EF8 /* AWSS3.framework */, 0AD7EA6D1AC3ED4A00617758 /* libz.dylib */, 0AD7EA6B1AC3ED4300617758 /* Security.framework */, 0AD7EA691AC3ED3D00617758 /* UIKit.framework */, @@ -707,6 +725,7 @@ 0A8F54B61AB67D11004AD60E /* Main.storyboard in Resources */, 5033159D1AC101C80008A209 /* ChatRightTextCell.xib in Resources */, 50E114FD1ABC549300F13000 /* ContactsCell.xib in Resources */, + 0AAFCFFB1AC8CDFE00EB0EF8 /* 1.png in Resources */, 502AE54F1AB93175005BD199 /* ProfileHeaderCell.xib in Resources */, 0A8F54BB1AB67D11004AD60E /* LaunchScreen.xib in Resources */, 0A8F54B81AB67D11004AD60E /* Images.xcassets in Resources */, @@ -832,6 +851,7 @@ 503315A01AC126100008A209 /* MessageToolbar.swift in Sources */, 502AE5531AB93215005BD199 /* ProfileFooterCell.swift in Sources */, 0A5D83991AC5D91E000045BF /* ContactsSearchTableViewController.swift in Sources */, + 0A944C251AC8BB2F00037A06 /* YepStorageService.swift in Sources */, 5023DE521AB6C51300B3EE96 /* CGRect+Yep.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -972,6 +992,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Yep", + "$(PROJECT_DIR)/build/Debug-iphoneos/Pods", ); GCC_OPTIMIZATION_LEVEL = 3; INFOPLIST_FILE = Yep/Info.plist; @@ -996,6 +1017,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Yep", + "$(PROJECT_DIR)/build/Debug-iphoneos/Pods", ); INFOPLIST_FILE = Yep/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; diff --git a/Yep/1.png b/Yep/1.png new file mode 100644 index 00000000..d0e35313 Binary files /dev/null and b/Yep/1.png differ diff --git a/Yep/Base.lproj/Intro.storyboard b/Yep/Base.lproj/Intro.storyboard index f62a5b94..22e69408 100644 --- a/Yep/Base.lproj/Intro.storyboard +++ b/Yep/Base.lproj/Intro.storyboard @@ -194,7 +194,7 @@ - + @@ -209,7 +209,7 @@ - + @@ -537,7 +537,7 @@ - + @@ -552,7 +552,7 @@ - + diff --git a/Yep/Services/YepNetworking.swift b/Yep/Services/YepNetworking.swift index d1515081..13ba8003 100644 --- a/Yep/Services/YepNetworking.swift +++ b/Yep/Services/YepNetworking.swift @@ -233,4 +233,4 @@ public func jsonResource(#token: String?, #path: String, #method: Method, #re } return Resource(path: path, method: method, requestBody: jsonBody, headers: headers, parse: jsonParse) -} +} \ No newline at end of file diff --git a/Yep/Services/YepStorageService.swift b/Yep/Services/YepStorageService.swift new file mode 100644 index 00000000..b6652be6 --- /dev/null +++ b/Yep/Services/YepStorageService.swift @@ -0,0 +1,159 @@ +// +// YepStorageService.swift +// Yep +// +// Created by kevinzhow on 15/3/30. +// Copyright (c) 2015年 Catch Inc. All rights reserved. +// + +import Foundation +import MobileCoreServices.UTType +import Ono +import AFNetworking + +struct S3UploadParams { + let url: String + let key: String + let acl: String + let algorithm: String + let signature: String + let date: String + let credential: String + let encodedPolicy: String +} + + +func uploadFileToS3WithFilePath(filePath: String, #s3UploadParams: S3UploadParams){ + + let param = [ + "key" : s3UploadParams.key, + "acl" : s3UploadParams.acl, + "X-Amz-Algorithm": s3UploadParams.algorithm, + "X-Amz-Signature": s3UploadParams.signature, + "X-Amz-Date": s3UploadParams.date, + "X-Amz-Credential": s3UploadParams.credential, + "Policy": s3UploadParams.encodedPolicy + ] + + let filename = filePath.lastPathComponent + let mimetype = mimeTypeForPath(filePath) + + let request = AFHTTPRequestSerializer().multipartFormRequestWithMethod("POST", URLString: s3UploadParams.url, parameters: param, constructingBodyWithBlock: { formData in + formData.appendPartWithFileURL(NSURL(fileURLWithPath: filePath)!, name: "file", fileName: filename, mimeType: mimetype, error: nil) + }, error: nil) + + let manager = AFURLSessionManager(sessionConfiguration: NSURLSessionConfiguration.defaultSessionConfiguration()) + manager.responseSerializer = AFXMLParserResponseSerializer() + var progress: NSProgress? + + var uploadTask = manager.uploadTaskWithStreamedRequest(request, progress: &progress, completionHandler: { (response, responseObject, error) in + + if (error != nil) { + println("Error \(error.description)") + }else { + println("Upload \(response) \(responseObject)") + } + }) + + uploadTask.resume() + +} + +// MARK: Upload + +extension NSMutableData { + + /// Append string to NSMutableData + /// + /// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8. + /// + /// :param: string The string to be added to the `NSMutableData`. + + func appendString(string: String) { + let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) + appendData(data!) + } +} + + + +/// Create boundary string for multipart/form-data request +/// +/// :returns: The boundary string that consists of "Boundary-" followed by a UUID string. + +func generateBoundaryString() -> String { + return "Boundary-\(NSUUID().UUIDString)" +} + +/// Determine mime type on the basis of extension of a file. +/// +/// This requires MobileCoreServices framework. +/// +/// :param: path The path of the file for which we are going to determine the mime type. +/// +/// :returns: Returns the mime type if successful. Returns application/octet-stream if unable to determine mime type. + +func mimeTypeForPath(path: String) -> String { + let pathExtension = path.pathExtension + + if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() { + if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() { + return mimetype as String + } + } + return "application/octet-stream"; +} + +func s3UploadParams(#failureHandler: ((Reason, String?) -> ())?, #completion: S3UploadParams -> Void) { + + let parse: JSONDictionary -> S3UploadParams? = { data in + println("s3FormData: \(data)") + + if let options = data["options"] as? JSONDictionary { + if + let encodedPolice = options["encoded_policy"] as? String, + let key = options["key"] as? String, + let signature = options["signature"] as? String, + let urlString = options["url"] as? String, + let policy = options["policy"] as? JSONDictionary, + let conditions = policy["conditions"] as? [JSONDictionary] { + + var acl: String? + var credential: String? + var algorithm: String? + var date: String? + + for dict in conditions { + for (key, value) in dict { + switch key { + case "acl": + acl = value as? String + case "x-amz-credential": + credential = value as? String + case "x-amz-algorithm": + algorithm = value as? String + case "x-amz-date": + date = value as? String + default: + break + } + } + } + + if let acl = acl, let credential = credential, let algorithm = algorithm, let date = date { + return S3UploadParams(url: urlString, key: key, acl: acl, algorithm: algorithm, signature: signature, date: date, credential: credential, encodedPolicy: encodedPolice) + } + } + } + + return nil + } + + let resource = authJsonResource(path: "/api/v1/attachments/s3_upload_public_form_fields", method: .GET, requestParameters:[:], parse: parse) + + if let failureHandler = failureHandler { + apiRequest({_ in}, baseURL, resource, failureHandler, completion) + } else { + apiRequest({_ in}, baseURL, resource, defaultFailureHandler, completion) + } +} diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index 40813584..0e678ec0 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -137,6 +137,17 @@ class ConversationViewController: UIViewController { }) } } + + messageToolbar.voiceSendAction = { messageToolbar in + s3UploadParams(failureHandler: nil) { s3UploadParams in + println("s3UploadParams: \(s3UploadParams)") + + let filePath = NSBundle.mainBundle().pathForResource("1", ofType: "png")! + //let form = token["options"] as! JSONDictionary + //uploadFileToAWSS3(filePath: filePath, dataForm: form) + uploadFileToS3WithFilePath(filePath, s3UploadParams: s3UploadParams) + } + } } override func viewDidLayoutSubviews() { diff --git a/Yep/Views/MessageToolbar/MessageToolbar.swift b/Yep/Views/MessageToolbar/MessageToolbar.swift index 2678c148..6abe3328 100644 --- a/Yep/Views/MessageToolbar/MessageToolbar.swift +++ b/Yep/Views/MessageToolbar/MessageToolbar.swift @@ -31,6 +31,8 @@ class MessageToolbar: UIToolbar { } var textSendAction: ((messageToolBar: MessageToolbar) -> ())? + + var voiceSendAction: ((messageToolBar: MessageToolbar) -> ())? lazy var cameraButton: UIButton = { let button = UIButton() @@ -51,6 +53,7 @@ class MessageToolbar: UIToolbar { let button = UIButton() button.setImage(UIImage(named: "item_mic"), forState: .Normal) button.tintColor = UIColor.yepTintColor() + button.addTarget(self, action: "trySendVoiceMessage", forControlEvents: UIControlEvents.TouchUpInside) return button }() @@ -127,6 +130,12 @@ class MessageToolbar: UIToolbar { textSendAction(messageToolBar: self) } } + + func trySendVoiceMessage() { + if let textSendAction = voiceSendAction { + textSendAction(messageToolBar: self) + } + } } // MARK: UITextFieldDelegate