As you probably already know, sending push notification from Firebase will not display attached image on device. This example assumes you know how to configure push notifications in your app with Firebase. So let’s start.Continue reading
Tag: ios
Internal error when trying to open XIB in XCode 10.3
If you already upgraded XCode to newest 10.3 version, you may notice that opening XIBs created in older versions causes “Internal error” and UI elements are not visible. But there is simple solution for that. Continue reading
RxSwift: How to stream values with dynamic delay
Imagine such case – you have list of messages, each one with timestamp. And you want to show them in the same way they arrived, with same timings. How to achieve this with RxSwift?Continue reading
How to check object’s reference count in Swift 3?
Sooner or later, you will face the problem with object which are not deallocating. How to check if object gets deallocated? Just use deinit method like below:
1 2 3 |
deinit { print("I'm deallocating \(self)") } |
If you won’t see such message when you think you class should be gone, the easiest way is to debug allocations. Because the most common reason for such behavior are strong references, you can print allocation counter this way:
1 |
print("ARC count \(CFGetRetainCount(self))") |
But where to put this? First of all, in all you initialization methods. Just add some those prints after each few lines, and you should see where counter is growing. The same in cleanup methods, or if you dont have any, you can put this somewhere when your view controller is closing, like:
1 2 3 |
self.dismiss(animated: false) { print("VC is closing \(self.myobject)") } |
And how to avoid strong references? The most common mistake, is using self in blocks, this is the place where you should use [unowned self], this way:
1 2 3 |
Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [unowned self] (timer) in self.myobject.doSomething() }) |
How to use JSContext from JavaScriptCore to create two way communication with web page
What is “JavaScriptCore”? Well, it is a name for internal Safari javascript engine. Is it useful? For most of us it is not, but if you thinking about communicating between your Swift app and web page, it can be really useful.
Continue reading
Fatal error: unexpectedly found nil while unwrapping an Optional value
This can be really painful when you just started with Swift. It really happens very often and can be confusing. Where it comes from?Continue reading
Jenkins & XCode: User interaction is not allowed when executing task
Getting “User interaction is not allowed” error in your build task? If yes, solution is very simple. Just login your build user (usually “jenkins”) via the GUI (which means VNC) and open Keychain Access. Select your signing private key, right-click, choose Get Info, change to the Access Control tab and select the “Allow all applications to access this item”.
Now error should be gone.
WatchConnectivity: Payload could not be delivered
So you are trying to send instant message to parent iPhone app and still receiving “Payload could not be delivered”? This happens only if one of those two thing occured:
1. there is no connection to watch (you should check session.reachable in WCSession)
2. your iPhone app is not responding with didReceiveMessage:replyHandler:
Second one is the most probably so how to fix it? There are 2 “didReceiveMessage” methods in WCSessionDelegate protocol, to make it work, you need to implement second one:
1 |
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message replyHandler:(void(^)(NSDictionary<NSString *, id> *replyMessage))replyHandler; |
so just make it look like:
1 2 3 4 |
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message replyHandler:(void(^)(NSDictionary<NSString *, id> *replyMessage))replyHandler { NSLog(@"iPhone: didReceiveMessage %@", message); replyHandler(@{@"command": @"reply"}); } |
You just need to respond with NSDictionary. And it works. Really.
Swift: How to consume SOAP using Alamofire?
Well, SOAP is kinda old. But if for some reason you need to use it, here is how.
1. Create empty “Single View” project, you can call it “Swift-SOAP-with-Alamofire”
2. Create file named “Podfile” inside, your directory should look like this:
3. Open “Podfile” and add following content:
use_frameworks! target 'Swift-SOAP-with-Alamofire' do pod 'Alamofire' pod 'SWXMLHash' pod 'AEXML' pod 'StringExtensionHTML' end post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['CONFIGURATION_BUILD_DIR'] = '$PODS_CONFIGURATION_BUILD_DIR' end end end
4. From terminal execute “pod install” (if you don’t have or don’t know what this command does, feel free to google)
5. After command is done, you should see new “Swift-SOAP-with-Alamofire.xcworkspace” file, close your project and open this one instead.
6. At top of your ViewController.swift file add:
import Alamofire import SWXMLHash import StringExtensionHTML import AEXML
7. Below, add new structure for your country:
struct Country { var name:String = "" }
8. Inside ViewController create new function:
func getCountries(completion: (result: [Country]) -> Void) -> Void { var result = [Country]() let soapRequest = AEXMLDocument() let envelopeAttributes = ["xmlns:SOAP-ENV" : "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:ns1" : "http://www.webserviceX.NET"] let envelope = soapRequest.addChild(name: "SOAP-ENV:Envelope", attributes: envelopeAttributes) let body = envelope.addChild(name: "SOAP-ENV:Body") body.addChild(name: "ns1:GetCountries") let soapLenth = String(soapRequest.xmlString.characters.count) let theURL = NSURL(string: "http://www.webservicex.net/country.asmx") let mutableR = NSMutableURLRequest(URL: theURL!) mutableR.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type") mutableR.addValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type") mutableR.addValue(soapLenth, forHTTPHeaderField: "Content-Length") mutableR.HTTPMethod = "POST" mutableR.HTTPBody = soapRequest.xmlString.dataUsingEncoding(NSUTF8StringEncoding) Alamofire.request(mutableR) .responseString { response in if let xmlString = response.result.value { let xml = SWXMLHash.parse(xmlString) let body = xml["soap:Envelope"]["soap:Body"] if let countriesElement = body["GetCountriesResponse"]["GetCountriesResult"].element { let getCountriesResult = countriesElement.text! let xmlInner = SWXMLHash.parse(getCountriesResult.stringByDecodingHTMLEntities) for element in xmlInner["NewDataSet"]["Table"].all { if let nameElement = element["Name"].element { var countryStruct = Country() countryStruct.name = nameElement.text! result.append(countryStruct) } } } completion(result: result) }else{ print("error fetching XML") } } }
Where actual magic happens.
9. Open your Info.plist as “Code view” and add this to allow loading from HTTP (unless your server has SSL working):
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
10. Just call:
self.getCountries { (result) in print(result) }
and your result will be populated.
You can checkout example project from github at:
https://github.com/blastar/Swift-SOAP-with-Alamofire
Keep in mind that my code is only a way to use, for real apps, you should for example use guard instead of multiple “if let”.
Swift 2.2: What is the difference between .map and .flatMap?
When you dive into .map and .flatMap ocean, you may be confused what is the difference. Consider such example:
let arr = [1,2,3,4,5,6] print(arr.map{ return String($0 * 2)+"x" }) print(arr.flatMap{ return String($0 * 2)+"x" })
Both print’s, will give you same result:
["2x", "4x", "6x", "8x", "10x", "12x"]
So why bother? The main details between them, is that flatMap will skip nil values and unwraps them, so the example:
let mapArr = arr.map{ (string:Int) -> String? in if string < 2 { return nil } return String(string * 2)+"x" } print(mapArr) let mapArr2 = arr.flatMap{ (string:Int) -> String? in if string < 2 { return nil } return String(string * 2)+"x" } print(mapArr2)
will give following results:
[nil, Optional("4x"), Optional("6x"), Optional("8x"), Optional("10x"), Optional("12x")] ["4x", "6x", "8x", "10x", "12x"]
As you can see now .map output are all optionals with nil as first value is skipped. In .flatMap each value is unwrapped to String and there is no nil values at all.