Pass image as string in deep link parameters - ios

I know we can pass key/value pairs in deep link URL. but can we pass an image as string as a value for a particular key ? I know about inter app communication through shared container. In my case there is a framework created by us which other developer can integrate in their apps. Through framework the user can send an image to our application(if its installed). So shared container will not work here.
Any help will be appreciated.
Is there any limit on the length of the url?
Thanks

Pass base64StrImage from source application
func gotToApp() {
let data = UIImagePNGRepresentation(#imageLiteral(resourceName: "img"))
let base64Str = data!.base64EncodedString()
if UIApplication.shared.canOpenURL(URL(string: "deep://")!) {
UIApplication.shared.open(URL(string: "deep://?img=\(base64Str)")!, options: ["img": #imageLiteral(resourceName: "img")]) { (finish) in
}
}
}
Get Image In Destination Application.
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
print(url.queryParameters!["img"])
return true
}
extension URL {
public var queryParameters: [String: String]? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true), let queryItems = components.queryItems else {
return nil
}
var parameters = [String: String]()
for item in queryItems {
parameters[item.name] = item.value
}
return parameters
}
}

Related

JSON array inside of an array

I am looking to access a string that is located inside of a JSON array that is located inside of another array. I am accessing the JSON API using JSONDecoder. I am receiving errors when trying the various methods that I have used in the past when using JSON arrays.
Here is the code:
var country = [Results]()
struct Rating: Codable {
let results: [Results]
}
struct Results: Codable {
let iso_3166_1: String
let release_dates: [Release_Dates]
}
struct Release_Dates: Codable {
let certification: String
}
func loadRating() {
let id = filmId
let apiKey = ""
let url = URL(string: "https://api.themoviedb.org/3/movie/\(id)/release_dates?api_key=\(apiKey)")
let request = URLRequest(
url: url! as URL,
cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData,
timeoutInterval: 10 )
let session = URLSession (
configuration: URLSessionConfiguration.default,
delegate: nil,
delegateQueue: OperationQueue.main
)
let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
if let data = dataOrNil {
do { let rates = try! JSONDecoder().decode(Rating.self, from: data)
self.country = rates.results
let us = self.country.filter({ $0.iso_3166_1.contains("US") })
print(us)
}
}
})
task.resume()
}
us prints to console
[Film.DetailsView.Results(iso_3166_1: "US", release_dates: [Film.DetailsView.Release_Dates(certification: "PG-13")])]
I am trying to access the certification string.
What would be the correct method used to achieve this?
us is an array of Results.
To get the first certification use this:
print(us.first!.release_dates.first!. certification)
I am force unwrapping for brevity, you should properly do it with optional binding or the guard statement.
Pretty straightforward, the result of filter is an array and certification is in the array release_dates
let us = self.country.filter({ $0.iso_3166_1.contains("US") })
for item in us {
for releaseDate in item.release_dates {
print(releaseDate.certification)
}
}
Please name your struct member names lowerCamelCased by mapping the keys with CodingKeys or with the convertFromSnakeCase strategy.
If there is only one US item, use first
if let us = self.country.first({ $0.iso_3166_1.contains("US") }) {
for releaseDate in us.release_dates {
print(releaseDate.certification)
}
}

Retrieving imageURL from Share Extension

I am trying to get the imageURL the ios share extension uses for the thumbnail generated in the action sheet.
I am retrieving the URL fine but cannot seem to figure out how to get the imageURL.
Here is how I get the normal URL,
if let item = extensionContext?.inputItems.first as? NSExtensionItem {
if let itemProvider = item.attachments?.first as? NSItemProvider {
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeItem as String) {
itemProvider.loadItem(forTypeIdentifier: kUTTypeItem as String, options: nil, completionHandler: { (url, error) -> Void in
if let shareURL = url as? NSURL {
let components = URLComponents(url:shareURL as URL, resolvingAgainstBaseURL: true)
if let host = components?.host { self.shareTitle = host }
self.shareURL = shareURL.absoluteString!
self.POSTShareData(completion: nil)
}
self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil)
})
}
}
}
I have tried to changing the typeIdentifier to kUTTypeImage to no avail. I have my info.plist set to NSExtensionActivationRule to TRUEPREDICATE to see what I can retrieve. I am thinking maybe I have to be more explicit in the .plist ??
I am targeting iOS 9.3
A workaround is that you can store that URL link in common UserDefaults
eg:- let defaults = UserDefaults(suiteName: "group.com.common.Notification")
Then access it in-app or extension

Keychain sharing between two apps

Apple has provided Generic Keychain sample which is written in Swift, I want to go ahead with Objective-C.
I have enabled keychain sharing in both the apps and on canOpenUrl I am able to invoke application B from A, now I want to share username and password from app A to app B. App ID is same for both the applications.
I have looked at various tutorials also don't want to use any third party project.
Could not came to know how to pass the parameter from app A to app B.
Enable Keychain sharing:
Turn on the Keychain Sharing capability.
Select developer team
Specify Keychain group name to something meaningful (for example testKeychainG1)
Open .entitlements file and replace $(AppIdentifierPrefix) with your APP ID (for example AB123CDE45.testKeychainG1)
Accessing Keychain (Retrieve shared items):
let itemKey = "Item Key"
let keychainAccessGroupName = "AB123CDE45.testKeychainG1"
let query:[String:Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: itemKey,
kSecReturnData as String: kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecAttrAccessGroup as String: keychainAccessGroupName
]
var result: AnyObject?
let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
if resultCodeLoad == noErr {
if let result = result as? Data,
let keyValue = NSString(data: result,
encoding: String.Encoding.utf8.rawValue) as? String {
// Found successfully
print(keyValue)
}
} else {
print("Error: \(resultCodeLoad)")
}
step 1:
Set URL Schemes and add the AppA's URL Scheme to the AppB's info.plist like this:<key>LSApplicationQueriesSchemes</key>
<array>
<string>Aapp_Scheme</string>
</array>
step 2:
In app A:
let url = URL.init(string: "B_Scheme://name=Obama&password=Trump");
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: ["":""], completionHandler: nil);
}
step 3:
In app B's AppDelegate.swift add the code:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var name = "", password = "";
let param = url.absoluteString.replacingOccurrences(of: "B_Scheme://", with: "");
let paramArray = param.components(separatedBy: "&");
for temp in paramArray {
if (temp.range(of: "name=") != nil){
name = temp.replacingOccurrences(of: "name=", with: "");
}
else if (temp.range(of: "password=") != nil){
password = temp.replacingOccurrences(of: "password=", with: "");
}
}
if name == "Obama" && password == "Trump" {
print("get param success!");
}
return true;
}

text file content issue

So, I'm making an importing system to bring text files from email into the app to read there contents. I am very new to swift, and app programming at that (mainly do backend), and I am having an issue with the code below. It is most likely very inefficient and there is probably a better way to do this, but currently I have the func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool with some other code to assign variables to the URL to send to the view controller (haven't worked with notifications/rootviewcontrollers yet). however, after running this code, the result, instead of the contents of the file, is ("matrixFile4197009889-26.text", Unicode (UTF-8)). What should I do? Please explain in "baby language."
My view controller code:
let delegate = UIApplication.shared.delegate as! AppDelegate
if delegate.importFileIndicator == true {
let filemgr = FileManager.default
let docsDirURL = try! filemgr.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let inboxURL = docsDirURL.appendingPathComponent("Inbox")
print(inboxURL)
do{
var directoryContents = try FileManager.default.contentsOfDirectory(at: inboxURL, includingPropertiesForKeys: nil, options: [])
var fileSearchBoolCounter = false
var fileSearchCounter = 0
var fileURL: URL
while fileSearchBoolCounter == false {
if (String(describing: directoryContents[fileSearchCounter].lastPathComponent).range(of: String(describing: NSURL(string: delegate.urlString)!.lastPathComponent!)) != nil) {
fileURL = directoryContents[fileSearchCounter]
fileSearchBoolCounter = true
print(fileURL)
let path = inboxURL.appendingPathComponent((NSURL(string: delegate.urlString)?.lastPathComponent!)!)
encryptedMessageField.text = try String(contentsOfFile: String(describing: path), encoding: String.Encoding.utf8)
}else{
print(directoryContents[fileSearchCounter])
fileSearchCounter += 1
print(NSURL(string: delegate.urlString)!.lastPathComponent!)
}
}
delegate.importFileIndicator = false
fileSearchBoolCounter = false
fileSearchCounter = 0
}catch let error as NSError{
print(error)
}
}
My AppDelegate code:
var importFileIndicator = false
var urlString = ""
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
urlString = String(describing: url)
print(urlString)
importFileIndicator = true
return true
}
I think you've already good with some parts, but I'm going to include them too in the whole process.
1. Make your app available to open a TXT file
To let the system know that your app is prepared to receive a TXT file, you need to configure the Info.plist itself, or the simplest way is to configure via TARGETS/"Info tab"/"Document Types section":
At this point your app becomes available to handle the TXT files coming from other external applications. So when you're about to open a TXT file that is attached to a mail, you should see your app available in the list:
2. Prepare app to receive incoming TXT file
In order to handle the supported file type, you need to implement the application:openURL:options: method you've already mentioned in your AppDelegate. Here you receive the file path as url, that you can easily send towards your ViewController for further processing. This url should looks something like this:
(lldb) po url
▿ file:///private/var/mobile/Containers/Data/Application/42D78E58-C7EC-4F3B-9100-B731AF7A4E45/Documents/Inbox/sample.txt
3. Handle the TXT file
Here you can also store the file's content in a String using the appropriate String initializer.
String(contentsOf: url, encoding: String.Encoding.utf8)
and then you can pass that String to your ViewController.
So your application:openURL:options: in your AppDelegate should looks something like this (depends on your actual view controller hierarchy) :
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
do {
let contentString = try String(contentsOf: url, encoding: .utf8)
if let window = self.window, let viewController = window.rootViewController as? ViewController {
viewController.displayText(text: contentString)
// here you pass the actual content as String to your custom ViewController which implements a displayText: function that receives a string
}
}
catch {
// contents could not be loaded
}
return true
}

How to create framework and use it into Project Appdelegate correctly ? in Swift

1- i try to create framework for my project but i didnt get true way to call my methods in another project inside appdelegate;
2- Using framework in another project App Transport Security warning !
my example framework codes under below;
myFramework.swift
import UIKit
var loginUrl = "http://bla/login.php"
let prefs = UserDefaults.standard
public class myFramework: NSObject {
public override init (){
print("Started.")
}
public func doSomething(){
print("works")
}
public func login(secret : String)
{
print("Login Request")
let post_data: NSDictionary = NSMutableDictionary()
post_data.setValue(secret, forKey: "secret")
let url:URL = URL(string: loginUrl)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
var paramString = ""
for (key, value) in post_data
{
paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
}
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["login"] as? NSDictionary
{
if let checksuccess = data_block["success"] as? Bool
{
if checksuccess == true {
if let getUserId = data_block["userId"] {
print("Service Connected")
print(getUserId)
prefs.set(secret, forKey: "secret")
prefs.set(getUserId, forKey: "userId")
}
}else{
if let getMessage = data_block["message"] {
print(getMessage)
}
}
DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
}
public func LoginDone()
{
}
}
Calling another project inside Appdelegate file.
import myFramework
myFramework().login(secret : "234234234234")
but i want to use `myframework without ()
must be;
myFramework.login(secret : "234234234234")
1- How can i do it?
(My all framework codes inside myFramework.swift)
2- When my framework using another project says me app App Transport Security warning , how can i fix it in my framework ? Warning message under below.
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file
Thank you !
In regards to your first question, if you are set on scoping your functions inside of a class, make your functions static or class functions like so:
class myFramework: NSObject {
public class func login(secret : String)
{
...
}
}
Now in code, you can call it like this:
import myFramework
myFramework.login("234234234234")
For your second question, see this post:
App Transport Security has blocked a cleartext HTTP resource
You don't need to put your functions inside a public class. Just make them public.
public func login() {
// insert code here
}
public func secondFunction() {
internalFunction()
}
internal func internalFunction() {
}
In your app:
include MyFramework
login()
secondFunction()
Note that you should name your frameworks like you do classes - capitalized.
You don't need the prefix your function calls with MyFramework.
Your app can see login() and secondFunction(), but cannot see internalFunction() as it's declared internal.
EDIT: Just saw your second question. Click on your framework target in the app explorer. Under General, DeploymentInfo, you'll see a checkbox labelled Allow App Extension API Only - check it. (I make this mistake often too!)

Resources