From 4851a21c41488a6f0c01e60f1bd1472846f816bf Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 16 Sep 2020 22:26:04 +0300 Subject: Initial API service in Swift --- Juick.xcodeproj/project.pbxproj | 24 +++++ Juick/Service.swift | 135 +++++++++++++++++++++++++ Juick/Supporting Files/Juick-Bridging-Header.h | 3 + 3 files changed, 162 insertions(+) create mode 100644 Juick/Service.swift create mode 100644 Juick/Supporting Files/Juick-Bridging-Header.h diff --git a/Juick.xcodeproj/project.pbxproj b/Juick.xcodeproj/project.pbxproj index dcb9060..8ab2162 100644 --- a/Juick.xcodeproj/project.pbxproj +++ b/Juick.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 774528C21F930C06004D110B /* Attachment.m in Sources */ = {isa = PBXBuildFile; fileRef = 774528C11F930C06004D110B /* Attachment.m */; }; 774746AD239F82A10001C7F9 /* NSDate+TimeAgo.m in Sources */ = {isa = PBXBuildFile; fileRef = 774746AC239F82A10001C7F9 /* NSDate+TimeAgo.m */; }; 774746B6239F872A0001C7F9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 774746B5239F872A0001C7F9 /* CoreServices.framework */; }; + 774C98CD25126C070073C70A /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774C98CC25126C070073C70A /* Service.swift */; }; 7761133821766A3000D350CD /* ContentLoadingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 7761133621766A3000D350CD /* ContentLoadingCell.m */; }; 7761133921766A3000D350CD /* ContentLoadingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7761133721766A3000D350CD /* ContentLoadingCell.xib */; }; 7761135D21790B0300D350CD /* JuickPush.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7761135521790B0200D350CD /* JuickPush.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -146,6 +147,8 @@ 774746AC239F82A10001C7F9 /* NSDate+TimeAgo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDate+TimeAgo.m"; sourceTree = ""; }; 774746B4239F86EA0001C7F9 /* JuickPush.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = JuickPush.entitlements; sourceTree = ""; }; 774746B5239F872A0001C7F9 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; }; + 774C98C0251263720073C70A /* Juick-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Juick-Bridging-Header.h"; sourceTree = ""; }; + 774C98CC25126C070073C70A /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; 7761133521766A3000D350CD /* ContentLoadingCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentLoadingCell.h; sourceTree = ""; }; 7761133621766A3000D350CD /* ContentLoadingCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentLoadingCell.m; sourceTree = ""; }; 7761133721766A3000D350CD /* ContentLoadingCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContentLoadingCell.xib; sourceTree = ""; }; @@ -296,6 +299,7 @@ 77317BC6181BBE8500D60005 /* Images.xcassets */, 77C6ADDD1F770EB2000AEA8C /* Main.storyboard */, 77C364912243FAEF0017522C /* Colors.xcassets */, + 774C98CC25126C070073C70A /* Service.swift */, ); path = Juick; sourceTree = ""; @@ -303,6 +307,7 @@ 77317BB2181BBE8500D60005 /* Supporting Files */ = { isa = PBXGroup; children = ( + 774C98C0251263720073C70A /* Juick-Bridging-Header.h */, 773F231B1F76DD0B00B5B0DF /* SerpentineBoldItalic.ttf */, 77317BB4181BBE8500D60005 /* InfoPlist.strings */, 77317BB3181BBE8500D60005 /* Juick-Info.plist */, @@ -500,6 +505,7 @@ TargetAttributes = { 77317BA7181BBE8500D60005 = { DevelopmentTeam = KH4MX79ZK7; + LastSwiftMigration = 1200; SystemCapabilities = { com.apple.BackgroundModes = { enabled = 1; @@ -615,6 +621,7 @@ 77E35A82189A5B5A00B2D216 /* LoginViewController.m in Sources */, 773E6397204BCB64008B8F8D /* ConversationCell.m in Sources */, 778560602343D24E00BB37A2 /* NSData+Hex.m in Sources */, + 774C98CD25126C070073C70A /* Service.swift in Sources */, 776C41BD1FD3EF180063B82E /* MessageCell.m in Sources */, 77317BB8181BBE8500D60005 /* main.m in Sources */, 77B8B39C207A5629005CB20C /* MessageInputView.m in Sources */, @@ -780,6 +787,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Juick/Juick.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -789,10 +797,14 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SUPPORTS_MACCATALYST = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Juick/Supporting Files/Juick-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; @@ -803,6 +815,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Juick/Juick.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = 148; @@ -810,10 +823,13 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SUPPORTS_MACCATALYST = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Juick/Supporting Files/Juick-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; @@ -948,6 +964,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Juick/Juick.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -957,10 +974,14 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Juick/Supporting Files/Juick-Prefix.pch"; INFOPLIST_FILE = "Juick/Supporting Files/Juick-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.juick.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SUPPORTS_MACCATALYST = YES; + SWIFT_OBJC_BRIDGING_HEADER = "Juick/Supporting Files/Juick-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; WRAPPER_EXTENSION = app; }; @@ -1004,6 +1025,7 @@ 77F2B6A6251249F300E42F6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1032,6 +1054,7 @@ 77F2B6A7251249F300E42F6F /* Debug (local) */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1060,6 +1083,7 @@ 77F2B6A8251249F300E42F6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; diff --git a/Juick/Service.swift b/Juick/Service.swift new file mode 100644 index 0000000..441451e --- /dev/null +++ b/Juick/Service.swift @@ -0,0 +1,135 @@ +// +// Service.swift +// Juick +// +// Created by Vitaly Takmazov on 16.09.2020. +// Copyright © 2020 com.juick. All rights reserved. +// + +import Foundation + +class Service : NSObject, URLSessionDelegate, URLSessionTaskDelegate { + let backgroundQueue = OperationQueue() + + let baseURL : URL? + + lazy var authorizationHeader : String? = { + if let token = UserDefaults.standard.string(forKey: "token") { + return "Juick \(token)" + } + return nil; + }() + + override init() { + let baseURLString = Bundle.main.object(forInfoDictionaryKey:"base_url") as! String + debugPrint("Initializing with \(baseURLString) base URL") + self.baseURL = URL(string: baseURLString) + super.init() + } + func url(url:URL, params:[String: String]) -> URL { + let parametersUrl = NSURLComponents(url: url, resolvingAgainstBaseURL: true) + var items : [URLQueryItem] = [] + for (key, value) in params { + items.append(URLQueryItem(name: key, value: value)) + } + parametersUrl?.queryItems = items + return (parametersUrl?.url!)! + } + func fetchData(url:URL, postData:Data?, boundary:String?, header:String?, callback: @escaping (Data?, Error?) -> Void) { + var request = URLRequest(url: url) + if (header != nil) { + request.addValue(header!, forHTTPHeaderField:"Authorization") + } + if (boundary != nil) { + request.httpMethod = "POST"; + request.httpBody = postData; + let contentType = "multipart/form-data; boundary=\(boundary!)" + request.setValue(contentType, forHTTPHeaderField:"Content-Type") + } else if (postData != nil) { + request.httpMethod = "PUT"; + request.httpBody = postData; + request.setValue("application/json", forHTTPHeaderField:"Content-Type") + } else { + request.httpMethod = "GET"; + } + + let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in + if (error != nil) { + OperationQueue.main.addOperation { + callback(nil, error) + } + } else { + let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 457 + if (response == nil || statusCode != 200) { + let url = request.url + let err = NSError(domain: "JuickErrorDomain", code: statusCode, userInfo: ["url":url!.absoluteString]) + OperationQueue.main.addOperation { + callback(nil, err) + } + } else { + OperationQueue.main.addOperation { + callback(data, error) + } + } + } + }) + self.backgroundQueue.addOperation { + task.resume() + } + } + private func multipartData(params:[String:String], boundary:String) -> Data { + var body = Data() + for (key, value) in params { + body.append("--\(boundary)\r\n".data(using: .utf8)!) + body.append("Content-Disposition: form-data; name=\(key)\r\n\r\n".data(using: .utf8)!) + body.append("\(value)\r\n".data(using: .utf8)!) + } + body.append("--\(boundary)--".data(using: .utf8)!) + return body; + } + private func get(path:String, params:[String:String], callback:@escaping (NSDictionary?, Error?) -> Void) { + if let url = URL(string: path, relativeTo: self.baseURL) { + let requestUrl = params.isEmpty ? url : self.url(url: url, params: params) + fetchData(url: requestUrl, postData: nil, boundary: nil, header: self.authorizationHeader) { + (responseData, err) in + if (err != nil) { + callback(nil, err) + } else { + if let data = responseData, let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) { + callback(jsonData as? NSDictionary, nil) + } else { + callback(nil, NSError(domain: "JuickErrorDomain", code: 500, userInfo: ["message":"JSON error"])) + } + } + } + } + } + + func post(path: String, params:[String:String], callback:@escaping (NSDictionary?, Error?) -> Void) { + if let url = URL(string: path, relativeTo: self.baseURL) { + let boundary = "Boundary-\(UUID().uuidString)"; + fetchData(url: url, postData:multipartData(params: params, boundary: boundary), boundary: boundary, header: self.authorizationHeader) { + (responseData, err) in + if (err != nil) { + callback(nil, err) + } else { + if let data = responseData, let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) { + callback(jsonData as? NSDictionary, nil) + } else { + callback(nil, NSError(domain: "JuickErrorDomain", code: 500, userInfo: ["message":"JSON error"])) + } + } + } + } + } + + func fetchImage(url:URL, callback:@escaping(Data?)-> Void) { + fetchData(url: url, postData: nil, boundary: nil, header: self.authorizationHeader) { (data, err) in + if (err != nil) { + callback(nil); + } else { + callback(data); + } + } + } +} diff --git a/Juick/Supporting Files/Juick-Bridging-Header.h b/Juick/Supporting Files/Juick-Bridging-Header.h new file mode 100644 index 0000000..e11d920 --- /dev/null +++ b/Juick/Supporting Files/Juick-Bridging-Header.h @@ -0,0 +1,3 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// -- cgit v1.2.3