We need a realtime database which we can deploy using docker.
I found a Hasura/PostGreSQL docker container which looks like we can use it for our purpose:
https://docs.hasura.io/1.0/graphql/manual/getting-started/docker-simple.html
One thing I figured out was that the URL in the documentation was wrong. It's http://localhost:8080/v1/graphql and not http://localhost:8080/graphql.
But I can't seem to get any results from my subscription...
I get a BAD_ACCESS crash in the package's SplitNetworkTransport.swift
Am I missing something?
My Apollo client code looks like this:
import Apollo
import ApolloWebSocket
class Apollo {
static let shared = Apollo()
let client: ApolloClient = {
let authPayloads = [ "x-hasura-admin-secret": "secret" ]
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = authPayloads
let wsEndpointURL = URL(string: "ws://localhost:8080/v1alpha1/graphql")!
let endpointURL = URL(string: "http://localhost:8080/v1alpha1/graphql")!
let map: GraphQLMap = authPayloads
let websocket = WebSocketTransport(request: URLRequest(url: wsEndpointURL), connectingPayload: map)
let httpNetworkTransport = HTTPNetworkTransport( url: endpointURL, session: URLSession(configuration: configuration))
let splitTransport = SplitNetworkTransport(httpNetworkTransport: httpNetworkTransport, webSocketNetworkTransport: websocket)
return ApolloClient(networkTransport: splitTransport)
}()
}
And I'm calling it as follows:
import UIKit
import Apollo
class ViewController: UIViewController {
private var subscription: Cancellable?
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.subscription = Apollo.shared.client.subscribe(subscription: MySubscriptionSubscription()) { [weak self] result in
guard let this = self else { return }
switch result {
case .success(let graphQLResult):
if let data = graphQLResult.data {
this.label.text = "data"
print("Simon Says data: \(data)")//TODO: Remove
}
case .failure(let error):
this.label.text = "error"
print("Simon Says error: \(error)")//TODO: Remove
}
}
}
deinit {
self.subscription?.cancel()
}
}
P.s. I'm using Xcode Version 11.2.1 (11B500), swift 5.1, Apollo swift package 0.20.0 and the docker-compose.yaml from the URL in the Hasura docs linked above.
The workaround is NOT using swift package manager and use COCOAPODS instead.
Related
I would like to print the url of my Apollo iOS Client GraphQL queries to the Xcode console when the query is called.
You don't need to write swift code to extract the QueryName.
Use Proxyman, like Charles Proxy. It will display the QueryName on the column by default.
Ref: https://docs.proxyman.io/advanced-features/graphql
Per the Apollo iOS Client docs, a logging interceptor can be added in a custom Interceptor Provider.
I created a custom interceptor provider using the code from DefaultInterceptorProvider, and included the logging interceptor.
import Apollo
class InterceptorProviderWithLogging: InterceptorProvider {
private let client: URLSessionClient
private let store: ApolloStore
private let shouldInvalidateClientOnDeinit: Bool
public init(client: URLSessionClient = URLSessionClient(),
shouldInvalidateClientOnDeinit: Bool = true,
store: ApolloStore) {
self.client = client
self.shouldInvalidateClientOnDeinit = shouldInvalidateClientOnDeinit
self.store = store
}
deinit {
if self.shouldInvalidateClientOnDeinit {
self.client.invalidate()
}
}
open func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
return [
MaxRetryInterceptor(),
CacheReadInterceptor(store: self.store),
RequestLoggingInterceptor(), // added logging interceptor
NetworkFetchInterceptor(client: self.client),
ResponseCodeInterceptor(),
JSONResponseParsingInterceptor(cacheKeyForObject: self.store.cacheKeyForObject),
AutomaticPersistedQueryInterceptor(),
CacheWriteInterceptor(store: self.store),
]
}
open func additionalErrorInterceptor<Operation: GraphQLOperation>(for operation: Operation) -> ApolloErrorInterceptor? {
return nil
}
}
class RequestLoggingInterceptor: ApolloInterceptor {
func interceptAsync<Operation: GraphQLOperation>(
chain: RequestChain,
request: HTTPRequest<Operation>,
response: HTTPResponse<Operation>?,
completion: #escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
if let url = try? request.toURLRequest().url?.absoluteString.removingPercentEncoding {
if let variables = request.operation.variables {
print("\(request.operation.operationName) parameters: \(variables) \(url)")
} else {
print("\(request.operation.operationName) \(url)")
}
}
chain.proceedAsync(request: request, response: response, completion: completion)
}
}
I use the custom interceptor provider in the Request Chain Network Transport.
private(set) lazy var apolloClient: ApolloClient = {
let store = ApolloStore()
let interceptorProvider = InterceptorProviderWithLogging(store: store)
let requestChainTransport = RequestChainNetworkTransport(
interceptorProvider: interceptorProvider,
endpointURL: url,
additionalHeaders: [:],
autoPersistQueries: false,
requestBodyCreator: ApolloRequestBodyCreator(),
useGETForQueries: true,
useGETForPersistedQueryRetry: false
)
return ApolloClient(networkTransport: requestChainTransport, store: store)
}()
Extending GraphQLQuery provides access to the operation name, operation id, and variables, which can be used to build up the url. I also print out the operation name and variables for the query.
extension GraphQLQuery {
func printInfo() {
if let variables = self.variables?.JSONString {
let cleanedVariables = variables.replacingOccurrences(of: "\\", with: "")
print("GraphQL Query: \(self.operationName) \(variables))")
if let operationID = self.operationIdentifier {
let url = "\(GraphQLClient.shared.url)?extensions={\"persistedQuery\":{\"sha256Hash\":\"\(operationID)\",\"version\":1}}&id=\(operationID)&operationName=\(self.operationName)&variables=\(cleanedVariables)"
print("GraphQL URL", url)
}
} else {
print("GraphQL Query: \(self.operationName)")
if let operationID = self.operationIdentifier {
let url = "\(GraphQLClient.shared.url)?extensions={\"persistedQuery\":{\"sha256Hash\":\"\(operationID)\",\"version\":1}}&id=\(operationID)&operationName=\(self.operationName)"
print("GraphQL URL", url)
}
}
}
}
Usage:
let standingsQuery = GetStandingsForSportQuery(sportID: sportIDInt, season: season)
standingsQuery.printInfo()
Example output:
GraphQL Query: getStandingsForSport {"sportID":7,"season":"2020"})
GraphQL URL: https://api.company.com/graphql?extensions={"persistedQuery":{"sha256Hash":"932b414fdadb641f95659d6c61aa29d6d6b0ccf1fa704a0ace751187b90b8cac","version":1}}&id=932b414fdadb641f95659d6c61aa29d6d6b0ccf1fa704a0ace751187b90b8cac&operationName=getStandingsForSport&variables={"sportID":1,"season":"2020"}
The url format in this example may not be typical as we're using persisted queries. I used Charles proxy to see the actual url being sent so I'd know the format.
You could also extend GraphQLOperation instead of GraphQLQuery to get this same info, which would also support mutations and subscriptions.
I'm really new into swift & currently learning API by doing a project that shows list of games from rawg.io referring to the website's doc. I created GameFeed.swift & GameDetail.swift to pull name, release date, and rating from it and working fine in my console.
GameFeed.swift :
struct GameFeed: Codable {
let results:[GameDetail]
}
GameDetail.swift :
struct GameDetail: Codable {
let name:String
let released:String
let rating:Double
}
Now i'm trying to put the results to a simple UIlabel like gameName.text, gameReleased.text & gameRating.text from ViewController.swift so it will be show in Main.Storyboard
i did research on google about how to show it to these UIlabel by using DispatchQueue.main.async but when i'm declaring it, it receiving error :
Value of type 'GameFeed' has no member 'name'
same error messages also happened to released & rating. This is my ViewController.Swift :
class ViewController: UIViewController {
#IBOutlet weak var gameName: UILabel!
#IBOutlet weak var gameReleased: UILabel!
#IBOutlet weak var gameRating: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Defining API Site
let urlString = "https://api.rawg.io/api/games"
let url = URL(string: urlString)
guard url != nil else {
return
}
// Calling API
let session = URLSession.shared
let dataTask = session.dataTask(with: url!){
(data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let gameFeed = try decoder.decode(GameFeed.self, from: data!)
print(gameFeed)
DispatchQueue.main.async {
self.gameName.text = gameFeed.name
self.gameReleased.text = gameFeed.released
self.gameRating.text = gameFeed.rating
}
}
catch {
print("Error Parsing JSON")
}
}
}
dataTask.resume()
}
}
What should i do to make it possible to parse the data to labels?
The GameFeed contains an Array of GameDetails. But you are trying to set a single GameDetail on those labels. You should first pull out a single GameDetail from that array, then assign it in a way you like.
DispatchQueue.main.async {
let gameDetail = gameFeed.results.first // <- This will return the first one
self.gameName.text = gameDetail?.name
self.gameReleased.text = gameDetail?.released
self.gameRating.text = gameDetail?.rating
}
I'm having an issue developing my app. The thing is this, when I enter a certain code, a JSON file starts downloading and got saved in an array. That is fine, the problem is ones it finishes downloading I tried to automatically save the information in CoreData, by running this code below.
var i:Int = 0
while(i < itemsDescargados.count){
let invitacionItems = Invitem(context: self.mangedObjectContext)
invitacionItems.titulo = itemsDescargados[i].titulo
invitacionItems.modo = itemsDescargados[i].modo
invitacionItems.id = itemsDescargados[i].id
invitacionItems.descripcion = itemsDescargados[i].descripcion
invitacionItems.category = itemsDescargados[i].category
invitacionItems.rojo = itemsDescargados[i].rojo
invitacionItems.verde = itemsDescargados[i].verde
invitacionItems.azul = itemsDescargados[i].azul
invitacionItems.imagen = itemsDescargados[i].imagen
do {
try self.mangedObjectContext.save()
print("Downloaded and Saved!")
}
catch{
print("errorCoreData: \(error)")
}
i = i + 1
}
When I put this code into a button and tap it myself it works perfectly, it saves the data and everything is fine, but the problem is that if I run this code into the network manager class class NetworkManagerInv: ObservableObject { after the download it's finished then when the app runs try self.mangedObjectContext.save() automatically prints in the Console log "errorCoreData:nilError" and doesn't save anything.
I did a lot of research but I didn't find anything helpful so I hope you guys can help me. Thanks in advance, sorry if I misspelled something English is not my main language.
Here the NetworkManagerCode
class NetworkManagerInv: ObservableObject {
#Environment(\.managedObjectContext) var mangedObjectContext
#FetchRequest(fetchRequest: Invitem.getAllItems()) var invitem:FetchedResults<Invitem>
var didChange = PassthroughSubject<NetworkManagerInv, Never>()
var jsoninvItem = [JsoninvItem]() {
didSet {
didChange.send(self)
}
}
init(){
let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)
print("Network")
guard let url = URL(string: "http://192.168.1.42:" + codigo + "/items.json") else { return }
session.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
let items2 = try! JSONDecoder().decode([JsoninvItem].self, from: data)
DispatchQueue.main.async {
self.jsoninvItem = items2
itemsDescargados = items2
if(imgDescargadas.count>0){
//Here i need to put the code, this runs ones it finished
}
}
print("Compleated fetching json items")
}.resume()
}
I can't upload an image into Firebase Storage
I found this error in my output log
"An SSL error has occurred and a secure connection to the server
cannot be made."
and last output is
print("check: 4 >> Don't put image")
Then the least of the code doesn't execute therefore the image still not uploading
In the putData method i also use url parameter but also got the same problem
MY Full code is
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
class ViewController: UIViewController {
#IBOutlet weak var imageview: UIImageView!
#IBOutlet weak var emailTextView: UITextField!
#IBOutlet weak var passwordTextView: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func buttonAction(_ sender: Any) {
Auth.auth().createUser(withEmail: emailTextView.text!, password: passwordTextView.text!){ (result, error) in
if let _eror = error {
print(_eror.localizedDescription)
return
}
print("check: 1 >> After creating user\(result!)")
let uid = result?.user.uid
let storage = Storage.storage()
let storageRef = storage.reference(forURL: "gs://porate-chai-4deee.appspot.com").child("profile_image").child(uid!)
print("check: 2 >> \(storageRef)")
if let profileImage = self.imageview!.image, let imageData = profileImage.jpegData(compressionQuality: 0.1) {
print("check: 3 >> \(imageData)")
storageRef.putData(imageData, metadata: nil) { (metadata, error) in
if error != nil {
print("check: 4 >> Don't put image")
return
}
print("put image on firebase storage")
storageRef.downloadURL(completion: {(url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
let downloadURL = url?.absoluteString
print(downloadURL!)
let ref = Database.database().reference()
print("seeref\(ref)")
let userReference = ref.child("tutor")
let newUserReference = userReference.child(uid!)
newUserReference.setValue([
"email": self.emailTextView.text!,
"profileimageurl": downloadURL!
])
})
}
}
}
}
}
My Firebase Storage Rules is
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
I also try this rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
With the help of this answer
"flutter pub get" can't get dependency plugins on Windows
After 27-03-2020 the code still work but after 28-03-2020 have some problem storage.googleapis.com in Bangladesh.
To overcome this issue, use VPN tool and re-run your project.
I used Hotspot Sheild VPN and after that everything was good.
I use this
Hotspot Shield: Fastest
VPNwww.hotspotshield.com
Then the file again uplod
I am trying to use crosswalk to make my web app into iOS app
and I following the steps of Quickstart in this page https://github.com/crosswalk-project/crosswalk-ios
but I keep facing the problem:
in the ViewController.swift
When I import XWalkView, It always appears that It can't find the XWalkView module which makes me stop from my development...
Somebody help me.
Here's the code of ViewController.swift:
import UIKit
import WebKit
import XWalkView
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var start_url = "index.html"
var xwalk_extensions = ["Extension.load"]
if let plistPath = NSBundle.mainBundle().pathForResource("manifest", ofType: "plist") {
if let manifest = NSDictionary(contentsOfFile: plistPath) {
start_url = manifest["start_url"] as? String ?? start_url
xwalk_extensions = manifest["xwalk_extensions"] as? [String] ?? xwalk_extensions
}
}
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
webview.scrollView.bounces = false
view.addSubview(webview)
for name in xwalk_extensions {
if let ext: AnyObject = XWalkExtensionFactory.createExtension(name) {
webview.loadExtension(ext, namespace: name)
}
}
if let root = NSBundle.mainBundle().resourceURL?.URLByAppendingPathComponent("www") {
var error: NSError?
let url = root.URLByAppendingPathComponent(start_url)
if url.checkResourceIsReachableAndReturnError(&error) {
webview.loadFileURL(url, allowingReadAccessToURL: root)
} else {
webview.loadHTMLString(error!.description, baseURL: nil)
}
}
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
perhaps you forget to link the XWalkView.framework in your app target?
Try this step:
Open Xcode, select your app project, in 'General' -> 'Linked Frameworks and Libraries', select '+', choose 'XWalkView.framework' to add it into the linking frameworks. Then shot another build. It's ok if the XWalkView.framework turns red.